diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/ui | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) | |
download | qtwebengine-chromium-d17ea114e5ef69ad5d5d7413280a13e6428098aa.tar.gz |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/ui')
1195 files changed, 29368 insertions, 12053 deletions
diff --git a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h index ba03959d822..0a20628daa2 100644 --- a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h +++ b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.h @@ -58,6 +58,7 @@ class ACCELERATED_WIDGET_MAC_EXPORT AcceleratedWidgetMac void SetNSView(AcceleratedWidgetMacNSView* view); void ResetNSView(); + void ResetNSViewPreservingContents(); // Return true if the last frame swapped has a size in DIP of |dip_size|. bool HasFrameOfSize(const gfx::Size& dip_size) const; diff --git a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm index 8306398431d..97e7b502f15 100644 --- a/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm +++ b/chromium/ui/accelerated_widget_mac/accelerated_widget_mac.mm @@ -94,6 +94,15 @@ void AcceleratedWidgetMac::ResetNSView() { view_ = NULL; } +void AcceleratedWidgetMac::ResetNSViewPreservingContents() { + if (!view_) + return; + + ScopedCAActionDisabler disabler; + [flipped_layer_ removeFromSuperlayer]; + view_ = NULL; +} + bool AcceleratedWidgetMac::HasFrameOfSize( const gfx::Size& dip_size) const { return last_swap_size_dip_ == dip_size; diff --git a/chromium/ui/accessibility/BUILD.gn b/chromium/ui/accessibility/BUILD.gn index d497360d8d8..1b7ba545cdf 100644 --- a/chromium/ui/accessibility/BUILD.gn +++ b/chromium/ui/accessibility/BUILD.gn @@ -20,6 +20,12 @@ mojom("ax_enums_mojo") { sources = [ "ax_enums.mojom", ] + + public_deps = [ + "//mojo/public/mojom/base", + "//ui/gfx/geometry/mojo", + "//ui/gfx/mojo", + ] } component("accessibility") { @@ -172,10 +178,14 @@ test("accessibility_unittests") { "ax_tree_combiner_unittest.cc", "ax_tree_serializer_unittest.cc", "ax_tree_unittest.cc", + "mojom/ax_node_data_mojom_traits_unittest.cc", + "mojom/ax_tree_data_mojom_traits_unittest.cc", + "mojom/ax_tree_update_mojom_traits_unittest.cc", "platform/ax_platform_node_unittest.cc", "platform/ax_platform_node_unittest.h", "platform/ax_platform_node_win_unittest.cc", "platform/ax_unique_id_unittest.cc", + "run_all_unittests.cc", ] deps = [ @@ -183,9 +193,14 @@ test("accessibility_unittests") { ":ax_enums_mojo", ":test_support", "//base", - "//base/test:run_all_unittests", + "//base/test:test_support", + "//mojo/common", + "//mojo/edk", + "//mojo/edk/test:test_support", + "//mojo/public/cpp/test_support:test_utils", "//skia", "//testing/gtest", + "//ui/accessibility/mojom", "//ui/base", "//ui/gfx", "//ui/gfx/geometry", diff --git a/chromium/ui/accessibility/DEPS b/chromium/ui/accessibility/DEPS index de7564af66b..33a31c8668a 100644 --- a/chromium/ui/accessibility/DEPS +++ b/chromium/ui/accessibility/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+mojo/public", "+third_party/iaccessible2", "+third_party/skia", "+ui/aura", @@ -8,3 +9,9 @@ include_rules = [ "+ui/gfx", "+ui/strings/grit/ui_strings.h", ] + +specific_include_rules = { + "run_all_unittests.cc": [ + "+mojo/edk/embedder", + ] +} diff --git a/chromium/ui/accessibility/PRESUBMIT.py b/chromium/ui/accessibility/PRESUBMIT.py index 500b1cc607e..b0d403420f7 100644 --- a/chromium/ui/accessibility/PRESUBMIT.py +++ b/chromium/ui/accessibility/PRESUBMIT.py @@ -115,12 +115,16 @@ def CheckEnumsMatch(input_api, output_api): output_api) CheckMatchingEnum(ax_enums, 'State', automation_enums, 'StateType', errs, output_api) + CheckMatchingEnum(ax_enums, 'Action', automation_enums, 'ActionType', errs, + output_api) CheckMatchingEnum(ax_enums, 'Event', automation_enums, 'EventType', errs, output_api) CheckMatchingEnum(ax_enums, 'NameFrom', automation_enums, 'NameFromType', errs, output_api) CheckMatchingEnum(ax_enums, 'Restriction', automation_enums, 'Restriction', errs, output_api) + CheckMatchingEnum(ax_enums, 'DefaultActionVerb', automation_enums, + 'DefaultActionVerb', errs, output_api) return errs # Given a full path to c++ header, return an array of the first static diff --git a/chromium/ui/accessibility/ax_enum_util.cc b/chromium/ui/accessibility/ax_enum_util.cc index f47b11a714e..8c80b463557 100644 --- a/chromium/ui/accessibility/ax_enum_util.cc +++ b/chromium/ui/accessibility/ax_enum_util.cc @@ -32,6 +32,8 @@ const char* ToString(ax::mojom::Event event) { return "expandedChanged"; case ax::mojom::Event::kFocus: return "focus"; + case ax::mojom::Event::kFocusContext: + return "focusContext"; case ax::mojom::Event::kHide: return "hide"; case ax::mojom::Event::kHitTestResult: @@ -98,6 +100,8 @@ const char* ToString(ax::mojom::Event event) { return "selectionRemove"; case ax::mojom::Event::kShow: return "show"; + case ax::mojom::Event::kStateChanged: + return "stateChanged"; case ax::mojom::Event::kTextChanged: return "textChanged"; case ax::mojom::Event::kTextSelectionChanged: @@ -784,10 +788,6 @@ const char* ToString(ax::mojom::State state) { return "required"; case ax::mojom::State::kRichlyEditable: return "richlyEditable"; - case ax::mojom::State::kSelectable: - return "selectable"; - case ax::mojom::State::kSelected: - return "selected"; case ax::mojom::State::kVertical: return "vertical"; case ax::mojom::State::kVisited: @@ -832,10 +832,6 @@ ax::mojom::State ParseState(const char* state) { return ax::mojom::State::kRequired; if (0 == strcmp(state, "richlyEditable")) return ax::mojom::State::kRichlyEditable; - if (0 == strcmp(state, "selectable")) - return ax::mojom::State::kSelectable; - if (0 == strcmp(state, "selected")) - return ax::mojom::State::kSelected; if (0 == strcmp(state, "vertical")) return ax::mojom::State::kVertical; if (0 == strcmp(state, "visited")) @@ -1435,6 +1431,8 @@ const char* ToString(ax::mojom::BoolAttribute bool_attribute) { return "clickable"; case ax::mojom::BoolAttribute::kClipsChildren: return "clipsChildren"; + case ax::mojom::BoolAttribute::kSelected: + return "selected"; } return ""; @@ -1465,6 +1463,8 @@ ax::mojom::BoolAttribute ParseBoolAttribute(const char* bool_attribute) { return ax::mojom::BoolAttribute::kClickable; if (0 == strcmp(bool_attribute, "clipsChildren")) return ax::mojom::BoolAttribute::kClipsChildren; + if (0 == strcmp(bool_attribute, "selected")) + return ax::mojom::BoolAttribute::kSelected; return ax::mojom::BoolAttribute::kNone; } diff --git a/chromium/ui/accessibility/ax_enums.mojom b/chromium/ui/accessibility/ax_enums.mojom index 7d0d2a3e026..773dbf24d71 100644 --- a/chromium/ui/accessibility/ax_enums.mojom +++ b/chromium/ui/accessibility/ax_enums.mojom @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(nektar): Migrate entire file to Mojoq. // Must also be kept in sync with chrome/common/extensions/api/automation.idl. module ax.mojom; @@ -10,19 +9,22 @@ module ax.mojom; // chrome/common/extensions/api/automation.idl. This is enforced // by a PRESUBMIT check. // - // Explanation of the comments next to these events: + // Explanation of in-lined comments next to some enum values/attributes: // - // Web: this event is only used in web content. Unless a specific platform - // is specified, it fires a native event on multiple platforms. + // Web: this attribute is only used in web content. // - // Native: this event is only used in native UI. + // Native: this attribute is only used in native UI. // - // Implicit: it would be cleaner if we just updated the AX node - // and each platform fired the appropriate events to indicate which + // Implicit: for events, it would be cleaner if we just updated the AX node and + // each platform fired the appropriate events to indicate which // platform-specific attributes changed. // - // If unspecified, the event is used across web and native on multiple + // if Native / [Platform1, ...] is specified, the attribute is only used + // on those platforms. + // + // If unspecified, the atribute is used across web and native on multiple // platforms. + enum Event { kNone, kActiveDescendantChanged, // Web @@ -36,6 +38,7 @@ enum Event { kDocumentSelectionChanged, kExpandedChanged, // Web kFocus, + kFocusContext, // Contextual focus event that must delay the next focus event kHide, // Remove: http://crbug.com/392502 kHitTestResult, kHover, @@ -46,8 +49,8 @@ enum Event { kLiveRegionChanged, // Web kLoadComplete, // Web kLocationChanged, // Web - kMediaStartedPlaying, // Automation - kMediaStoppedPlaying, // Automation + kMediaStartedPlaying, // Native / Automation + kMediaStoppedPlaying, // Native / Automation kMenuEnd, // Native / Win kMenuListItemSelected, // Web kMenuListValueChanged, // Web @@ -69,13 +72,13 @@ enum Event { kSelectionAdd, // Native kSelectionRemove, // Native kShow, // Remove: http://crbug.com/392502 + kStateChanged, // Native / Automation kTextChanged, kTextSelectionChanged, kTreeChanged, // Accessibility tree changed. Don't // explicitly fire an accessibility event, // only implicitly due to the change. kValueChanged, - kLast = kValueChanged, }; // Explanation: @@ -216,7 +219,6 @@ enum Role { kWebArea, kWebView, kWindow, - kLast = kWindow, }; enum State { @@ -239,12 +241,9 @@ enum State { kProtected, kRequired, kRichlyEditable, - kSelectable, - kSelected, // Grows vertically, e.g. menu or combo box. kVertical, kVisited, - kLast = kVisited, }; // An action to be taken on an accessibility node. @@ -286,11 +285,11 @@ enum Action { // Scrolls by approximately one screen in a specific direction. Should be // called on a node that has scrollable boolean set to true. kScrollBackward, - kScrollForward, - kScrollUp, kScrollDown, + kScrollForward, kScrollLeft, kScrollRight, + kScrollUp, // Scroll any scrollable containers to make the target object visible // on the screen. Optionally pass a subfocus rect in @@ -314,14 +313,12 @@ enum Action { kSetValue, kShowContextMenu, - kLast = kShowContextMenu, }; enum ActionFlags { kNone, kRequestImages, kRequestInlineTextBoxes, - kLast = kRequestInlineTextBoxes, }; // A list of valid values for the |AXIntAttribute| |default_action_verb|. @@ -347,7 +344,6 @@ enum DefaultActionVerb { kPress, kSelect, kUncheck, - kLast = kUncheck, }; // A change to the accessibility tree. @@ -357,7 +353,6 @@ enum Mutation { kSubtreeCreated, kNodeChanged, kNodeRemoved, - kLast = kNodeRemoved, }; enum StringAttribute { @@ -366,8 +361,8 @@ enum StringAttribute { // Only used when invalid_state == invalid_state_other. kAriaInvalidValue, kAutoComplete, - kChromeChannel, // Automation only. - kClassName, // views and Android + kChromeChannel, // Native / Automation + kClassName, // Native / Android kContainerLiveRelevant, kContainerLiveStatus, kDescription, @@ -388,7 +383,6 @@ enum StringAttribute { kRoleDescription, kUrl, kValue, - kLast = kValue, }; enum IntAttribute { @@ -493,7 +487,6 @@ enum IntAttribute { // Focus traversal in views and Android. kPreviousFocusId, kNextFocusId, - kLast = kNextFocusId, }; enum FloatAttribute { @@ -507,9 +500,16 @@ enum FloatAttribute { // Text attributes. // Font size is in pixels. kFontSize, - kLast = kFontSize, }; +// These attributes can take three states: +// true, false, or undefined/unset. +// +// Some attributes are only ever true or unset. In these cases, undefined is +// equivalent to false. In other attributes, all three states have meaning. +// +// Finally, note that different tree sources can use all three states for a +// given attribute, while another tree source only uses two. enum BoolAttribute { kNone, // Generic busy state, does not have to be on a live region. @@ -542,7 +542,9 @@ enum BoolAttribute { // Indicates that this node clips its children, i.e. may have // overflow: hidden or clip children by default. kClipsChildren, - kLast = kClipsChildren, + + // Indicates whether this node is selected or unselected. + kSelected, }; enum IntListAttribute { @@ -605,7 +607,6 @@ enum IntListAttribute { // items. Developer can expose those actions as custom actions. Currently // custom actions are used only in Android window. kCustomActionIds, - kLast = kCustomActionIds, }; enum StringListAttribute { @@ -613,7 +614,6 @@ enum StringListAttribute { // Descriptions for custom actions. This must be aligned with // custom_action_ids. kCustomActionDescriptions, - kLast = kCustomActionDescriptions, }; // TODO(dmazzoni, nektar): make this list not grow exponentially as new @@ -655,7 +655,6 @@ enum MarkerType { kSpellingTextMatchActiveSuggestionSuggestion = 53, kGrammarTextMatchActiveSuggestionSuggestion = 54, kSpellingGrammarTextMatchActiveSuggestionSuggestion = 55, - kLast = kSpellingGrammarTextMatchActiveSuggestionSuggestion, }; enum TextDirection { @@ -664,7 +663,6 @@ enum TextDirection { kRtl, kTtb, kBtt, - kLast = kBtt, }; // A Java counterpart will be generated for this enum. @@ -687,7 +685,6 @@ enum TextStyle { kTextStyleBoldUnderlineLineThrough = 13, kTextStyleItalicUnderlineLineThrough = 14, kTextStyleBoldItalicUnderlineLineThrough = 15, - kLast = kTextStyleBoldItalicUnderlineLineThrough, }; enum AriaCurrentState { @@ -700,7 +697,6 @@ enum AriaCurrentState { kUnclippedLocation, kDate, kTime, - kLast = kTime, }; enum InvalidState { @@ -710,7 +706,6 @@ enum InvalidState { kSpelling, kGrammar, kOther, - kLast = kOther, }; // Input restriction associated with an object. @@ -721,7 +716,6 @@ enum Restriction { kNone, kReadOnly, kDisabled, - kLast = kDisabled, }; enum CheckedState { @@ -729,7 +723,6 @@ enum CheckedState { kFalse, kTrue, kMixed, - kLast = kMixed, }; enum SortDirection { @@ -738,7 +731,6 @@ enum SortDirection { kAscending, kDescending, kOther, - kLast = kOther, }; enum NameFrom { @@ -750,7 +742,6 @@ enum NameFrom { kPlaceholder, kRelatedElement, kValue, - kLast = kValue, }; enum DescriptionFrom { @@ -760,7 +751,6 @@ enum DescriptionFrom { kContents, kPlaceholder, kRelatedElement, - kLast = kRelatedElement, }; enum EventFrom { @@ -768,7 +758,6 @@ enum EventFrom { kUser, kPage, kAction, - kLast = kAction, }; // Touch gestures on Chrome OS. @@ -792,14 +781,12 @@ enum Gesture { kSwipeRight4, kSwipeDown4, kTap2, - kLast = kTap2, }; enum TextAffinity { kNone, kDownstream, kUpstream, - kLast = kUpstream, }; // Compares two nodes in an accessibility tree in pre-order traversal. @@ -816,5 +803,4 @@ enum TreeOrder { // First node is after the second one. kAfter, - kLast = kAfter, }; diff --git a/chromium/ui/accessibility/ax_event_generator.cc b/chromium/ui/accessibility/ax_event_generator.cc index 52747b1632b..2c132265225 100644 --- a/chromium/ui/accessibility/ax_event_generator.cc +++ b/chromium/ui/accessibility/ax_event_generator.cc @@ -141,16 +141,6 @@ void AXEventGenerator::OnStateChanged(AXTree* tree, AddEvent(container, Event::ROW_COUNT_CHANGED); } break; - case ax::mojom::State::kSelected: { - AddEvent(node, Event::SELECTED_CHANGED); - ui::AXNode* container = node; - while (container && - !ui::IsContainerWithSelectableChildrenRole(container->data().role)) - container = container->parent(); - if (container) - AddEvent(container, Event::SELECTED_CHILDREN_CHANGED); - break; - } case ax::mojom::State::kIgnored: { ui::AXNode* unignored_parent = node->GetUnignoredParent(); if (unignored_parent) @@ -251,6 +241,17 @@ void AXEventGenerator::OnBoolAttributeChanged(AXTree* tree, bool new_value) { DCHECK_EQ(tree_, tree); + if (attr == ax::mojom::BoolAttribute::kSelected) { + AddEvent(node, Event::SELECTED_CHANGED); + ui::AXNode* container = node; + while (container && + !ui::IsContainerWithSelectableChildrenRole(container->data().role)) + container = container->parent(); + if (container) + AddEvent(container, Event::SELECTED_CHILDREN_CHANGED); + return; + } + AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED); } @@ -411,7 +412,10 @@ void AXEventGenerator::FireRelationSourceEvents(AXTree* tree, return; source_nodes.insert(source_node); - AddEvent(source_node, Event::RELATED_NODE_CHANGED); + + // GCC < 6.4 requires this pointer when calling a member + // function in anonymous function + this->AddEvent(source_node, Event::RELATED_NODE_CHANGED); }); }; diff --git a/chromium/ui/accessibility/ax_event_generator_unittest.cc b/chromium/ui/accessibility/ax_event_generator_unittest.cc index 2d3f192568f..2cbbb9e02ab 100644 --- a/chromium/ui/accessibility/ax_event_generator_unittest.cc +++ b/chromium/ui/accessibility/ax_event_generator_unittest.cc @@ -228,20 +228,23 @@ TEST(AXEventGeneratorTest, SelectedAndSelectedChildren) { initial_state.nodes[2].role = ax::mojom::Role::kMenuItem; initial_state.nodes[3].id = 4; initial_state.nodes[3].role = ax::mojom::Role::kListBoxOption; - initial_state.nodes[3].AddState(ax::mojom::State::kSelected); + initial_state.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, + true); AXTree tree(initial_state); AXEventGenerator event_generator(&tree); AXTreeUpdate update = initial_state; - update.nodes[2].AddState(ax::mojom::State::kSelected); - update.nodes[3].state = 0; + update.nodes[2].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes.pop_back(); + update.nodes.emplace_back(); + update.nodes[3].id = 4; + update.nodes[3].role = ax::mojom::Role::kListBoxOption; + update.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, false); EXPECT_TRUE(tree.Unserialize(update)); EXPECT_EQ( "SELECTED_CHANGED on 3, " "SELECTED_CHANGED on 4, " - "SELECTED_CHILDREN_CHANGED on 2, " - "STATE_CHANGED on 3, " - "STATE_CHANGED on 4", + "SELECTED_CHILDREN_CHANGED on 2", DumpEvents(&event_generator)); } diff --git a/chromium/ui/accessibility/ax_node_data.cc b/chromium/ui/accessibility/ax_node_data.cc index 5f8967f3698..ac0c81ea87a 100644 --- a/chromium/ui/accessibility/ax_node_data.cc +++ b/chromium/ui/accessibility/ax_node_data.cc @@ -32,7 +32,7 @@ uint32_t ModifyFlag(uint32_t bitfield, uint32_t flag, bool set) { std::string StateBitfieldToString(uint32_t state) { std::string str; for (uint32_t i = static_cast<uint32_t>(ax::mojom::State::kNone) + 1; - i <= static_cast<uint32_t>(ax::mojom::State::kLast); ++i) { + i <= static_cast<uint32_t>(ax::mojom::State::kMaxValue); ++i) { if (IsFlagSet(state, i)) str += " " + base::ToUpperASCII(ui::ToString(static_cast<ax::mojom::State>(i))); @@ -43,7 +43,7 @@ std::string StateBitfieldToString(uint32_t state) { std::string ActionsBitfieldToString(uint32_t actions) { std::string str; for (uint32_t i = static_cast<uint32_t>(ax::mojom::Action::kNone) + 1; - i <= static_cast<uint32_t>(ax::mojom::Action::kLast); ++i) { + i <= static_cast<uint32_t>(ax::mojom::Action::kMaxValue); ++i) { if (IsFlagSet(actions, i)) { str += ui::ToString(static_cast<ax::mojom::Action>(i)); actions = ModifyFlag(actions, i, false); @@ -1001,6 +1001,9 @@ std::string AXNodeData::ToString() const { case ax::mojom::BoolAttribute::kClipsChildren: result += " clips_children=" + value; break; + case ax::mojom::BoolAttribute::kSelected: + result += " selected=" + value; + break; case ax::mojom::BoolAttribute::kNone: break; } diff --git a/chromium/ui/accessibility/ax_position.h b/chromium/ui/accessibility/ax_position.h index e3ed7ee6a46..67726fa1b31 100644 --- a/chromium/ui/accessibility/ax_position.h +++ b/chromium/ui/accessibility/ax_position.h @@ -8,6 +8,7 @@ #include <stdint.h> #include <memory> +#include <ostream> #include <string> #include <utility> #include <vector> @@ -1319,6 +1320,13 @@ bool operator>=(const AXPosition<AXPositionType, AXNodeType>& first, return first == second || first > second; } +template <class AXPositionType, class AXNodeType> +std::ostream& operator<<( + std::ostream& stream, + const AXPosition<AXPositionType, AXNodeType>& position) { + return stream << position.ToString(); +} + } // namespace ui #endif // UI_ACCESSIBILITY_AX_POSITION_H_ diff --git a/chromium/ui/accessibility/ax_tree.cc b/chromium/ui/accessibility/ax_tree.cc index b6fe217c0b0..a7de4473d1a 100644 --- a/chromium/ui/accessibility/ax_tree.cc +++ b/chromium/ui/accessibility/ax_tree.cc @@ -269,8 +269,8 @@ gfx::RectF AXTree::RelativeToTreeBounds(const AXNode* node, // Here we are extending the definition of offscreen to include elements // that are clipped by their parents in addition to those clipped by // the rootWebArea. - // No need to update |offscreen| if |clipped| is not empty, because it - // should be false by default. + // No need to update |offscreen| if |intersection| is not empty, because + // it should be false by default. if (offscreen != nullptr) *offscreen |= true; } @@ -544,7 +544,7 @@ void AXTree::CallNodeChangeCallbacks(AXNode* node, const AXNodeData& new_data) { if (old_data.state != new_data.state) { for (int32_t i = static_cast<int32_t>(ax::mojom::State::kNone) + 1; - i <= static_cast<int32_t>(ax::mojom::State::kLast); ++i) { + i <= static_cast<int32_t>(ax::mojom::State::kMaxValue); ++i) { ax::mojom::State state = static_cast<ax::mojom::State>(i); if (old_data.HasState(state) != new_data.HasState(state)) delegate_->OnStateChanged(this, node, state, new_data.HasState(state)); diff --git a/chromium/ui/accessibility/ax_tree_serializer.h b/chromium/ui/accessibility/ax_tree_serializer.h index 098ad4b66a7..b97ad768847 100644 --- a/chromium/ui/accessibility/ax_tree_serializer.h +++ b/chromium/ui/accessibility/ax_tree_serializer.h @@ -200,12 +200,14 @@ AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::~AXTreeSerializer() { template <typename AXSourceNode, typename AXNodeData, typename AXTreeData> void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::Reset() { client_tree_data_ = AXTreeData(); - if (!client_root_) - return; - DeleteClientSubtree(client_root_); - client_id_map_.erase(client_root_->id); - delete client_root_; + // Normally we use DeleteClientSubtree to remove nodes from the tree, + // but Reset() needs to work even if the tree is in a broken state. + // Instead, iterate over |client_id_map_| to ensure we clear all nodes and + // start from scratch. + for (auto&& item : client_id_map_) + delete item.second; + client_id_map_.clear(); client_root_ = nullptr; } @@ -321,10 +323,11 @@ template <typename AXSourceNode, typename AXNodeData, typename AXTreeData> bool AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::SerializeChanges( AXSourceNode node, AXTreeUpdateBase<AXNodeData, AXTreeData>* out_update) { - // Send the tree data if it's changed since the last update. + // Send the tree data if it's changed since the last update, or if + // out_update->has_tree_data is already set to true. AXTreeData new_tree_data; if (tree_->GetTreeData(&new_tree_data) && - new_tree_data != client_tree_data_) { + (out_update->has_tree_data || new_tree_data != client_tree_data_)) { out_update->has_tree_data = true; out_update->tree_data = new_tree_data; client_tree_data_ = new_tree_data; diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc index 81d8fb536eb..0bca9825265 100644 --- a/chromium/ui/accessibility/ax_tree_unittest.cc +++ b/chromium/ui/accessibility/ax_tree_unittest.cc @@ -9,7 +9,6 @@ #include <memory> -#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" diff --git a/chromium/ui/accessibility/mojom/BUILD.gn b/chromium/ui/accessibility/mojom/BUILD.gn new file mode 100644 index 00000000000..289e5ca4d01 --- /dev/null +++ b/chromium/ui/accessibility/mojom/BUILD.gn @@ -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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("mojom") { + sources = [ + "ax_node_data.mojom", + "ax_tree_data.mojom", + "ax_tree_update.mojom", + ] + + public_deps = [ + "//mojo/public/mojom/base", + "//ui/accessibility:ax_enums_mojo", + "//ui/gfx/geometry/mojo", + "//ui/gfx/mojo", + ] +} diff --git a/chromium/ui/accessibility/mojom/OWNERS b/chromium/ui/accessibility/mojom/OWNERS new file mode 100644 index 00000000000..ae29a36aac8 --- /dev/null +++ b/chromium/ui/accessibility/mojom/OWNERS @@ -0,0 +1,6 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS +per-file *_mojom_traits*.*=set noparent +per-file *_mojom_traits*.*=file://ipc/SECURITY_OWNERS +per-file *.typemap=set noparent +per-file *.typemap=file://ipc/SECURITY_OWNERS diff --git a/chromium/ui/accessibility/mojom/ax_node_data.mojom b/chromium/ui/accessibility/mojom/ax_node_data.mojom new file mode 100644 index 00000000000..bbaef03c247 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_node_data.mojom @@ -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. + +module ax.mojom; + +import "ui/accessibility/ax_enums.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; +import "ui/gfx/mojo/transform.mojom"; + +// See ui::AXNodeData for comments / explanations of these fields. +struct AXNodeData { + int32 id; + ax.mojom.Role role; + uint32 state; + uint32 actions; + map<ax.mojom.StringAttribute, string> string_attributes; + map<ax.mojom.IntAttribute, int32> int_attributes; + map<ax.mojom.FloatAttribute, float> float_attributes; + map<ax.mojom.BoolAttribute, bool> bool_attributes; + map<ax.mojom.IntListAttribute, array<int32>> + intlist_attributes; + map<ax.mojom.StringListAttribute, array<string>> + stringlist_attributes; + map<string, string> html_attributes; + array<int32> child_ids; + int32 offset_container_id; + gfx.mojom.RectF location; + gfx.mojom.Transform transform; +}; diff --git a/chromium/ui/accessibility/mojom/ax_node_data.typemap b/chromium/ui/accessibility/mojom/ax_node_data.typemap new file mode 100644 index 00000000000..4a36ac29159 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_node_data.typemap @@ -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. + +mojom = "//ui/accessibility/mojom/ax_node_data.mojom" +public_headers = [ "//ui/accessibility/ax_node_data.h" ] +traits_headers = [ "//ui/accessibility/mojom/ax_node_data_mojom_traits.h" ] +sources = [ + "ax_node_data_mojom_traits.cc", + "ax_node_data_mojom_traits.h", +] +public_deps = [ + "//ui/accessibility", + "//ui/gfx", + "//ui/gfx/geometry/mojo", + "//ui/gfx/geometry/mojo:struct_traits", + "//ui/gfx/mojo", +] +type_mappings = [ "ax.mojom.AXNodeData=ui::AXNodeData" ] diff --git a/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits.cc b/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits.cc new file mode 100644 index 00000000000..22873c6b4be --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits.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/accessibility/mojom/ax_node_data_mojom_traits.h" + +namespace mojo { + +// static +std::unordered_map<ax::mojom::StringAttribute, std::string> +StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::string_attributes( + const ui::AXNodeData& p) { + std::unordered_map<ax::mojom::StringAttribute, std::string> result; + for (const auto& iter : p.string_attributes) + result[iter.first] = iter.second; + return result; +} + +// static +std::unordered_map<ax::mojom::IntAttribute, int32_t> +StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::int_attributes( + const ui::AXNodeData& p) { + std::unordered_map<ax::mojom::IntAttribute, int32_t> result; + for (const auto& iter : p.int_attributes) + result[iter.first] = iter.second; + return result; +} + +// static +std::unordered_map<ax::mojom::FloatAttribute, float> +StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::float_attributes( + const ui::AXNodeData& p) { + std::unordered_map<ax::mojom::FloatAttribute, float> result; + for (const auto& iter : p.float_attributes) + result[iter.first] = iter.second; + return result; +} + +// static +std::unordered_map<ax::mojom::BoolAttribute, bool> +StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::bool_attributes( + const ui::AXNodeData& p) { + std::unordered_map<ax::mojom::BoolAttribute, bool> result; + for (const auto& iter : p.bool_attributes) + result[iter.first] = iter.second; + return result; +} + +// static +std::unordered_map<ax::mojom::IntListAttribute, std::vector<int32_t>> +StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::intlist_attributes( + const ui::AXNodeData& p) { + std::unordered_map<ax::mojom::IntListAttribute, std::vector<int32_t>> result; + for (const auto& iter : p.intlist_attributes) + result[iter.first] = iter.second; + return result; +} + +// static +std::unordered_map<ax::mojom::StringListAttribute, std::vector<std::string>> +StructTraits<ax::mojom::AXNodeDataDataView, + ui::AXNodeData>::stringlist_attributes(const ui::AXNodeData& p) { + std::unordered_map<ax::mojom::StringListAttribute, std::vector<std::string>> + result; + for (const auto& iter : p.stringlist_attributes) + result[iter.first] = iter.second; + return result; +} + +// static +std::unordered_map<std::string, std::string> +StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::html_attributes( + const ui::AXNodeData& p) { + std::unordered_map<std::string, std::string> result; + for (const auto& iter : p.html_attributes) + result[iter.first] = iter.second; + return result; +} + +// static +gfx::Transform +StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::transform( + const ui::AXNodeData& p) { + if (p.transform) + return *p.transform; + else + return gfx::Transform(); +} + +// static +bool StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData>::Read( + ax::mojom::AXNodeDataDataView data, + ui::AXNodeData* out) { + out->id = data.id(); + out->role = data.role(); + out->state = data.state(); + out->actions = data.actions(); + + std::unordered_map<ax::mojom::StringAttribute, std::string> string_attributes; + if (!data.ReadStringAttributes(&string_attributes)) + return false; + for (const auto& iter : string_attributes) + out->AddStringAttribute(iter.first, iter.second); + + std::unordered_map<ax::mojom::IntAttribute, int32_t> int_attributes; + if (!data.ReadIntAttributes(&int_attributes)) + return false; + for (const auto& iter : int_attributes) + out->AddIntAttribute(iter.first, iter.second); + + std::unordered_map<ax::mojom::FloatAttribute, float> float_attributes; + if (!data.ReadFloatAttributes(&float_attributes)) + return false; + for (const auto& iter : float_attributes) + out->AddFloatAttribute(iter.first, iter.second); + + std::unordered_map<ax::mojom::BoolAttribute, bool> bool_attributes; + if (!data.ReadBoolAttributes(&bool_attributes)) + return false; + for (const auto& iter : bool_attributes) + out->AddBoolAttribute(iter.first, iter.second); + + std::unordered_map<ax::mojom::IntListAttribute, std::vector<int32_t>> + intlist_attributes; + if (!data.ReadIntlistAttributes(&intlist_attributes)) + return false; + for (const auto& iter : intlist_attributes) + out->AddIntListAttribute(iter.first, iter.second); + + std::unordered_map<ax::mojom::StringListAttribute, std::vector<std::string>> + stringlist_attributes; + if (!data.ReadStringlistAttributes(&stringlist_attributes)) + return false; + for (const auto& iter : stringlist_attributes) + out->AddStringListAttribute(iter.first, iter.second); + + std::unordered_map<std::string, std::string> html_attributes; + if (!data.ReadHtmlAttributes(&html_attributes)) + return false; + for (const auto& iter : html_attributes) + out->html_attributes.push_back(std::make_pair(iter.first, iter.second)); + + if (!data.ReadChildIds(&out->child_ids)) + return false; + + out->offset_container_id = data.offset_container_id(); + + if (!data.ReadLocation(&out->location)) + return false; + + gfx::Transform transform; + if (!data.ReadTransform(&transform)) + return false; + if (!transform.IsIdentity()) + out->transform.reset(new gfx::Transform(transform)); + + return true; +} + +} // namespace mojo diff --git a/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits.h b/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits.h new file mode 100644 index 00000000000..22c44e975de --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits.h @@ -0,0 +1,50 @@ +// 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_NODE_DATA_MOJOM_TRAITS_H_ +#define UI_ACCESSIBILITY_MOJOM_AX_NODE_DATA_MOJOM_TRAITS_H_ + +#include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/mojom/ax_node_data.mojom-shared.h" +#include "ui/gfx/geometry/mojo/geometry_struct_traits.h" +#include "ui/gfx/mojo/transform.mojom.h" +#include "ui/gfx/mojo/transform_struct_traits.h" + +namespace mojo { + +template <> +struct StructTraits<ax::mojom::AXNodeDataDataView, ui::AXNodeData> { + static int32_t id(const ui::AXNodeData& p) { return p.id; } + static ax::mojom::Role role(const ui::AXNodeData& p) { return p.role; } + static uint32_t state(const ui::AXNodeData& p) { return p.state; } + static uint32_t actions(const ui::AXNodeData& p) { return p.actions; } + static std::unordered_map<ax::mojom::StringAttribute, std::string> + string_attributes(const ui::AXNodeData& p); + static std::unordered_map<ax::mojom::IntAttribute, int32_t> int_attributes( + const ui::AXNodeData& p); + static std::unordered_map<ax::mojom::FloatAttribute, float> float_attributes( + const ui::AXNodeData& p); + static std::unordered_map<ax::mojom::BoolAttribute, bool> bool_attributes( + const ui::AXNodeData& p); + static std::unordered_map<ax::mojom::IntListAttribute, std::vector<int32_t>> + intlist_attributes(const ui::AXNodeData& p); + static std::unordered_map<ax::mojom::StringListAttribute, + std::vector<std::string>> + stringlist_attributes(const ui::AXNodeData& p); + static std::unordered_map<std::string, std::string> html_attributes( + const ui::AXNodeData& p); + static std::vector<int32_t> child_ids(const ui::AXNodeData& p) { + return p.child_ids; + } + static int32_t offset_container_id(const ui::AXNodeData& p) { + return p.offset_container_id; + } + static gfx::RectF location(const ui::AXNodeData& p) { return p.location; } + static gfx::Transform transform(const ui::AXNodeData& p); + static bool Read(ax::mojom::AXNodeDataDataView data, ui::AXNodeData* out); +}; + +} // namespace mojo + +#endif // UI_ACCESSIBILITY_MOJOM_AX_NODE_DATA_MOJOM_TRAITS_H_ diff --git a/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits_unittest.cc b/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits_unittest.cc new file mode 100644 index 00000000000..2e25fdda961 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_node_data_mojom_traits_unittest.cc @@ -0,0 +1,134 @@ +// 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_node_data_mojom_traits.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/mojom/ax_node_data.mojom.h" + +using mojo::test::SerializeAndDeserialize; + +TEST(AXNodeDataMojomTraitsTest, ID) { + ui::AXNodeData input, output; + input.id = 42; + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(42, output.id); +} + +TEST(AXNodeDataMojomTraitsTest, Role) { + ui::AXNodeData input, output; + input.role = ax::mojom::Role::kButton; + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(ax::mojom::Role::kButton, output.role); +} + +TEST(AXNodeDataMojomTraitsTest, State) { + ui::AXNodeData input, output; + input.state = 0; + input.AddState(ax::mojom::State::kCollapsed); + input.AddState(ax::mojom::State::kHorizontal); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_TRUE(output.HasState(ax::mojom::State::kCollapsed)); + EXPECT_TRUE(output.HasState(ax::mojom::State::kHorizontal)); + EXPECT_FALSE(output.HasState(ax::mojom::State::kFocusable)); + EXPECT_FALSE(output.HasState(ax::mojom::State::kMultiline)); +} + +TEST(AXNodeDataMojomTraitsTest, Actions) { + ui::AXNodeData input, output; + input.actions = 0; + input.AddAction(ax::mojom::Action::kDoDefault); + input.AddAction(ax::mojom::Action::kDecrement); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_TRUE(output.HasAction(ax::mojom::Action::kDoDefault)); + EXPECT_TRUE(output.HasAction(ax::mojom::Action::kDecrement)); + EXPECT_FALSE(output.HasAction(ax::mojom::Action::kFocus)); + EXPECT_FALSE(output.HasAction(ax::mojom::Action::kBlur)); +} + +TEST(AXNodeDataMojomTraitsTest, StringAttributes) { + ui::AXNodeData input, output; + input.AddStringAttribute(ax::mojom::StringAttribute::kName, "Mojo"); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ("Mojo", + output.GetStringAttribute(ax::mojom::StringAttribute::kName)); +} + +TEST(AXNodeDataMojomTraitsTest, IntAttributes) { + ui::AXNodeData input, output; + input.AddIntAttribute(ax::mojom::IntAttribute::kScrollX, 42); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(42, output.GetIntAttribute(ax::mojom::IntAttribute::kScrollX)); +} + +TEST(AXNodeDataMojomTraitsTest, FloatAttributes) { + ui::AXNodeData input, output; + input.AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize, 42); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(42, output.GetFloatAttribute(ax::mojom::FloatAttribute::kFontSize)); +} + +TEST(AXNodeDataMojomTraitsTest, BoolAttributes) { + ui::AXNodeData input, output; + input.AddBoolAttribute(ax::mojom::BoolAttribute::kBusy, true); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_TRUE(output.GetBoolAttribute(ax::mojom::BoolAttribute::kBusy)); +} + +TEST(AXNodeDataMojomTraitsTest, IntListAttributes) { + ui::AXNodeData input, output; + input.AddIntListAttribute(ax::mojom::IntListAttribute::kControlsIds, {1, 2}); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ( + std::vector<int32_t>({1, 2}), + output.GetIntListAttribute(ax::mojom::IntListAttribute::kControlsIds)); +} + +TEST(AXNodeDataMojomTraitsTest, StringListAttributes) { + ui::AXNodeData input, output; + input.AddStringListAttribute( + ax::mojom::StringListAttribute::kCustomActionDescriptions, + {"foo", "bar"}); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(std::vector<std::string>({"foo", "bar"}), + output.GetStringListAttribute( + ax::mojom::StringListAttribute::kCustomActionDescriptions)); +} + +TEST(AXNodeDataMojomTraitsTest, ChildIds) { + ui::AXNodeData input, output; + input.child_ids = {3, 4}; + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(std::vector<int32_t>({3, 4}), output.child_ids); +} + +TEST(AXNodeDataMojomTraitsTest, OffsetContainerID) { + ui::AXNodeData input, output; + input.offset_container_id = 10; + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(10, output.offset_container_id); +} + +TEST(AXNodeDataMojomTraitsTest, Location) { + ui::AXNodeData input, output; + input.location = gfx::RectF(1, 2, 3, 4); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_EQ(1, output.location.x()); + EXPECT_EQ(2, output.location.y()); + EXPECT_EQ(3, output.location.width()); + EXPECT_EQ(4, output.location.height()); +} + +TEST(AXNodeDataMojomTraitsTest, Transform) { + ui::AXNodeData input, output; + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_FALSE(output.transform); + + input.transform = std::make_unique<gfx::Transform>(); + input.transform->Scale(2.0, 2.0); + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXNodeData>(&input, &output)); + EXPECT_TRUE(output.transform); + EXPECT_FALSE(output.transform->IsIdentity()); +} diff --git a/chromium/ui/accessibility/mojom/ax_tree_data.mojom b/chromium/ui/accessibility/mojom/ax_tree_data.mojom new file mode 100644 index 00000000000..668134576bd --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_data.mojom @@ -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. + +module ax.mojom; + +import "ui/accessibility/ax_enums.mojom"; + +// See ui::AXTreeData for comments / explanations of these fields. +struct AXTreeData { + int32 tree_id; + int32 parent_tree_id; + int32 focused_tree_id; + string doctype; + bool loaded; + float loading_progress; + string mimetype; + string title; + string url; + int32 focus_id; + int32 sel_anchor_object_id; + int32 sel_anchor_offset; + ax.mojom.TextAffinity sel_anchor_affinity; + int32 sel_focus_object_id; + int32 sel_focus_offset; + ax.mojom.TextAffinity sel_focus_affinity; +}; diff --git a/chromium/ui/accessibility/mojom/ax_tree_data.typemap b/chromium/ui/accessibility/mojom/ax_tree_data.typemap new file mode 100644 index 00000000000..36ca634361e --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_data.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_data.mojom" +public_headers = [ "//ui/accessibility/ax_tree_data.h" ] +traits_headers = [ "//ui/accessibility/mojom/ax_tree_data_mojom_traits.h" ] +sources = [ + "ax_tree_data_mojom_traits.cc", + "ax_tree_data_mojom_traits.h", +] +public_deps = [ + "//ui/accessibility", +] +type_mappings = [ "ax.mojom.AXTreeData=ui::AXTreeData" ] diff --git a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc new file mode 100644 index 00000000000..4c465686e8c --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.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/accessibility/mojom/ax_tree_data_mojom_traits.h" + +namespace mojo { + +// static +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.ReadDoctype(&out->doctype)) + return false; + out->loaded = data.loaded(); + out->loading_progress = data.loading_progress(); + if (!data.ReadMimetype(&out->mimetype)) + return false; + if (!data.ReadTitle(&out->title)) + return false; + if (!data.ReadUrl(&out->url)) + return false; + out->focus_id = data.focus_id(); + out->sel_anchor_object_id = data.sel_anchor_object_id(); + out->sel_anchor_offset = data.sel_anchor_offset(); + out->sel_anchor_affinity = data.sel_anchor_affinity(); + out->sel_focus_object_id = data.sel_focus_object_id(); + out->sel_focus_offset = data.sel_focus_offset(); + out->sel_focus_affinity = data.sel_focus_affinity(); + return true; +} + +} // namespace mojo diff --git a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.h b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.h new file mode 100644 index 00000000000..0b3a2b138b9 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.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_ACCESSIBILITY_MOJOM_AX_TREE_DATA_MOJOM_TRAITS_H_ +#define UI_ACCESSIBILITY_MOJOM_AX_TREE_DATA_MOJOM_TRAITS_H_ + +#include "ui/accessibility/ax_tree_data.h" +#include "ui/accessibility/mojom/ax_tree_data.mojom-shared.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) { + return p.parent_tree_id; + } + static int32_t focused_tree_id(const ui::AXTreeData& p) { + return p.focused_tree_id; + } + static 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 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; + } + static int32_t sel_anchor_offset(const ui::AXTreeData& p) { + return p.sel_anchor_offset; + } + static ax::mojom::TextAffinity sel_anchor_affinity(const ui::AXTreeData& p) { + return p.sel_anchor_affinity; + } + static int32_t sel_focus_object_id(const ui::AXTreeData& p) { + return p.sel_focus_object_id; + } + static int32_t sel_focus_offset(const ui::AXTreeData& p) { + return p.sel_focus_offset; + } + static ax::mojom::TextAffinity sel_focus_affinity(const ui::AXTreeData& p) { + return p.sel_focus_affinity; + } + + static bool Read(ax::mojom::AXTreeDataDataView data, ui::AXTreeData* out); +}; + +} // namespace mojo + +#endif // UI_ACCESSIBILITY_MOJOM_AX_TREE_DATA_MOJOM_TRAITS_H_ 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 new file mode 100644 index 00000000000..02496fbb001 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc @@ -0,0 +1,50 @@ +// 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_data_mojom_traits.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/ax_tree_data.h" +#include "ui/accessibility/mojom/ax_tree_data.mojom.h" + +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.doctype = "4"; + input.loaded = true; + input.loading_progress = 5; + input.mimetype = "6"; + input.title = "7"; + input.url = "8"; + input.focus_id = 9; + input.sel_anchor_object_id = 10; + input.sel_anchor_offset = 11; + input.sel_anchor_affinity = ax::mojom::TextAffinity::kUpstream; + input.sel_focus_object_id = 12; + input.sel_focus_offset = 13; + input.sel_focus_affinity = ax::mojom::TextAffinity::kDownstream; + + 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("4", output.doctype); + EXPECT_EQ(true, output.loaded); + EXPECT_EQ(5, output.loading_progress); + EXPECT_EQ("6", output.mimetype); + EXPECT_EQ("7", output.title); + EXPECT_EQ("8", output.url); + EXPECT_EQ(9, output.focus_id); + EXPECT_EQ(10, output.sel_anchor_object_id); + EXPECT_EQ(11, output.sel_anchor_offset); + EXPECT_EQ(ax::mojom::TextAffinity::kUpstream, output.sel_anchor_affinity); + EXPECT_EQ(12, output.sel_focus_object_id); + EXPECT_EQ(13, output.sel_focus_offset); + EXPECT_EQ(ax::mojom::TextAffinity::kDownstream, output.sel_focus_affinity); +} diff --git a/chromium/ui/accessibility/mojom/ax_tree_update.mojom b/chromium/ui/accessibility/mojom/ax_tree_update.mojom new file mode 100644 index 00000000000..b121cd6c66f --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_update.mojom @@ -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. + +module ax.mojom; + +import "ui/accessibility/ax_enums.mojom"; +import "ui/accessibility/mojom/ax_node_data.mojom"; +import "ui/accessibility/mojom/ax_tree_data.mojom"; + +// See ui::AXTreeUpdate for comments / explanations of these fields. +struct AXTreeUpdate { + bool has_tree_data; + AXTreeData tree_data; + int32 node_id_to_clear; + int32 root_id; + array<AXNodeData> nodes; +}; diff --git a/chromium/ui/accessibility/mojom/ax_tree_update.typemap b/chromium/ui/accessibility/mojom/ax_tree_update.typemap new file mode 100644 index 00000000000..0fa557951e4 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_update.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_update.mojom" +public_headers = [ "//ui/accessibility/ax_tree_update.h" ] +traits_headers = [ "//ui/accessibility/mojom/ax_tree_update_mojom_traits.h" ] +sources = [ + "ax_tree_update_mojom_traits.cc", + "ax_tree_update_mojom_traits.h", +] +public_deps = [ + "//ui/accessibility", +] +type_mappings = [ "ax.mojom.AXTreeUpdate=ui::AXTreeUpdate" ] diff --git a/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits.cc b/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits.cc new file mode 100644 index 00000000000..c3f29453c86 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits.cc @@ -0,0 +1,23 @@ +// 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_update_mojom_traits.h" + +namespace mojo { + +// static +bool StructTraits<ax::mojom::AXTreeUpdateDataView, ui::AXTreeUpdate>::Read( + ax::mojom::AXTreeUpdateDataView data, + ui::AXTreeUpdate* out) { + out->has_tree_data = data.has_tree_data(); + if (!data.ReadTreeData(&out->tree_data)) + return false; + out->node_id_to_clear = data.node_id_to_clear(); + out->root_id = data.root_id(); + if (!data.ReadNodes(&out->nodes)) + return false; + return true; +} + +} // namespace mojo diff --git a/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits.h b/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits.h new file mode 100644 index 00000000000..90718e3b849 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits.h @@ -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. + +#ifndef UI_ACCESSIBILITY_MOJOM_AX_TREE_UPDATE_MOJOM_TRAITS_H_ +#define UI_ACCESSIBILITY_MOJOM_AX_TREE_UPDATE_MOJOM_TRAITS_H_ + +#include "ui/accessibility/ax_tree_update.h" +#include "ui/accessibility/mojom/ax_node_data_mojom_traits.h" +#include "ui/accessibility/mojom/ax_tree_data_mojom_traits.h" +#include "ui/accessibility/mojom/ax_tree_update.mojom-shared.h" + +namespace mojo { + +template <> +struct StructTraits<ax::mojom::AXTreeUpdateDataView, ui::AXTreeUpdate> { + static bool has_tree_data(const ui::AXTreeUpdate& p) { + return p.has_tree_data; + } + static ui::AXTreeData tree_data(const ui::AXTreeUpdate& p) { + return p.tree_data; + } + static int32_t node_id_to_clear(const ui::AXTreeUpdate& p) { + return p.node_id_to_clear; + } + static int32_t root_id(const ui::AXTreeUpdate& p) { return p.root_id; } + static std::vector<ui::AXNodeData> nodes(const ui::AXTreeUpdate& p) { + return p.nodes; + } + + static bool Read(ax::mojom::AXTreeUpdateDataView data, ui::AXTreeUpdate* out); +}; + +} // namespace mojo + +#endif // UI_ACCESSIBILITY_MOJOM_AX_TREE_UPDATE_MOJOM_TRAITS_H_ diff --git a/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits_unittest.cc b/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits_unittest.cc new file mode 100644 index 00000000000..49b3d262be8 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_update_mojom_traits_unittest.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/accessibility/mojom/ax_tree_update_mojom_traits.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/ax_tree_update.h" +#include "ui/accessibility/mojom/ax_tree_update.mojom.h" + +using mojo::test::SerializeAndDeserialize; + +TEST(AXTreeUpdateMojomTraitsTest, TestSerializeAndDeserializeAXTreeUpdate) { + ui::AXTreeUpdate input, output; + input.has_tree_data = true; + input.tree_data.focus_id = 1; + input.node_id_to_clear = 2; + input.root_id = 3; + input.nodes.resize(2); + input.nodes[0].role = ax::mojom::Role::kButton; + input.nodes[1].id = 4; + EXPECT_TRUE( + SerializeAndDeserialize<ax::mojom::AXTreeUpdate>(&input, &output)); + EXPECT_EQ(true, output.has_tree_data); + EXPECT_EQ(1, output.tree_data.focus_id); + EXPECT_EQ(2, output.node_id_to_clear); + EXPECT_EQ(3, output.root_id); + ASSERT_EQ(2U, output.nodes.size()); + EXPECT_EQ(ax::mojom::Role::kButton, output.nodes[0].role); + EXPECT_EQ(4, output.nodes[1].id); +} diff --git a/chromium/ui/accessibility/mojom/typemaps.gni b/chromium/ui/accessibility/mojom/typemaps.gni new file mode 100644 index 00000000000..019293d4809 --- /dev/null +++ b/chromium/ui/accessibility/mojom/typemaps.gni @@ -0,0 +1,9 @@ +# 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. + +typemaps = [ + "//ui/accessibility/mojom/ax_node_data.typemap", + "//ui/accessibility/mojom/ax_tree_data.typemap", + "//ui/accessibility/mojom/ax_tree_update.typemap", +] diff --git a/chromium/ui/accessibility/platform/ax_platform_node.cc b/chromium/ui/accessibility/platform/ax_platform_node.cc index f0f00d69bd1..8522f21599e 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node.cc @@ -22,6 +22,9 @@ base::LazyInstance<AXPlatformNode::NativeWindowHandlerCallback>::Leaky AXPlatformNode::native_window_handler_ = LAZY_INSTANCE_INITIALIZER; // static +bool AXPlatformNode::is_autofill_shown_ = false; + +// static AXPlatformNode* AXPlatformNode::FromNativeWindow( gfx::NativeWindow native_window) { if (native_window_handler_.Get()) @@ -72,4 +75,19 @@ void AXPlatformNode::NotifyAddAXModeFlags(AXMode mode_flags) { observer.OnAXModeAdded(mode_flags); } +// static +void AXPlatformNode::OnAutofillShown() { + is_autofill_shown_ = true; +} + +// static +void AXPlatformNode::OnAutofillHidden() { + is_autofill_shown_ = false; +} + +// static +bool AXPlatformNode::IsAutofillShown() { + return is_autofill_shown_; +} + } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node.h b/chromium/ui/accessibility/platform/ax_platform_node.h index f723bf064c4..95d38c7288d 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node.h +++ b/chromium/ui/accessibility/platform/ax_platform_node.h @@ -53,6 +53,10 @@ class AX_EXPORT AXPlatformNode { // the addition of an AXMode flag. static void NotifyAddAXModeFlags(AXMode mode_flags); + static void OnAutofillShown(); + static void OnAutofillHidden(); + static bool IsAutofillShown(); + // Call Destroy rather than deleting this, because the subclass may // use reference counting. virtual void Destroy(); @@ -84,6 +88,8 @@ class AX_EXPORT AXPlatformNode { static base::LazyInstance<NativeWindowHandlerCallback>::Leaky native_window_handler_; + static bool is_autofill_shown_; + DISALLOW_COPY_AND_ASSIGN(AXPlatformNode); }; diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc index b502a6786c6..b73cd96f754 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc @@ -986,9 +986,9 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { atk_state_set_add_state(atk_state_set, ATK_STATE_HAS_POPUP); #endif #endif - if (data.HasState(ax::mojom::State::kSelected)) + if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTED); - if (data.HasState(ax::mojom::State::kSelectable)) + if (data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTABLE); // Checked state @@ -1113,6 +1113,7 @@ void AXPlatformNodeAuraLinux::NotifyAccessibilityEvent( ax::mojom::Event event_type) { switch (event_type) { case ax::mojom::Event::kFocus: + case ax::mojom::Event::kFocusContext: OnFocused(); break; default: diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base.h b/chromium/ui/accessibility/platform/ax_platform_node_base.h index 2525833c6c0..3b308c716f5 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_base.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_base.h @@ -134,6 +134,8 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode { // that might send notifications. bool IsLeaf(); + bool HasFocus(); + virtual base::string16 GetText(); virtual base::string16 GetValue(); diff --git a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm index 6a439f162f8..6c12a5cb164 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm +++ b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm @@ -200,6 +200,8 @@ EventMap BuildEventMap() { const EventMap::value_type events[] = { {ax::mojom::Event::kFocus, NSAccessibilityFocusedUIElementChangedNotification}, + {ax::mojom::Event::kFocusContext, + NSAccessibilityFocusedUIElementChangedNotification}, {ax::mojom::Event::kTextChanged, NSAccessibilityTitleChangedNotification}, {ax::mojom::Event::kValueChanged, NSAccessibilityValueChangedNotification}, @@ -514,7 +516,8 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { // Since tabs use the Radio Button role on Mac, the standard way to set // them is via the value attribute rather than the selected attribute. if (node_->GetData().role == ax::mojom::Role::kTab) - return !node_->GetData().HasState(ax::mojom::State::kSelected); + return !node_->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected); return restriction != ax::mojom::Restriction::kReadOnly; } @@ -719,7 +722,8 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { // Misc attributes. - (NSNumber*)AXSelected { - return @(node_->GetData().HasState(ax::mojom::State::kSelected)); + return + @(node_->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)); } - (NSString*)AXPlaceholderValue { diff --git a/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc index 9cb8b4428a3..29a5404d084 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_unittest.cc @@ -74,7 +74,7 @@ AXTreeUpdate AXPlatformNodeTest::BuildTextFieldWithSelectionRange( text_field_node.id = 1; text_field_node.role = ax::mojom::Role::kTextField; text_field_node.AddState(ax::mojom::State::kEditable); - text_field_node.AddState(ax::mojom::State::kSelected); + text_field_node.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); text_field_node.AddIntAttribute(ax::mojom::IntAttribute::kTextSelStart, start); text_field_node.AddIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, stop); @@ -108,7 +108,8 @@ AXTreeUpdate AXPlatformNodeTest::BuildContentEditableWithSelectionRange( content_editable_node.id = 1; content_editable_node.role = ax::mojom::Role::kGroup; content_editable_node.AddState(ax::mojom::State::kRichlyEditable); - content_editable_node.AddState(ax::mojom::State::kSelected); + content_editable_node.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, + true); content_editable_node.AddBoolAttribute( ax::mojom::BoolAttribute::kEditableRoot, true); content_editable_node.SetValue("How now brown cow."); diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.cc b/chromium/ui/accessibility/platform/ax_platform_node_win.cc index f266a181e11..8a0e88b2ecb 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.cc @@ -347,9 +347,17 @@ void AXPlatformNodeWin::NotifyAccessibilityEvent(ax::mojom::Event event_type) { // Menu items fire selection events but Windows screen readers work reliably // with focus events. Remap here. - if (event_type == ax::mojom::Event::kSelection && - GetData().role == ax::mojom::Role::kMenuItem) - event_type = ax::mojom::Event::kFocus; + if (event_type == ax::mojom::Event::kSelection) { + // A menu item could have something other than a role of + // |ROLE_SYSTEM_MENUITEM|. Zoom modification controls for example have a + // role of button. + auto* parent = + static_cast<AXPlatformNodeWin*>(FromNativeViewAccessible(GetParent())); + if (MSAARole() == ROLE_SYSTEM_MENUITEM || + (parent && parent->MSAARole() == ROLE_SYSTEM_MENUPOPUP)) { + event_type = ax::mojom::Event::kFocus; + } + } int native_event = MSAAEvent(event_type); if (native_event < EVENT_MIN) @@ -844,7 +852,8 @@ STDMETHODIMP AXPlatformNodeWin::get_accSelection(VARIANT* selected) { for (int i = 0; i < delegate_->GetChildCount(); ++i) { auto* node = static_cast<AXPlatformNodeWin*>( FromNativeViewAccessible(delegate_->ChildAtIndex(i))); - if (node && node->GetData().HasState(ax::mojom::State::kSelected)) + if (node && + node->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) selected_nodes.emplace_back(node); } @@ -1440,7 +1449,8 @@ STDMETHODIMP AXPlatformNodeWin::get_nSelectedChildren(LONG* cell_count) { for (int r = 0; r < rows; ++r) { for (int c = 0; c < columns; ++c) { AXPlatformNodeBase* cell = GetTableCell(r, c); - if (cell && cell->GetData().HasState(ax::mojom::State::kSelected)) + if (cell && + cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) result++; } } @@ -1467,7 +1477,8 @@ STDMETHODIMP AXPlatformNodeWin::get_nSelectedColumns(LONG* column_count) { bool selected = true; for (int r = 0; r < rows && selected == true; ++r) { AXPlatformNodeBase* cell = GetTableCell(r, c); - if (!cell || !(cell->GetData().HasState(ax::mojom::State::kSelected))) + if (!cell || !(cell->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected))) selected = false; } if (selected) @@ -1497,7 +1508,8 @@ STDMETHODIMP AXPlatformNodeWin::get_nSelectedRows(LONG* row_count) { bool selected = true; for (int c = 0; c < columns && selected == true; ++c) { AXPlatformNodeBase* cell = GetTableCell(r, c); - if (!cell || !(cell->GetData().HasState(ax::mojom::State::kSelected))) + if (!cell || !(cell->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected))) selected = false; } if (selected) @@ -1607,7 +1619,8 @@ STDMETHODIMP AXPlatformNodeWin::get_selectedChildren(LONG max_children, for (int r = 0; r < rows; ++r) { for (int c = 0; c < columns; ++c) { AXPlatformNodeBase* cell = GetTableCell(r, c); - if (cell && cell->GetData().HasState(ax::mojom::State::kSelected)) + if (cell && + cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) // index is row index * column count + column index. results.push_back(r * columns + c); } @@ -1636,7 +1649,8 @@ STDMETHODIMP AXPlatformNodeWin::get_selectedColumns(LONG max_columns, bool selected = true; for (int r = 0; r < row_count && selected == true; ++r) { AXPlatformNodeBase* cell = GetTableCell(r, c); - if (!cell || !(cell->GetData().HasState(ax::mojom::State::kSelected))) + if (!cell || !(cell->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected))) selected = false; } if (selected) @@ -1664,7 +1678,8 @@ STDMETHODIMP AXPlatformNodeWin::get_selectedRows(LONG max_rows, bool selected = true; for (int c = 0; c < column_count && selected == true; ++c) { AXPlatformNodeBase* cell = GetTableCell(r, c); - if (!cell || !(cell->GetData().HasState(ax::mojom::State::kSelected))) + if (!cell || !(cell->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected))) selected = false; } if (selected) @@ -1701,7 +1716,8 @@ STDMETHODIMP AXPlatformNodeWin::get_isColumnSelected(LONG column, for (int r = 0; r < rows; ++r) { AXPlatformNodeBase* cell = GetTableCell(r, column); - if (!cell || !(cell->GetData().HasState(ax::mojom::State::kSelected))) + if (!cell || !(cell->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected))) return S_OK; } @@ -1724,7 +1740,8 @@ STDMETHODIMP AXPlatformNodeWin::get_isRowSelected(LONG row, for (int c = 0; c < columns; ++c) { AXPlatformNodeBase* cell = GetTableCell(row, c); - if (!cell || !(cell->GetData().HasState(ax::mojom::State::kSelected))) + if (!cell || !(cell->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected))) return S_OK; } @@ -1748,7 +1765,8 @@ STDMETHODIMP AXPlatformNodeWin::get_isSelected(LONG row, return S_FALSE; AXPlatformNodeBase* cell = GetTableCell(row, column); - if (cell && cell->GetData().HasState(ax::mojom::State::kSelected)) + if (cell && + cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) *is_selected = true; return S_OK; @@ -1865,7 +1883,8 @@ STDMETHODIMP AXPlatformNodeWin::get_selectedCells(IUnknown*** cells, for (int r = 0; r < rows; ++r) { for (int c = 0; c < columns; ++c) { AXPlatformNodeBase* cell = GetTableCell(r, c); - if (cell && cell->GetData().HasState(ax::mojom::State::kSelected)) + if (cell && + cell->GetData().GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) selected.push_back(cell); } } @@ -2411,7 +2430,10 @@ int AXPlatformNodeWin::MSAARole() { return ROLE_SYSTEM_ALERT; case ax::mojom::Role::kAlertDialog: - return ROLE_SYSTEM_DIALOG; + // We temporarily use |ROLE_SYSTEM_ALERT| because some Windows screen + // readers are not compatible with |ax::mojom::Role::kAlertDialog| yet. + // TODO(aleventhal) modify this to return |ROLE_SYSTEM_DIALOG|. + return ROLE_SYSTEM_ALERT; case ax::mojom::Role::kAnchor: return ROLE_SYSTEM_LINK; @@ -2859,8 +2881,10 @@ int32_t AXPlatformNodeWin::ComputeIA2State() { ia2_state |= IA2_STATE_SELECTABLE_TEXT; } - if (!GetStringAttribute(ax::mojom::StringAttribute::kAutoComplete).empty()) + if (!GetStringAttribute(ax::mojom::StringAttribute::kAutoComplete).empty() || + IsAutofillField()) { ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION; + } if (GetBoolAttribute(ax::mojom::BoolAttribute::kModal)) ia2_state |= IA2_STATE_MODAL; @@ -3024,6 +3048,11 @@ std::vector<base::string16> AXPlatformNodeWin::ComputeIA2Attributes() { StringAttributeToIA2(result, ax::mojom::StringAttribute::kAutoComplete, "autocomplete"); + if (!HasStringAttribute(ax::mojom::StringAttribute::kAutoComplete) && + IsAutofillField()) { + result.push_back(L"autocomplete:list"); + } + StringAttributeToIA2(result, ax::mojom::StringAttribute::kRoleDescription, "roledescription"); StringAttributeToIA2(result, ax::mojom::StringAttribute::kKeyShortcuts, @@ -3375,7 +3404,7 @@ bool AXPlatformNodeWin::ShouldNodeHaveFocusableState( case ax::mojom::Role::kListBoxOption: case ax::mojom::Role::kMenuListOption: - if (data.HasState(ax::mojom::State::kSelectable)) + if (data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) return true; break; @@ -3386,6 +3415,11 @@ bool AXPlatformNodeWin::ShouldNodeHaveFocusableState( return data.HasState(ax::mojom::State::kFocusable); } +bool AXPlatformNodeWin::IsAutofillField() { + return IsAutofillShown() && IsPlainTextField() && + delegate_->GetFocus() == GetNativeViewAccessible(); +} + int AXPlatformNodeWin::MSAAState() { const AXNodeData& data = GetData(); int msaa_state = 0; @@ -3410,7 +3444,9 @@ int AXPlatformNodeWin::MSAAState() { if (ShouldNodeHaveFocusableState(data)) msaa_state |= STATE_SYSTEM_FOCUSABLE; - if (data.HasState(ax::mojom::State::kHaspopup)) + // Note: autofill is special-cased here because there is no way for the + // browser to know when the autofill popup is shown. + if (data.HasState(ax::mojom::State::kHaspopup) || IsAutofillField()) msaa_state |= STATE_SYSTEM_HASPOPUP; // TODO(dougt) unhandled ux::ax::mojom::State::kHorizontal @@ -3448,10 +3484,10 @@ int AXPlatformNodeWin::MSAAState() { // TODO(dougt) unhandled ux::ax::mojom::State::kRequired // TODO(dougt) unhandled ux::ax::mojom::State::kRichlyEditable - if (data.HasState(ax::mojom::State::kSelectable)) + if (data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) msaa_state |= STATE_SYSTEM_SELECTABLE; - if (data.HasState(ax::mojom::State::kSelected)) + if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) msaa_state |= STATE_SYSTEM_SELECTED; // TODO(dougt) unhandled VERTICAL @@ -3504,6 +3540,22 @@ int AXPlatformNodeWin::MSAAState() { if (focus == GetNativeViewAccessible()) msaa_state |= STATE_SYSTEM_FOCUSED; + // 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) && + data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) { + AXPlatformNodeBase* container = FromNativeViewAccessible(GetParent()); + if (container && container->GetParent() == focus) { + ui::AXNodeData container_data = container->GetData(); + if ((container_data.role == ax::mojom::Role::kListBox || + container_data.role == ax::mojom::Role::kMenu) && + !container_data.HasState(ax::mojom::State::kMultiselectable)) { + msaa_state |= STATE_SYSTEM_FOCUSED; + } + } + } + // On Windows, the "focus" bit should be set on certain containers, like // menu bars, when visible. // @@ -3534,7 +3586,10 @@ int AXPlatformNodeWin::MSAAEvent(ax::mojom::Event event) { case ax::mojom::Event::kExpandedChanged: return EVENT_OBJECT_STATECHANGE; case ax::mojom::Event::kFocus: + case ax::mojom::Event::kFocusContext: return EVENT_OBJECT_FOCUS; + case ax::mojom::Event::kLiveRegionChanged: + return EVENT_OBJECT_LIVEREGIONCHANGED; case ax::mojom::Event::kMenuStart: return EVENT_SYSTEM_MENUSTART; case ax::mojom::Event::kMenuEnd: diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.h b/chromium/ui/accessibility/platform/ax_platform_node_win.h index 4efab44077a..b9177015b47 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.h @@ -775,6 +775,8 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) bool IsAncestorComboBox(); + // Is in a focused texfield with a related autofill popup currently visible. + bool IsAutofillField(); }; } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc index 20de83957ca..ac24063a4f9 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc @@ -295,7 +295,7 @@ TEST_F(AXPlatformNodeWinTest, AXNodeData list_item_1; list_item_1.id = 1; list_item_1.role = ax::mojom::Role::kListBoxOption; - list_item_1.AddState(ax::mojom::State::kSelected); + list_item_1.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); list_item_1.SetName("Name1"); AXNodeData list_item_2; @@ -327,13 +327,13 @@ TEST_F(AXPlatformNodeWinTest, AXNodeData list_item_1; list_item_1.id = 1; list_item_1.role = ax::mojom::Role::kListBoxOption; - list_item_1.AddState(ax::mojom::State::kSelected); + list_item_1.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); list_item_1.SetName("Name1"); AXNodeData list_item_2; list_item_2.id = 2; list_item_2.role = ax::mojom::Role::kListBoxOption; - list_item_2.AddState(ax::mojom::State::kSelected); + list_item_2.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); list_item_2.SetName("Name2"); AXNodeData list_item_3; @@ -415,7 +415,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionTableRowOneSelected) { AXTreeUpdate update = Build3X3Table(); // 5 == table_row_1 - update.nodes[5].AddState(ax::mojom::State::kSelected); + update.nodes[5].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -443,8 +443,8 @@ TEST_F(AXPlatformNodeWinTest, // 5 == table_row_1 // 9 == table_row_2 - update.nodes[5].AddState(ax::mojom::State::kSelected); - update.nodes[9].AddState(ax::mojom::State::kSelected); + update.nodes[5].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[9].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -505,7 +505,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionTableCellOneSelected) { AXTreeUpdate update = Build3X3Table(); // 7 == table_cell_1 - update.nodes[7].AddState(ax::mojom::State::kSelected); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -543,8 +543,8 @@ TEST_F(AXPlatformNodeWinTest, // 11 == table_cell_3 // 12 == table_cell_4 - update.nodes[11].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1647,7 +1647,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedChildrenOne) { AXTreeUpdate update = Build3X3Table(); // 7 == table_cell_1 - update.nodes[7].AddState(ax::mojom::State::kSelected); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); ComPtr<IAccessibleTableCell> cell = GetCellInTable(); @@ -1672,10 +1672,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedChildrenMany) { // 8 == table_cell_2 // 11 == table_cell_3 // 12 == table_cell_4 - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1718,9 +1718,9 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedColumnsOne) { // 3 == table_column_header_2 // 7 == table_cell_1 // 11 == table_cell_3 - update.nodes[3].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); + update.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1745,16 +1745,16 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedColumnsMany) { // 3 == table_column_header_2 // 7 == table_cell_1 // 11 == table_cell_3 - update.nodes[3].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); + update.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); // 4 == table_column_header_3 // 8 == table_cell_2 // 12 == table_cell_4 - update.nodes[4].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[4].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1797,9 +1797,9 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedRowsOne) { // 6 == table_row_header_1 // 7 == table_cell_1 // 8 == table_cell_2 - update.nodes[6].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); + update.nodes[6].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1824,16 +1824,16 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedRowsMany) { // 6 == table_row_header_3 // 7 == table_cell_1 // 8 == table_cell_2 - update.nodes[6].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); + update.nodes[6].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); // 10 == table_row_header_3 // 11 == table_cell_1 // 12 == table_cell_2 - update.nodes[10].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[10].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1857,8 +1857,8 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedChildren) { // 7 == table_cell_1 // 12 == table_cell_4 - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1886,8 +1886,8 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedChildrenZeroMax) { // 7 == table_cell_1 // 12 == table_cell_4 - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1911,8 +1911,8 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedColumnsZero) { // 7 == table_cell_1 // 11 == table_cell_3 - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1940,9 +1940,9 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedColumnsOne) { // 3 == table_column_header_2 // 7 == table_cell_1 // 11 == table_cell_3 - update.nodes[3].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); + update.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -1971,16 +1971,16 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedColumnsMany) { // 3 == table_column_header_2 // 7 == table_cell_1 // 11 == table_cell_3 - update.nodes[3].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); + update.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); // 4 == table_column_header_3 // 8 == table_cell_2 // 12 == table_cell_4 - update.nodes[4].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[4].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -2030,9 +2030,9 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedRowsOne) { // 6 == table_row_header_1 // 7 == table_cell_1 // 8 == table_cell_2 - update.nodes[6].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); + update.nodes[6].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -2060,16 +2060,16 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedRowsMany) { // 6 == table_row_header_3 // 7 == table_cell_1 // 8 == table_cell_2 - update.nodes[6].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); + update.nodes[6].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); // 10 == table_row_header_3 // 11 == table_cell_1 // 12 == table_cell_2 - update.nodes[10].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[10].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -2098,9 +2098,9 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsColumnSelected) { // 3 == table_column_header_2 // 7 == table_cell_1 // 11 == table_cell_3 - update.nodes[3].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[11].AddState(ax::mojom::State::kSelected); + update.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[11].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -2134,9 +2134,9 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsRowSelected) { // 6 == table_row_header_3 // 7 == table_cell_1 // 8 == table_cell_2 - update.nodes[6].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); + update.nodes[6].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -2170,9 +2170,9 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsSelected) { // 6 == table_row_header_3 // 7 == table_cell_1 // 8 == table_cell_2 - update.nodes[6].AddState(ax::mojom::State::kSelected); - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[8].AddState(ax::mojom::State::kSelected); + update.nodes[6].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[8].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); @@ -2235,8 +2235,8 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTable2GetSelectedChildren) { // 7 == table_cell_1 // 12 == table_cell_4 - update.nodes[7].AddState(ax::mojom::State::kSelected); - update.nodes[12].AddState(ax::mojom::State::kSelected); + update.nodes[7].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); + update.nodes[12].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true); Init(update); diff --git a/chromium/ui/accessibility/platform/ax_platform_relation_win.cc b/chromium/ui/accessibility/platform/ax_platform_relation_win.cc index 803fc97e4d8..8c3402e7417 100644 --- a/chromium/ui/accessibility/platform/ax_platform_relation_win.cc +++ b/chromium/ui/accessibility/platform/ax_platform_relation_win.cc @@ -122,7 +122,7 @@ int AXPlatformRelationWin::EnumerateRelationships( if (first_time) { for (int32_t attr_index = static_cast<int32_t>(ax::mojom::IntAttribute::kNone); - attr_index <= static_cast<int32_t>(ax::mojom::IntAttribute::kLast); + attr_index <= static_cast<int32_t>(ax::mojom::IntAttribute::kMaxValue); ++attr_index) { auto attr = static_cast<ax::mojom::IntAttribute>(attr_index); if (!GetIA2ReverseRelationFromIntAttr(attr).empty()) @@ -130,7 +130,8 @@ int AXPlatformRelationWin::EnumerateRelationships( } for (int32_t attr_index = static_cast<int32_t>(ax::mojom::IntListAttribute::kNone); - attr_index <= static_cast<int32_t>(ax::mojom::IntListAttribute::kLast); + attr_index <= + static_cast<int32_t>(ax::mojom::IntListAttribute::kMaxValue); ++attr_index) { auto attr = static_cast<ax::mojom::IntListAttribute>(attr_index); if (!GetIA2ReverseRelationFromIntListAttr(attr).empty()) diff --git a/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.cc b/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.cc index 998ae65757c..7ef8a5c6689 100644 --- a/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.cc +++ b/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.cc @@ -7,7 +7,6 @@ #include <string> #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_enums.mojom.h" diff --git a/chromium/ui/accessibility/run_all_unittests.cc b/chromium/ui/accessibility/run_all_unittests.cc new file mode 100644 index 00000000000..3cdbe276dd9 --- /dev/null +++ b/chromium/ui/accessibility/run_all_unittests.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 "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#include "build/build_config.h" +#include "mojo/edk/embedder/embedder.h" + +int main(int argc, char** argv) { + mojo::edk::Init(); + + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, argv, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); +} diff --git a/chromium/ui/android/BUILD.gn b/chromium/ui/android/BUILD.gn index a9196e95dc7..4002626c015 100644 --- a/chromium/ui/android/BUILD.gn +++ b/chromium/ui/android/BUILD.gn @@ -63,7 +63,7 @@ component("android") { "//components/viz/service", "//services/viz/public/interfaces", "//skia", - "//third_party/WebKit/public:blink_headers", + "//third_party/blink/public:blink_headers", "//ui/base", "//ui/compositor", "//ui/display", @@ -197,16 +197,21 @@ android_library("ui_full_java") { "java/src/org/chromium/ui/DropdownDividerDrawable.java", "java/src/org/chromium/ui/DropdownItem.java", "java/src/org/chromium/ui/DropdownItemBase.java", + "java/src/org/chromium/ui/DropdownPopupWindowImpl.java", "java/src/org/chromium/ui/DropdownPopupWindow.java", + "java/src/org/chromium/ui/DropdownPopupWindowJellyBean.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/VSyncMonitor.java", + "java/src/org/chromium/ui/base/ActivityAndroidPermissionDelegate.java", "java/src/org/chromium/ui/base/ActivityWindowAndroid.java", "java/src/org/chromium/ui/base/AndroidPermissionDelegate.java", "java/src/org/chromium/ui/base/Clipboard.java", "java/src/org/chromium/ui/base/DeviceFormFactor.java", "java/src/org/chromium/ui/base/EventForwarder.java", "java/src/org/chromium/ui/base/LocalizationUtils.java", + "java/src/org/chromium/ui/base/PermissionCallback.java", "java/src/org/chromium/ui/base/ResourceBundle.java", "java/src/org/chromium/ui/base/SelectFileDialog.java", "java/src/org/chromium/ui/base/SPenSupport.java", @@ -217,6 +222,7 @@ android_library("ui_full_java") { "java/src/org/chromium/ui/display/DisplayAndroid.java", "java/src/org/chromium/ui/display/DisplayAndroidManager.java", "java/src/org/chromium/ui/display/DisplaySwitches.java", + "java/src/org/chromium/ui/display/DisplayUtil.java", "java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java", "java/src/org/chromium/ui/display/VirtualDisplayAndroid.java", "java/src/org/chromium/ui/events/devices/InputDeviceObserver.java", @@ -248,6 +254,7 @@ android_library("ui_full_java") { "java/src/org/chromium/ui/widget/TextViewWithClickableSpans.java", "java/src/org/chromium/ui/widget/TextViewWithLeading.java", "java/src/org/chromium/ui/widget/Toast.java", + "java/src/org/chromium/ui/widget/UiWidgetFactory.java", "java/src/org/chromium/ui/widget/ViewRectProvider.java", ] deps = [ @@ -259,7 +266,7 @@ android_library("ui_full_java") { ] srcjar_deps = [ ":java_enums_srcjar", - "//third_party/WebKit/public:blink_cursor_type_java_enums_srcjar", + "//third_party/blink/public:blink_cursor_type_java_enums_srcjar", ] } @@ -295,6 +302,7 @@ junit_binary("ui_junit_tests") { test("ui_android_unittests") { sources = [ + "delegated_frame_host_android_unittest.cc", "overscroll_refresh_unittest.cc", "resources/resource_manager_impl_unittest.cc", "run_all_unittests.cc", @@ -308,10 +316,13 @@ test("ui_android_unittests") { "//base/test:test_support", "//cc", "//cc:test_support", + "//components/viz/host:host", + "//components/viz/service:service", "//skia", "//testing/gmock", "//testing/gtest", "//ui/base", + "//ui/compositor", "//ui/events", "//ui/gfx", "//ui/resources:ui_test_pak", diff --git a/chromium/ui/android/DEPS b/chromium/ui/android/DEPS index 6f43de66845..bfdcf7553b2 100644 --- a/chromium/ui/android/DEPS +++ b/chromium/ui/android/DEPS @@ -13,7 +13,7 @@ include_rules = [ "+jni", "+services/viz/public/interfaces", "+skia/ext", - "+third_party/WebKit/public/platform/WebCursorInfo.h", + "+third_party/blink/public/platform/web_cursor_info.h", "+third_party/skia", "+ui/base", "+ui/compositor/compositor_lock.h", diff --git a/chromium/ui/android/delegated_frame_host_android.cc b/chromium/ui/android/delegated_frame_host_android.cc index 07798eaf868..cea1307c5eb 100644 --- a/chromium/ui/android/delegated_frame_host_android.cc +++ b/chromium/ui/android/delegated_frame_host_android.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "cc/layers/solid_color_layer.h" #include "cc/layers/surface_layer.h" #include "components/viz/common/features.h" @@ -27,16 +26,18 @@ namespace ui { namespace { scoped_refptr<cc::SurfaceLayer> CreateSurfaceLayer( - viz::SurfaceInfo surface_info, + const viz::SurfaceId& surface_id, + const gfx::Size& size_in_pixels, bool surface_opaque) { // manager must outlive compositors using it. auto layer = cc::SurfaceLayer::Create(); - layer->SetPrimarySurfaceId(surface_info.id(), + layer->SetPrimarySurfaceId(surface_id, cc::DeadlinePolicy::UseDefaultDeadline()); - layer->SetFallbackSurfaceId(surface_info.id()); - layer->SetBounds(surface_info.size_in_pixels()); + layer->SetFallbackSurfaceId(surface_id); + layer->SetBounds(size_in_pixels); layer->SetIsDrawable(true); layer->SetContentsOpaque(surface_opaque); + layer->SetHitTestable(true); return layer; } @@ -89,7 +90,8 @@ void DelegatedFrameHostAndroid::SubmitCompositorFrame( std::move(hit_test_region_list)); content_layer_ = - CreateSurfaceLayer(surface_info_, !has_transparent_background_); + CreateSurfaceLayer(surface_info_.id(), surface_info_.size_in_pixels(), + !has_transparent_background_); view_->GetLayer()->AddChild(content_layer_); } else { support_->SubmitCompositorFrame(local_surface_id, std::move(frame), @@ -111,27 +113,28 @@ void DelegatedFrameHostAndroid::CopyFromCompositingSurface( const gfx::Rect& src_subrect, const gfx::Size& output_size, base::OnceCallback<void(const SkBitmap&)> callback) { + // TODO(vmpstr): We should defer this request until such time that this + // returns true. https://crbug.com/826097. if (!CanCopyFromCompositingSurface()) { std::move(callback).Run(SkBitmap()); return; } - scoped_refptr<cc::Layer> readback_layer = - CreateSurfaceLayer(surface_info_, !has_transparent_background_); - readback_layer->SetHideLayerAndSubtree(true); - view_->GetWindowAndroid()->GetCompositor()->AttachLayerForReadback( - readback_layer); + WindowAndroidCompositor* compositor = + view_->GetWindowAndroid()->GetCompositor(); + compositor->IncrementReadbackRequestCount(); std::unique_ptr<viz::CopyOutputRequest> request = std::make_unique<viz::CopyOutputRequest>( viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, base::BindOnce( [](base::OnceCallback<void(const SkBitmap&)> callback, - scoped_refptr<cc::Layer> readback_layer, + base::WeakPtr<WindowAndroidCompositor> compositor_weak_ptr, std::unique_ptr<viz::CopyOutputResult> result) { - readback_layer->RemoveFromParent(); + if (compositor_weak_ptr) + compositor_weak_ptr->DecrementReadbackRequestCount(); std::move(callback).Run(result->AsSkBitmap()); }, - std::move(callback), std::move(readback_layer))); + std::move(callback), compositor->GetWeakPtr())); if (src_subrect.IsEmpty()) { request->set_area(gfx::Rect(surface_info_.size_in_pixels())); @@ -147,7 +150,7 @@ void DelegatedFrameHostAndroid::CopyFromCompositingSurface( gfx::Vector2d(output_size.width(), output_size.height())); } - support_->RequestCopyOfSurface(std::move(request)); + support_->RequestCopyOfOutput(local_surface_id_, std::move(request)); } bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const { @@ -156,15 +159,17 @@ bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const { } void DelegatedFrameHostAndroid::DestroyDelegatedContent() { - if (!surface_info_.is_valid()) - return; - - DCHECK(content_layer_); + // TakeFallbackContentFrom() can populate |content_layer_| when + // |surface_info_| is invalid. + if (content_layer_) { + content_layer_->RemoveFromParent(); + content_layer_ = nullptr; + } - content_layer_->RemoveFromParent(); - content_layer_ = nullptr; - support_->EvictLastActivatedSurface(); - surface_info_ = viz::SurfaceInfo(); + if (surface_info_.is_valid()) { + support_->EvictLastActivatedSurface(); + surface_info_ = viz::SurfaceInfo(); + } } bool DelegatedFrameHostAndroid::HasDelegatedContent() const { @@ -228,10 +233,14 @@ void DelegatedFrameHostAndroid::DidPresentCompositorFrame( uint32_t presentation_token, base::TimeTicks time, base::TimeDelta refresh, - uint32_t flags) {} + uint32_t flags) { + client_->DidPresentCompositorFrame(presentation_token, time, refresh, flags); +} void DelegatedFrameHostAndroid::DidDiscardCompositorFrame( - uint32_t presentation_token) {} + uint32_t presentation_token) { + client_->DidDiscardCompositorFrame(presentation_token); +} void DelegatedFrameHostAndroid::OnBeginFrame(const viz::BeginFrameArgs& args) { begin_frame_source_.OnBeginFrame(args); @@ -279,4 +288,15 @@ const viz::LocalSurfaceId& DelegatedFrameHostAndroid::GetLocalSurfaceId() return local_surface_id_; } +void DelegatedFrameHostAndroid::TakeFallbackContentFrom( + DelegatedFrameHostAndroid* other) { + if (content_layer_ || !other->content_layer_) + return; + content_layer_ = + CreateSurfaceLayer(other->content_layer_->fallback_surface_id(), + other->content_layer_->bounds(), + other->content_layer_->contents_opaque()); + view_->GetLayer()->AddChild(content_layer_); +} + } // namespace ui diff --git a/chromium/ui/android/delegated_frame_host_android.h b/chromium/ui/android/delegated_frame_host_android.h index c0fbac92d08..b0fed79e73e 100644 --- a/chromium/ui/android/delegated_frame_host_android.h +++ b/chromium/ui/android/delegated_frame_host_android.h @@ -42,6 +42,11 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid virtual void SetBeginFrameSource( viz::BeginFrameSource* begin_frame_source) = 0; virtual void DidReceiveCompositorFrameAck() = 0; + virtual void DidPresentCompositorFrame(uint32_t presentation_token, + base::TimeTicks time, + base::TimeDelta refresh, + uint32_t flags) = 0; + virtual void DidDiscardCompositorFrame(uint32_t presentation_token) = 0; virtual void ReclaimResources( const std::vector<viz::ReturnedResource>&) = 0; virtual void OnFrameTokenChanged(uint32_t frame_token) = 0; @@ -91,6 +96,8 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid // Returns the local surface ID for this delegated content. const viz::LocalSurfaceId& GetLocalSurfaceId() const; + void TakeFallbackContentFrom(DelegatedFrameHostAndroid* other); + private: // viz::mojom::CompositorFrameSinkClient implementation. void DidReceiveCompositorFrameAck( diff --git a/chromium/ui/android/delegated_frame_host_android_unittest.cc b/chromium/ui/android/delegated_frame_host_android_unittest.cc new file mode 100644 index 00000000000..fc0c6c4b95d --- /dev/null +++ b/chromium/ui/android/delegated_frame_host_android_unittest.cc @@ -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. + +#include "ui/android/delegated_frame_host_android.h" +#include "base/test/test_mock_time_task_runner.h" +#include "cc/layers/layer.h" +#include "cc/layers/solid_color_layer.h" +#include "components/viz/host/host_frame_sink_manager.h" +#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/android/resources/resource_manager.h" +#include "ui/android/view_android.h" +#include "ui/android/window_android_compositor.h" + +namespace ui { +namespace { + +using ::testing::Return; +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Invoke; + +class MockDelegatedFrameHostAndroidClient + : public DelegatedFrameHostAndroid::Client { + public: + MOCK_METHOD1(SetBeginFrameSource, void(viz::BeginFrameSource*)); + MOCK_METHOD0(DidReceiveCompositorFrameAck, void()); + MOCK_METHOD4(DidPresentCompositorFrame, + void(uint32_t, base::TimeTicks, base::TimeDelta, uint32_t)); + MOCK_METHOD1(DidDiscardCompositorFrame, void(uint32_t)); + MOCK_METHOD1(ReclaimResources, + void(const std::vector<viz::ReturnedResource>&)); + MOCK_METHOD1(OnFrameTokenChanged, void(uint32_t)); +}; + +class MockWindowAndroidCompositor : public WindowAndroidCompositor { + public: + MOCK_METHOD0(GetWeakPtr, base::WeakPtr<ui::WindowAndroidCompositor>()); + MOCK_METHOD0(IncrementReadbackRequestCount, void()); + MOCK_METHOD0(DecrementReadbackRequestCount, void()); + MOCK_METHOD1(DoRequestCopyOfOutputOnRootLayer, void(viz::CopyOutputRequest*)); + MOCK_METHOD0(SetNeedsAnimate, void()); + MOCK_METHOD0(GetResourceManager, ResourceManager&()); + MOCK_METHOD0(GetFrameSinkId, viz::FrameSinkId()); + MOCK_METHOD1(AddChildFrameSink, void(const viz::FrameSinkId&)); + MOCK_METHOD1(RemoveChildFrameSink, void(const viz::FrameSinkId&)); + MOCK_METHOD2(DoGetCompositorLock, + CompositorLock*(CompositorLockClient*, base::TimeDelta)); + MOCK_CONST_METHOD0(IsDrawingFirstVisibleFrame, bool()); + + // Helpers for move-only types: + void RequestCopyOfOutputOnRootLayer( + std::unique_ptr<viz::CopyOutputRequest> request) override { + return DoRequestCopyOfOutputOnRootLayer(request.get()); + } + + std::unique_ptr<CompositorLock> GetCompositorLock( + CompositorLockClient* client, + base::TimeDelta time_delta) override { + return std::unique_ptr<CompositorLock>( + DoGetCompositorLock(client, time_delta)); + } +}; + +class MockCompositorLockManagerClient : public ui::CompositorLockManagerClient { + public: + MOCK_METHOD1(OnCompositorLockStateChanged, void(bool)); +}; + +class DelegatedFrameHostAndroidTest : public testing::Test { + public: + DelegatedFrameHostAndroidTest() + : frame_sink_id_(1, 1), + task_runner_(new base::TestMockTimeTaskRunner()), + lock_manager_(task_runner_, &lock_manager_client_) { + host_frame_sink_manager_.SetLocalManager(&frame_sink_manager_impl_); + view_.SetLayer(cc::SolidColorLayer::Create()); + frame_host_ = std::make_unique<DelegatedFrameHostAndroid>( + &view_, &host_frame_sink_manager_, &client_, frame_sink_id_); + } + + static viz::LocalSurfaceId GetFakeId() { + return viz::LocalSurfaceId(1, 1, base::UnguessableToken::Create()); + } + + ui::CompositorLock* GetLock(CompositorLockClient* client, + base::TimeDelta time_delta) { + return lock_manager_.GetCompositorLock(client, time_delta).release(); + } + + void SubmitCompositorFrame() { + viz::CompositorFrame frame; + auto render_pass = viz::RenderPass::Create(); + render_pass->output_rect = gfx::Rect(10, 10); + frame.render_pass_list.push_back(std::move(render_pass)); + frame.metadata.begin_frame_ack.sequence_number = 1; + frame.metadata.device_scale_factor = 1; + frame_host_->SubmitCompositorFrame(GetFakeId(), std::move(frame), + viz::mojom::HitTestRegionList::New()); + } + + protected: + MockWindowAndroidCompositor compositor_; + ui::ViewAndroid view_; + viz::FrameSinkManagerImpl frame_sink_manager_impl_; + viz::HostFrameSinkManager host_frame_sink_manager_; + MockDelegatedFrameHostAndroidClient client_; + viz::FrameSinkId frame_sink_id_; + std::unique_ptr<DelegatedFrameHostAndroid> frame_host_; + MockCompositorLockManagerClient lock_manager_client_; + scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; + CompositorLockManager lock_manager_; +}; + +TEST_F(DelegatedFrameHostAndroidTest, CompositorLockDuringFirstFrame) { + // Attach during the first frame, lock will be taken. + 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_); + + // Lock should be released when we submit a compositor frame. + EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) + .Times(1); + SubmitCompositorFrame(); +} + +TEST_F(DelegatedFrameHostAndroidTest, CompositorLockDuringLaterFrame) { + // Attach after the first frame, lock will not be taken. + EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()) + .WillOnce(Return(false)); + EXPECT_CALL(compositor_, DoGetCompositorLock(_, _)).Times(0); + frame_host_->AttachToCompositor(&compositor_); +} + +TEST_F(DelegatedFrameHostAndroidTest, CompositorLockWithDelegatedContent) { + // Submit a compositor frame to ensure we have delegated content. + SubmitCompositorFrame(); + + // Even though it's the first frame, we won't take the lock as we already have + // delegated content. + EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true)); + EXPECT_CALL(compositor_, DoGetCompositorLock(_, _)).Times(0); + frame_host_->AttachToCompositor(&compositor_); +} + +TEST_F(DelegatedFrameHostAndroidTest, CompositorLockReleasedWithDetach) { + // Attach during the first frame, lock will be taken. + 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_); + + // Lock should be released when we detach. + EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) + .Times(1); + frame_host_->DetachFromCompositor(); +} + +} // namespace +} // namespace ui diff --git a/chromium/ui/android/event_forwarder.cc b/chromium/ui/android/event_forwarder.cc index 7be0652c95d..8386118ef39 100644 --- a/chromium/ui/android/event_forwarder.cc +++ b/chromium/ui/android/event_forwarder.cc @@ -128,8 +128,7 @@ void EventForwarder::OnMouseWheelEvent(JNIEnv* env, jfloat x, jfloat y, jfloat ticks_x, - jfloat ticks_y, - jfloat pixels_per_tick) { + jfloat ticks_y) { if (!ticks_x && !ticks_y) return; @@ -142,6 +141,11 @@ void EventForwarder::OnMouseWheelEvent(JNIEnv* env, delta.InMicroseconds(), 1, 1000000, 50); ui::MotionEventAndroid::Pointer pointer( 0, x, y, 0.0f /* touch_major */, 0.0f /* touch_minor */, 0.0f, 0.0f, 0); + + auto* window = view_->GetWindowAndroid(); + float pixels_per_tick = + window ? window->mouse_wheel_scroll_factor() + : kDefaultMouseWheelTickMultiplier * view_->GetDipScale(); ui::MotionEventAndroid event( env, nullptr, 1.f / view_->GetDipScale(), ticks_x, ticks_y, pixels_per_tick, time_ms, 0 /* action */, 1 /* pointer_count */, @@ -188,13 +192,43 @@ bool EventForwarder::OnGestureEvent(JNIEnv* env, scale, 0, 0, 0, 0, false, false)); } -void EventForwarder::OnStartFling(JNIEnv* env, - const JavaParamRef<jobject>& jobj, - jlong time_ms, - jfloat velocity_x, - jfloat velocity_y, - jboolean synthetic_scroll) { - OnCancelFling(env, jobj, time_ms); +void EventForwarder::Scroll(JNIEnv* env, + const JavaParamRef<jobject>& jobj, + jlong time_ms, + jfloat delta_x, + jfloat delta_y) { + float dip_scale = view_->GetDipScale(); + float delta_xdip = delta_x / dip_scale; + float delta_ydip = delta_y / dip_scale; + view_->OnGestureEvent(GestureEventAndroid( + GESTURE_EVENT_TYPE_SCROLL_START, gfx::PointF(), gfx::PointF(), time_ms, 0, + -delta_xdip, -delta_ydip, 0, 0, true, false)); + view_->OnGestureEvent(GestureEventAndroid( + GESTURE_EVENT_TYPE_SCROLL_BY, gfx::PointF(), gfx::PointF(), time_ms, 0, + -delta_xdip, -delta_ydip, 0, 0, true, false)); + view_->OnGestureEvent(GestureEventAndroid( + GESTURE_EVENT_TYPE_SCROLL_END, gfx::PointF(), gfx::PointF(), time_ms, 0, + -delta_xdip, -delta_ydip, 0, 0, true, false)); +} + +void EventForwarder::DoubleTap(JNIEnv* env, + const JavaParamRef<jobject>& jobj, + jlong time_ms, + jint x, + jint y) { + float dip_scale = view_->GetDipScale(); + view_->OnGestureEvent(GestureEventAndroid( + GESTURE_EVENT_TYPE_DOUBLE_TAP, gfx::PointF(x / dip_scale, y / dip_scale), + gfx::PointF(), time_ms, 0, 0, 0, 0, 0, true, false)); +} + +void EventForwarder::StartFling(JNIEnv* env, + const JavaParamRef<jobject>& jobj, + jlong time_ms, + jfloat velocity_x, + jfloat velocity_y, + jboolean synthetic_scroll) { + CancelFling(env, jobj, time_ms); if (velocity_x == 0 && velocity_y == 0) return; // Use velocity as delta in scroll event. @@ -206,9 +240,9 @@ void EventForwarder::OnStartFling(JNIEnv* env, 0, 0, velocity_x, velocity_y, true, synthetic_scroll)); } -void EventForwarder::OnCancelFling(JNIEnv* env, - const JavaParamRef<jobject>& jobj, - jlong time_ms) { +void EventForwarder::CancelFling(JNIEnv* env, + const JavaParamRef<jobject>& jobj, + jlong time_ms) { view_->OnGestureEvent( GestureEventAndroid(GESTURE_EVENT_TYPE_FLING_CANCEL, gfx::PointF(), gfx::PointF(), time_ms, 0, 0, 0, 0, 0, false, false)); diff --git a/chromium/ui/android/event_forwarder.h b/chromium/ui/android/event_forwarder.h index 68bd4db9735..efeac487b2f 100644 --- a/chromium/ui/android/event_forwarder.h +++ b/chromium/ui/android/event_forwarder.h @@ -71,8 +71,7 @@ class EventForwarder { jfloat x, jfloat y, jfloat ticks_x, - jfloat ticks_y, - jfloat pixels_per_tick); + jfloat ticks_y); void OnDragEvent(JNIEnv* env, const base::android::JavaParamRef<jobject>& jobj, @@ -90,16 +89,28 @@ class EventForwarder { jlong time_ms, jfloat scale); - void OnStartFling(JNIEnv* env, - const base::android::JavaParamRef<jobject>& jobj, - jlong time_ms, - jfloat velocity_x, - jfloat velocity_y, - jboolean synthetic_scroll); - - void OnCancelFling(JNIEnv* env, - const base::android::JavaParamRef<jobject>& jobj, - jlong time_ms); + void Scroll(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jobj, + jlong time_ms, + jfloat delta_x, + jfloat delta_y); + + void DoubleTap(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jobj, + jlong time_ms, + jint x, + jint y); + + void StartFling(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jobj, + jlong time_ms, + jfloat velocity_x, + jfloat velocity_y, + jboolean synthetic_scroll); + + void CancelFling(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jobj, + jlong time_ms); private: friend class ViewAndroid; diff --git a/chromium/ui/android/resources/nine_patch_resource.cc b/chromium/ui/android/resources/nine_patch_resource.cc index d665072defd..4e9a26b4d81 100644 --- a/chromium/ui/android/resources/nine_patch_resource.cc +++ b/chromium/ui/android/resources/nine_patch_resource.cc @@ -4,7 +4,6 @@ #include "ui/android/resources/nine_patch_resource.h" -#include "base/memory/ptr_util.h" #include "cc/layers/nine_patch_layer.h" #include "ui/gfx/geometry/point_f.h" diff --git a/chromium/ui/android/resources/resource.cc b/chromium/ui/android/resources/resource.cc index 5ac17fa0556..385f253bb89 100644 --- a/chromium/ui/android/resources/resource.cc +++ b/chromium/ui/android/resources/resource.cc @@ -4,7 +4,6 @@ #include "ui/android/resources/resource.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/memory_usage_estimator.h" namespace ui { diff --git a/chromium/ui/android/resources/resource_manager_impl_unittest.cc b/chromium/ui/android/resources/resource_manager_impl_unittest.cc index 58a16219901..ab9055b8f09 100644 --- a/chromium/ui/android/resources/resource_manager_impl_unittest.cc +++ b/chromium/ui/android/resources/resource_manager_impl_unittest.cc @@ -5,7 +5,6 @@ #include <stddef.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/process_memory_dump.h" diff --git a/chromium/ui/android/view_android.cc b/chromium/ui/android/view_android.cc index b1b489e06f5..a2e12fc1909 100644 --- a/chromium/ui/android/view_android.cc +++ b/chromium/ui/android/view_android.cc @@ -13,7 +13,7 @@ #include "base/stl_util.h" #include "cc/layers/layer.h" #include "jni/ViewAndroidDelegate_jni.h" -#include "third_party/WebKit/public/platform/WebCursorInfo.h" +#include "third_party/blink/public/platform/web_cursor_info.h" #include "ui/android/event_forwarder.h" #include "ui/android/view_client.h" #include "ui/android/window_android.h" @@ -277,6 +277,23 @@ void ViewAndroid::RemoveObserver(ViewAndroidObserver* observer) { observer_list_.RemoveObserver(observer); } +void ViewAndroid::RequestDisallowInterceptTouchEvent() { + ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate()); + if (delegate.is_null()) + return; + JNIEnv* env = base::android::AttachCurrentThread(); + Java_ViewAndroidDelegate_requestDisallowInterceptTouchEvent(env, delegate); +} + +void ViewAndroid::RequestUnbufferedDispatch(const MotionEventAndroid& event) { + ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate()); + if (delegate.is_null()) + return; + JNIEnv* env = base::android::AttachCurrentThread(); + Java_ViewAndroidDelegate_requestUnbufferedDispatch(env, delegate, + event.GetJavaObject()); +} + void ViewAndroid::OnAttachedToWindow() { for (auto& observer : observer_list_) observer.OnAttachedToWindow(); diff --git a/chromium/ui/android/view_android.h b/chromium/ui/android/view_android.h index 43faf43a1a7..5d32b20a4b1 100644 --- a/chromium/ui/android/view_android.h +++ b/chromium/ui/android/view_android.h @@ -167,6 +167,11 @@ class UI_ANDROID_EXPORT ViewAndroid { void AddObserver(ViewAndroidObserver* observer); void RemoveObserver(ViewAndroidObserver* observer); + void RequestDisallowInterceptTouchEvent(); + void RequestUnbufferedDispatch(const MotionEventAndroid& event); + + ViewAndroid* parent() const { return parent_; } + protected: ViewAndroid* parent_; diff --git a/chromium/ui/android/window_android.cc b/chromium/ui/android/window_android.cc index 2520b391470..9b28a66e033 100644 --- a/chromium/ui/android/window_android.cc +++ b/chromium/ui/android/window_android.cc @@ -24,6 +24,8 @@ using base::android::JavaParamRef; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; +const float kDefaultMouseWheelTickMultiplier = 64; + class WindowAndroid::WindowBeginFrameSource : public viz::BeginFrameSource { public: explicit WindowBeginFrameSource(WindowAndroid* window) @@ -125,12 +127,18 @@ WindowAndroid* WindowAndroid::FromJavaWindowAndroid( AttachCurrentThread(), jwindow_android)); } -WindowAndroid::WindowAndroid(JNIEnv* env, jobject obj, int display_id) +WindowAndroid::WindowAndroid(JNIEnv* env, + jobject obj, + int display_id, + float scroll_factor) : display_id_(display_id), compositor_(NULL), begin_frame_source_(new WindowBeginFrameSource(this)), needs_begin_frames_(false) { java_window_.Reset(env, obj); + mouse_wheel_scroll_factor_ = + scroll_factor > 0 ? scroll_factor + : kDefaultMouseWheelTickMultiplier * GetDipScale(); } void WindowAndroid::Destroy(JNIEnv* env, const JavaParamRef<jobject>& obj) { @@ -295,8 +303,10 @@ ScopedJavaLocalRef<jobject> WindowAndroid::GetWindowToken() { jlong JNI_WindowAndroid_Init(JNIEnv* env, const JavaParamRef<jobject>& obj, - int sdk_display_id) { - WindowAndroid* window = new WindowAndroid(env, obj, sdk_display_id); + int sdk_display_id, + float scroll_factor) { + WindowAndroid* window = + new WindowAndroid(env, obj, sdk_display_id, scroll_factor); return reinterpret_cast<intptr_t>(window); } diff --git a/chromium/ui/android/window_android.h b/chromium/ui/android/window_android.h index 35d3b7aa6be..91f8c079ac6 100644 --- a/chromium/ui/android/window_android.h +++ b/chromium/ui/android/window_android.h @@ -31,6 +31,8 @@ class BeginFrameSource; namespace ui { +extern const float kDefaultMouseWheelTickMultiplier; + class WindowAndroidCompositor; class WindowAndroidObserver; @@ -41,7 +43,7 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid { static WindowAndroid* FromJavaWindowAndroid( const base::android::JavaParamRef<jobject>& jwindow_android); - WindowAndroid(JNIEnv* env, jobject obj, int display_id); + WindowAndroid(JNIEnv* env, jobject obj, int display_id, float scroll_factor); ~WindowAndroid() override; @@ -86,6 +88,8 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid { // Return whether the specified Android permission can be requested by Chrome. bool CanRequestPermission(const std::string& permission); + float mouse_wheel_scroll_factor() const { return mouse_wheel_scroll_factor_; } + static WindowAndroid* CreateForTesting(); // Return the window token for this window, if one exists. @@ -114,6 +118,7 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid { std::unique_ptr<WindowBeginFrameSource> begin_frame_source_; bool needs_begin_frames_; std::list<base::Closure> vsync_complete_callbacks_; + float mouse_wheel_scroll_factor_; DISALLOW_COPY_AND_ASSIGN(WindowAndroid); }; diff --git a/chromium/ui/android/window_android_compositor.h b/chromium/ui/android/window_android_compositor.h index 7446d2a4932..740e69e8486 100644 --- a/chromium/ui/android/window_android_compositor.h +++ b/chromium/ui/android/window_android_compositor.h @@ -7,15 +7,12 @@ #include <memory> +#include "base/memory/weak_ptr.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/surfaces/frame_sink_id.h" #include "ui/android/ui_android_export.h" #include "ui/compositor/compositor_lock.h" -namespace cc { -class Layer; -} - namespace ui { class ResourceManager; @@ -25,7 +22,9 @@ class UI_ANDROID_EXPORT WindowAndroidCompositor { public: virtual ~WindowAndroidCompositor() {} - virtual void AttachLayerForReadback(scoped_refptr<cc::Layer> layer) = 0; + virtual base::WeakPtr<WindowAndroidCompositor> GetWeakPtr() = 0; + virtual void IncrementReadbackRequestCount() = 0; + virtual void DecrementReadbackRequestCount() = 0; virtual void RequestCopyOfOutputOnRootLayer( std::unique_ptr<viz::CopyOutputRequest> request) = 0; virtual void SetNeedsAnimate() = 0; diff --git a/chromium/ui/app_list/BUILD.gn b/chromium/ui/app_list/BUILD.gn index 56706f28bd1..0189982a58f 100644 --- a/chromium/ui/app_list/BUILD.gn +++ b/chromium/ui/app_list/BUILD.gn @@ -9,6 +9,8 @@ assert(is_chromeos) component("app_list") { sources = [ + "answer_card_contents_registry.cc", + "answer_card_contents_registry.h", "app_list_constants.cc", "app_list_constants.h", "app_list_export.h", @@ -21,23 +23,13 @@ component("app_list") { "app_list_util.cc", "app_list_util.h", "app_list_view_delegate.h", + "assistant_interaction_model.h", + "assistant_interaction_model_observer.h", "pagination_controller.cc", "pagination_controller.h", "pagination_model.cc", "pagination_model.h", "pagination_model_observer.h", - "search/dictionary_data_store.cc", - "search/dictionary_data_store.h", - "search/history.cc", - "search/history.h", - "search/history_data.cc", - "search/history_data.h", - "search/history_data_observer.h", - "search/history_data_store.cc", - "search/history_data_store.h", - "search/history_types.h", - "search_provider.cc", - "search_provider.h", "views/app_list_drag_and_drop_host.h", "views/app_list_folder_view.cc", "views/app_list_folder_view.h", @@ -54,6 +46,10 @@ component("app_list") { "views/apps_grid_view.cc", "views/apps_grid_view.h", "views/apps_grid_view_folder_delegate.h", + "views/assistant_bubble_view.cc", + "views/assistant_bubble_view.h", + "views/assistant_container_view.cc", + "views/assistant_container_view.h", "views/contents_view.cc", "views/contents_view.h", "views/expand_arrow_view.cc", @@ -63,6 +59,10 @@ component("app_list") { "views/folder_header_view.cc", "views/folder_header_view.h", "views/folder_header_view_delegate.h", + "views/horizontal_page.cc", + "views/horizontal_page.h", + "views/horizontal_page_container.cc", + "views/horizontal_page_container.h", "views/image_shadow_animator.cc", "views/image_shadow_animator.h", "views/indicator_chip_view.cc", @@ -92,6 +92,8 @@ component("app_list") { "views/search_result_tile_item_view.h", "views/search_result_view.cc", "views/search_result_view.h", + "views/suggestion_chip_view.cc", + "views/suggestion_chip_view.h", "views/suggestions_container_view.cc", "views/suggestions_container_view.h", "views/top_icon_animation_view.cc", @@ -105,9 +107,9 @@ component("app_list") { "//base:i18n", "//base/third_party/dynamic_annotations", "//cc/paint", + "//chromeos:chromeos", "//components/keyed_service/core", "//components/sync", - "//components/wallpaper", "//mojo/public/cpp/bindings", "//services/ui/public/cpp", "//services/ui/public/interfaces", @@ -129,6 +131,7 @@ component("app_list") { "//ui/resources", "//ui/strings", "//ui/views", + "//ui/views/mus/remote_view:remote_view_host", "//ui/wm", ] @@ -190,15 +193,8 @@ executable("app_list_demo") { test("app_list_unittests") { sources = [ - "app_list_item_list_unittest.cc", - "app_list_model_unittest.cc", "folder_image_unittest.cc", "pagination_model_unittest.cc", - "search/history_data_store_unittest.cc", - "search/term_break_iterator_unittest.cc", - "search/tokenized_string_char_iterator_unittest.cc", - "search/tokenized_string_match_unittest.cc", - "search/tokenized_string_unittest.cc", "test/run_all_unittests.cc", "views/app_list_main_view_unittest.cc", "views/app_list_view_unittest.cc", @@ -219,7 +215,7 @@ test("app_list_unittests") { ":test_support", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gtest", "//ui/accessibility", diff --git a/chromium/ui/app_list/presenter/BUILD.gn b/chromium/ui/app_list/presenter/BUILD.gn deleted file mode 100644 index 82d6dae01c9..00000000000 --- a/chromium/ui/app_list/presenter/BUILD.gn +++ /dev/null @@ -1,114 +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("//build/config/ui.gni") -import("//mojo/public/tools/bindings/mojom.gni") -import("//testing/test.gni") - -assert(use_aura) - -mojom("mojom") { - sources = [ - "app_list_presenter.mojom", - ] - deps = [ - "//services/ui/public/interfaces", - ] - - component_output_prefix = "app_list_presenter_mojom" - export_class_attribute = "APP_LIST_PRESENTER_EXPORT" - export_define = "APP_LIST_PRESENTER_IMPLEMENTATION=1" - export_header = "ui/app_list/presenter/app_list_presenter_export.h" -} - -component("presenter") { - sources = [ - "app_list.cc", - "app_list.h", - "app_list_delegate.h", - "app_list_presenter_delegate.cc", - "app_list_presenter_delegate.h", - "app_list_presenter_delegate_factory.h", - "app_list_presenter_export.h", - "app_list_presenter_impl.cc", - "app_list_presenter_impl.h", - "app_list_view_delegate_factory.cc", - "app_list_view_delegate_factory.h", - ] - - defines = [ "APP_LIST_PRESENTER_IMPLEMENTATION" ] - - public_deps = [ - ":mojom", - "//base", - "//mojo/public/cpp/bindings", - "//ui/app_list", - "//ui/aura", - "//ui/compositor", - "//ui/gfx/geometry", - "//ui/views", - - # Temporary dependency to fix compile flake in http://crbug.com/611898. - # TODO(tapted): Remove once http://crbug.com/612382 is fixed. - "//ui/accessibility:ax_enums_mojo", - ] -} - -static_library("test_support") { - sources = [ - "test/app_list_presenter_impl_test_api.cc", - "test/app_list_presenter_impl_test_api.h", - "test/test_app_list_presenter.cc", - "test/test_app_list_presenter.h", - "test/test_app_list_view_delegate_factory.cc", - "test/test_app_list_view_delegate_factory.h", - - # Temporary dependency to fix compile flake in http://crbug.com/611898. - # TODO(tapted): Remove once http://crbug.com/612382 is fixed. - "//ui/accessibility:ax_enums_mojo", - ] - - public_deps = [ - ":mojom", - ":presenter", - ] - deps = [ - "//base", - "//mojo/public/cpp/bindings", - "//ui/app_list:test_support", - ] -} - -test("app_list_presenter_unittests") { - sources = [ - "app_list_presenter_impl_unittest.cc", - "test/run_all_unittests.cc", - ] - - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] - - deps = [ - ":presenter", - ":test_support", - "//base", - "//base/test:test_support", - "//mojo/edk/system", - "//testing/gtest", - "//ui/app_list:test_support", - "//ui/aura:aura", - "//ui/aura:test_support", - "//ui/base", - "//ui/gl:test_support", - "//ui/views:test_support", - "//ui/wm:wm", - - # Temporary dependency to fix compile flake in http://crbug.com/611898. - # TODO(tapted): Remove once http://crbug.com/612382 is fixed. - "//ui/accessibility:ax_enums_mojo", - ] - - data_deps = [ - "//ui/resources:ui_test_pak_data", - ] -} diff --git a/chromium/ui/app_list/presenter/app_list_presenter.mojom b/chromium/ui/app_list/presenter/app_list_presenter.mojom deleted file mode 100644 index 2b54979b6b2..00000000000 --- a/chromium/ui/app_list/presenter/app_list_presenter.mojom +++ /dev/null @@ -1,71 +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 app_list.mojom; - -import "services/ui/public/interfaces/window_manager_constants.mojom"; - -// TODO(msw): Rename this file to app_list.mojom; move to ash? -// TODO(msw): Ash should implement the app list and presenter; chrome should -// just push data about its apps into the app list interface. - -// Matches app_list::AppListView:AppListState. -enum AppListState { - // Closes |app_list_main_view_| and dismisses the delegate. - CLOSED = 0, - // The initial state for the app list when neither maximize or side shelf - // modes are active. If set, the widget will peek over the shelf by - // kPeekingAppListHeight DIPs. - PEEKING = 1, - // Entered when text is entered into the search box from peeking mode. - HALF = 2, - // Default app list state in maximize and side shelf modes. Entered from an - // upward swipe from |PEEKING| or from clicking the chevron. - FULLSCREEN_ALL_APPS = 3, - // Entered from an upward swipe from |HALF| or by entering text in the - // search box from |FULLSCREEN_ALL_APPS|. - FULLSCREEN_SEARCH = 4, -}; - -// Implemented by ash. Used by chrome to set the presenter interface. -interface AppList { - // Set the app list presenter interface, to let ash trigger Chrome's app list. - SetAppListPresenter(AppListPresenter presenter); - - // Notify the app list that the presenter's [target] visibility changed. - OnTargetVisibilityChanged(bool visible); - - // |display_id| gives the display containing the app list. - OnVisibilityChanged(bool visible, int64 display_id); -}; - -// Implemented by chrome. Used by ash to actually show and dismiss the app list. -interface AppListPresenter { - // Show the app list on the specified display. - Show(int64 display_id); - - // Dismiss the app list. - Dismiss(); - - // Show the app list (on the specified display) if it is hidden; hide the - // app list if it is shown. - ToggleAppList(int64 display_id); - - // Starts a voice interaction session. - StartVoiceInteractionSession(); - - // Starts or stops a voice interaction session based on the current state. - ToggleVoiceInteractionSession(); - - // Updates y position and opacity of app list. - UpdateYPositionAndOpacity(int32 y_position_in_screen, - float background_opacity); - - // Ends the drag of app list from shelf. - EndDragFromShelf(AppListState app_list_state); - - // Passes MouseWheelEvents from the Shelf to the AppListView. - ProcessMouseWheelOffset(int32 y_scroll_offset); - -}; diff --git a/chromium/ui/app_list/vector_icons/BUILD.gn b/chromium/ui/app_list/vector_icons/BUILD.gn index c9c66f798ea..f667bec6fa8 100644 --- a/chromium/ui/app_list/vector_icons/BUILD.gn +++ b/chromium/ui/app_list/vector_icons/BUILD.gn @@ -8,33 +8,18 @@ aggregate_vector_icons("app_list_vector_icons") { icon_directory = "." icons = [ - "ic_arrow_up.1x.icon", "ic_arrow_up.icon", - "ic_badge_instant.1x.icon", "ic_badge_instant.icon", - "ic_badge_play.1x.icon", "ic_badge_play.icon", - "ic_badge_rating.1x.icon", "ic_badge_rating.icon", - "ic_bookmark.1x.icon", "ic_bookmark.icon", - "ic_close.1x.icon", - "ic_close.icon", - "ic_domain.1x.icon", "ic_domain.icon", - "ic_equal.1x.icon", "ic_equal.icon", - "ic_google_black.1x.icon", "ic_google_black.icon", - "ic_google_color.1x.icon", "ic_google_color.icon", - "ic_history.1x.icon", "ic_history.icon", - "ic_mic_black.1x.icon", "ic_mic_black.icon", - "ic_search.1x.icon", "ic_search.icon", - "ic_search_engine_not_google.1x.icon", "ic_search_engine_not_google.icon", ] } diff --git a/chromium/ui/arc/BUILD.gn b/chromium/ui/arc/BUILD.gn index 40b7f36cf0b..ecc44786636 100644 --- a/chromium/ui/arc/BUILD.gn +++ b/chromium/ui/arc/BUILD.gn @@ -34,7 +34,7 @@ static_library("arc") { "//components/exo", "//components/keyed_service/content:content", "//components/signin/core/account_id", - "//mojo/common:common_base", + "//mojo/public/cpp/system", "//skia", "//ui/accessibility", "//ui/aura", @@ -52,6 +52,19 @@ static_library("arc") { ] } +static_library("test_support") { + testonly = true + sources = [ + "notification/mock_arc_notification_item.cc", + "notification/mock_arc_notification_item.h", + ] + + deps = [ + ":arc", + "//ui/gl:test_support", + ] +} + test("ui_arc_unittests") { testonly = true sources = [ @@ -63,13 +76,14 @@ test("ui_arc_unittests") { deps = [ ":arc", + ":test_support", "//ash:test_support_without_content", "//base", "//base/test:test_support", "//components/arc:arc_test_support", "//components/exo", "//components/exo:test_support", - "//mojo/edk/system", + "//mojo/edk", "//testing/gmock", "//testing/gtest", "//ui/aura:test_support", diff --git a/chromium/ui/arc/OWNERS b/chromium/ui/arc/OWNERS index d7d81e7a131..853205b1bd1 100644 --- a/chromium/ui/arc/OWNERS +++ b/chromium/ui/arc/OWNERS @@ -1,3 +1,7 @@ +# This is for the common case of adding or renaming files. If you're doing +# structural changes, use usual OWNERS rules. +per-file BUILD.gn=* + elijahtaylor@chromium.org hidehiko@chromium.org lhchavez@chromium.org diff --git a/chromium/ui/arc/notification/arc_notification_content_view.cc b/chromium/ui/arc/notification/arc_notification_content_view.cc index baecc8484c1..9a3ad45d84e 100644 --- a/chromium/ui/arc/notification/arc_notification_content_view.cc +++ b/chromium/ui/arc/notification/arc_notification_content_view.cc @@ -6,7 +6,6 @@ #include "ash/wm/window_util.h" #include "base/auto_reset.h" -#include "base/memory/ptr_util.h" #include "components/exo/notification_surface.h" #include "components/exo/surface.h" #include "ui/accessibility/ax_node_data.h" @@ -18,8 +17,9 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/transform.h" +#include "ui/message_center/message_center.h" #include "ui/message_center/public/cpp/message_center_constants.h" -#include "ui/message_center/views/notification_control_buttons_view.h" +#include "ui/message_center/public/cpp/notification.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/widget/root_view.h" @@ -231,50 +231,19 @@ class ArcNotificationContentView::SlideHelper DISALLOW_COPY_AND_ASSIGN(SlideHelper); }; -class ArcNotificationContentView::ContentViewDelegate - : public ArcNotificationContentViewDelegate { - public: - explicit ContentViewDelegate(ArcNotificationContentView* owner) - : owner_(owner) {} - - void UpdateControlButtonsVisibility() override { - owner_->UpdateControlButtonsVisibility(); - } - - void OnSlideChanged() override { - if (owner_->slide_helper_) - owner_->slide_helper_->Update(); - } - - message_center::NotificationControlButtonsView* GetControlButtonsView() - const override { - return owner_->control_buttons_view_; - } - - void OnContainerAnimationStarted() override { - owner_->OnContainerAnimationStarted(); - } - - void OnContainerAnimationEnded() override { - owner_->OnContainerAnimationEnded(); - } - - private: - ArcNotificationContentView* const owner_; - - DISALLOW_COPY_AND_ASSIGN(ContentViewDelegate); -}; - // static, for ArcNotificationContentView::GetClassName(). const char ArcNotificationContentView::kViewClassName[] = "ArcNotificationContentView"; ArcNotificationContentView::ArcNotificationContentView( - ArcNotificationItem* item) + ArcNotificationItem* item, + const message_center::Notification& notification, + message_center::MessageView* message_view) : item_(item), notification_key_(item->GetNotificationKey()), event_forwarder_(new EventForwarder(this)), - mouse_enter_exit_handler_(new MouseEnterExitHandler(this)) { + mouse_enter_exit_handler_(new MouseEnterExitHandler(this)), + control_buttons_view_(message_view) { // kNotificationWidth must be 360, since this value is separately defiend in // ArcNotificationWrapperView class in Android side. DCHECK_EQ(360, message_center::kNotificationWidth); @@ -294,10 +263,17 @@ ArcNotificationContentView::ArcNotificationContentView( OnNotificationSurfaceAdded(surface); } + // Creates the control_buttons_view_, which collects all control buttons into + // a horizontal box. + control_buttons_view_.set_owned_by_client(); + control_buttons_view_.SetBackgroundColor( + GetControlButtonBackgroundColor(item_->GetShownContents())); + + Update(message_view, notification); + // Create a layer as an anchor to insert surface copy during a slide. SetPaintToLayer(); UpdatePreferredSize(); - UpdateAccessibleName(); } ArcNotificationContentView::~ArcNotificationContentView() { @@ -316,10 +292,66 @@ const char* ArcNotificationContentView::GetClassName() const { return kViewClassName; } -std::unique_ptr<ArcNotificationContentViewDelegate> -ArcNotificationContentView::CreateContentViewDelegate() { - return std::make_unique<ArcNotificationContentView::ContentViewDelegate>( - this); +void ArcNotificationContentView::Update( + message_center::MessageView* message_view, + const message_center::Notification& notification) { + control_buttons_view_.ShowSettingsButton( + notification.should_show_settings_button()); + control_buttons_view_.ShowCloseButton(!message_view->GetPinned()); + control_buttons_view_.SetBackgroundColor( + GetControlButtonBackgroundColor(item_->GetShownContents())); + UpdateControlButtonsVisibility(); + + accessible_name_ = notification.accessible_name(); + UpdateSnapshot(); +} + +message_center::NotificationControlButtonsView* +ArcNotificationContentView::GetControlButtonsView() { + // |control_buttons_view_| is hosted in |floating_control_buttons_widget_| and + // should not be used when there is no |floating_control_buttons_widget_|. + return floating_control_buttons_widget_ ? &control_buttons_view_ : nullptr; +} + +void ArcNotificationContentView::UpdateControlButtonsVisibility() { + if (!control_buttons_view_.parent()) + return; + + // If the visibility change is ongoing, skip this method to prevent an + // infinite loop. + if (updating_control_buttons_visibility_) + return; + + DCHECK(floating_control_buttons_widget_); + + const bool target_visiblity = + IsMouseHovered() || (control_buttons_view_.IsCloseButtonFocused()) || + (control_buttons_view_.IsSettingsButtonFocused()); + + if (target_visiblity == floating_control_buttons_widget_->IsVisible()) + return; + + // Add the guard to prevent an infinite loop. Changing visibility may generate + // an event and it may call thie method again. + base::AutoReset<bool> reset(&updating_control_buttons_visibility_, true); + + if (target_visiblity) + floating_control_buttons_widget_->Show(); + else + floating_control_buttons_widget_->Hide(); +} + +void ArcNotificationContentView::OnSlideChanged() { + if (slide_helper_) + slide_helper_->Update(); +} + +void ArcNotificationContentView::OnContainerAnimationStarted() { + ShowCopiedSurface(); +} + +void ArcNotificationContentView::OnContainerAnimationEnded() { + HideCopiedSurface(); } void ArcNotificationContentView::MaybeCreateFloatingControlButtons() { @@ -330,22 +362,9 @@ void ArcNotificationContentView::MaybeCreateFloatingControlButtons() { if (!surface_ || !GetWidget() || !item_) return; - DCHECK(!control_buttons_view_); + DCHECK(!control_buttons_view_.parent()); DCHECK(!floating_control_buttons_widget_); - CHECK_EQ(ArcNotificationView::kViewClassName, parent()->GetClassName()); - auto* notification_view = static_cast<ArcNotificationView*>(parent()); - - // Creates the control_buttons_view_, which collects all control buttons into - // a horizontal box. - control_buttons_view_ = - new message_center::NotificationControlButtonsView(notification_view); - control_buttons_view_->SetBackgroundColor( - GetControlButtonBackgroundColor(item_->GetShownContents())); - control_buttons_view_->ShowSettingsButton( - item_->IsOpeningSettingsSupported()); - control_buttons_view_->ShowCloseButton(!notification_view->GetPinned()); - views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; @@ -353,7 +372,7 @@ void ArcNotificationContentView::MaybeCreateFloatingControlButtons() { floating_control_buttons_widget_.reset(new views::Widget); floating_control_buttons_widget_->Init(params); - floating_control_buttons_widget_->SetContentsView(control_buttons_view_); + floating_control_buttons_widget_->SetContentsView(&control_buttons_view_); floating_control_buttons_widget_->GetNativeWindow()->AddPreTargetHandler( mouse_enter_exit_handler_.get()); @@ -369,9 +388,6 @@ void ArcNotificationContentView::SetSurface(ArcNotificationSurface* surface) { if (surface_ == surface) return; - // Put null to |control_buttos|view_| before deleting the widget, since it may - // be referred while deletion. - control_buttons_view_ = nullptr; // Reset |floating_control_buttons_widget_| when |surface_| is changed. floating_control_buttons_widget_.reset(); @@ -428,34 +444,6 @@ void ArcNotificationContentView::UpdatePreferredSize() { SetPreferredSize(preferred_size); } -void ArcNotificationContentView::UpdateControlButtonsVisibility() { - if (!control_buttons_view_) - return; - - // If the visibility change is ongoing, skip this method to prevent an - // infinite loop. - if (updating_control_buttons_visibility_) - return; - - DCHECK(floating_control_buttons_widget_); - - const bool target_visiblity = - IsMouseHovered() || (control_buttons_view_->IsCloseButtonFocused()) || - (control_buttons_view_->IsSettingsButtonFocused()); - - if (target_visiblity == floating_control_buttons_widget_->IsVisible()) - return; - - // Add the guard to prevent an infinite loop. Changing visibility may generate - // an event and it may call thie method again. - base::AutoReset<bool> reset(&updating_control_buttons_visibility_, true); - - if (target_visiblity) - floating_control_buttons_widget_->Show(); - else - floating_control_buttons_widget_->Hide(); -} - void ArcNotificationContentView::UpdateSnapshot() { // Bail if we have a |surface_| because it controls the sizes and paints UI. if (surface_) @@ -489,22 +477,6 @@ void ArcNotificationContentView::AttachSurface() { MaybeCreateFloatingControlButtons(); } -void ArcNotificationContentView::UpdateAccessibleName() { - // Don't update the accessible name when we are about to be destroyed. - if (!item_) - return; - - accessible_name_ = item_->GetAccessibleName(); -} - -void ArcNotificationContentView::OnContainerAnimationStarted() { - ShowCopiedSurface(); -} - -void ArcNotificationContentView::OnContainerAnimationEnded() { - HideCopiedSurface(); -} - void ArcNotificationContentView::ShowCopiedSurface() { if (!surface_) return; @@ -576,19 +548,17 @@ void ArcNotificationContentView::Layout() { // be positioned without the need to consider the transform. surface_->GetContentWindow()->SetTransform(transform); - if (control_buttons_view_) { - DCHECK(floating_control_buttons_widget_); + if (floating_control_buttons_widget_) { gfx::Rect control_buttons_bounds(contents_bounds); - int buttons_width = control_buttons_view_->GetPreferredSize().width(); - int buttons_height = control_buttons_view_->GetPreferredSize().height(); + const gfx::Size button_size = control_buttons_view_.GetPreferredSize(); control_buttons_bounds.set_x(control_buttons_bounds.right() - - buttons_width - + button_size.width() - message_center::kControlButtonPadding); control_buttons_bounds.set_y(control_buttons_bounds.y() + message_center::kControlButtonPadding); - control_buttons_bounds.set_width(buttons_width); - control_buttons_bounds.set_height(buttons_height); + control_buttons_bounds.set_width(button_size.width()); + control_buttons_bounds.set_height(button_size.height()); floating_control_buttons_widget_->SetBounds(control_buttons_bounds); } @@ -625,10 +595,14 @@ void ArcNotificationContentView::OnMouseExited(const ui::MouseEvent&) { } void ArcNotificationContentView::OnFocus() { - CHECK_EQ(ArcNotificationView::kViewClassName, parent()->GetClassName()); + CHECK_EQ(message_center::MessageView::kViewClassName, + parent()->GetClassName()); + auto* notification_view = static_cast<ArcNotificationView*>(parent()); + CHECK_EQ(ArcNotificationView::kMessageViewSubClassName, + notification_view->GetMessageViewSubClassName()); NativeViewHost::OnFocus(); - static_cast<ArcNotificationView*>(parent())->OnContentFocused(); + notification_view->OnContentFocused(); if (surface_ && surface_->GetAXTreeId() != -1) Activate(); @@ -640,10 +614,14 @@ void ArcNotificationContentView::OnBlur() { return; } - CHECK_EQ(ArcNotificationView::kViewClassName, parent()->GetClassName()); + CHECK_EQ(message_center::MessageView::kViewClassName, + parent()->GetClassName()); + auto* notification_view = static_cast<ArcNotificationView*>(parent()); + CHECK_EQ(ArcNotificationView::kMessageViewSubClassName, + notification_view->GetMessageViewSubClassName()); NativeViewHost::OnBlur(); - static_cast<ArcNotificationView*>(parent())->OnContentBlured(); + notification_view->OnContentBlured(); } void ArcNotificationContentView::Activate() { @@ -683,6 +661,19 @@ void ArcNotificationContentView::GetAccessibleNodeData( node_data->SetName(accessible_name_); } +void ArcNotificationContentView::OnAccessibilityEvent(ax::mojom::Event event) { + if (event == ax::mojom::Event::kTextSelectionChanged) { + // Activate and request focus on notification content view. If text + // selection changed event is dispatched, it indicates that user is going to + // type something inside Android notification. Widget of message center is + // not activated by default. We need to activate the widget. If other view + // in message center has focus, it can consume key event. We need to request + // focus to move it to this content view. + Activate(); + RequestFocus(); + } +} + void ArcNotificationContentView::OnWindowBoundsChanged( aura::Window* window, const gfx::Rect& old_bounds, @@ -708,16 +699,6 @@ void ArcNotificationContentView::OnItemDestroying() { SetSurface(nullptr); } -void ArcNotificationContentView::OnItemUpdated() { - UpdateAccessibleName(); - UpdateSnapshot(); - if (control_buttons_view_) { - DCHECK(floating_control_buttons_widget_); - control_buttons_view_->SetBackgroundColor( - GetControlButtonBackgroundColor(item_->GetShownContents())); - } -} - void ArcNotificationContentView::OnNotificationSurfaceAdded( ArcNotificationSurface* surface) { if (surface->GetNotificationKey() != notification_key_) diff --git a/chromium/ui/arc/notification/arc_notification_content_view.h b/chromium/ui/arc/notification/arc_notification_content_view.h index fe405a0c4dd..2d4fdf39aba 100644 --- a/chromium/ui/arc/notification/arc_notification_content_view.h +++ b/chromium/ui/arc/notification/arc_notification_content_view.h @@ -9,13 +9,14 @@ #include <string> #include "base/macros.h" -#include "ui/arc/notification/arc_notification_content_view_delegate.h" #include "ui/arc/notification/arc_notification_item.h" #include "ui/arc/notification/arc_notification_surface_manager.h" #include "ui/aura/window_observer.h" +#include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/views/controls/native/native_view_host.h" namespace message_center { +class Notification; class NotificationControlButtonsView; } @@ -42,19 +43,25 @@ class ArcNotificationContentView public: static const char kViewClassName[]; - explicit ArcNotificationContentView(ArcNotificationItem* item); + ArcNotificationContentView(ArcNotificationItem* item, + const message_center::Notification& notification, + message_center::MessageView* message_view); ~ArcNotificationContentView() override; // views::View overrides: const char* GetClassName() const override; - std::unique_ptr<ArcNotificationContentViewDelegate> - CreateContentViewDelegate(); + void Update(message_center::MessageView* message_view, + const message_center::Notification& notification); + message_center::NotificationControlButtonsView* GetControlButtonsView(); + void UpdateControlButtonsVisibility(); + void OnSlideChanged(); + void OnContainerAnimationStarted(); + void OnContainerAnimationEnded(); private: friend class ArcNotificationContentViewTest; - class ContentViewDelegate; class EventForwarder; class MouseEnterExitHandler; class SettingsButton; @@ -65,17 +72,13 @@ class ArcNotificationContentView void MaybeCreateFloatingControlButtons(); void SetSurface(ArcNotificationSurface* surface); void UpdatePreferredSize(); - void UpdateControlButtonsVisibility(); void UpdateSnapshot(); void AttachSurface(); void Activate(); - void UpdateAccessibleName(); void SetExpanded(bool expanded); bool IsExpanded() const; void SetManuallyExpandedOrCollapsed(bool value); bool IsManuallyExpandedOrCollapsed() const; - void OnContainerAnimationStarted(); - void OnContainerAnimationEnded(); void ShowCopiedSurface(); void HideCopiedSurface(); @@ -91,6 +94,7 @@ class ArcNotificationContentView void OnBlur() override; views::FocusTraversable* GetFocusTraversable() override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + void OnAccessibilityEvent(ax::mojom::Event event) override; // aura::WindowObserver void OnWindowBoundsChanged(aura::Window* window, @@ -101,7 +105,6 @@ class ArcNotificationContentView // ArcNotificationItem::Observer void OnItemDestroying() override; - void OnItemUpdated() override; // ArcNotificationSurfaceManager::Observer: void OnNotificationSurfaceAdded(ArcNotificationSurface* surface) override; @@ -138,8 +141,8 @@ class ArcNotificationContentView // it. std::unique_ptr<views::Widget> floating_control_buttons_widget_; - message_center::NotificationControlButtonsView* control_buttons_view_ = - nullptr; + // This view is owned by client (this). + message_center::NotificationControlButtonsView control_buttons_view_; // Protects from call loops between Layout and OnWindowBoundsChanged. bool in_layout_ = false; diff --git a/chromium/ui/arc/notification/arc_notification_content_view_delegate.h b/chromium/ui/arc/notification/arc_notification_content_view_delegate.h deleted file mode 100644 index 2d8e4d04ffc..00000000000 --- a/chromium/ui/arc/notification/arc_notification_content_view_delegate.h +++ /dev/null @@ -1,30 +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_ARC_NOTIFICATION_ARC_NOTIFICATION_CONTENT_VIEW_DELEGATE_H_ -#define UI_ARC_NOTIFICATION_ARC_NOTIFICATION_CONTENT_VIEW_DELEGATE_H_ - -namespace message_center { -class NotificationControlButtonsView; -} - -namespace arc { - -// Delegate for a view that is hosted by CustomNotificationView. -// TODO(yoshiki): Remove this delegate and call code in the content view -// directly without delegate. -class ArcNotificationContentViewDelegate { - public: - virtual ~ArcNotificationContentViewDelegate() = default; - virtual void UpdateControlButtonsVisibility() = 0; - virtual void OnSlideChanged() = 0; - virtual message_center::NotificationControlButtonsView* - GetControlButtonsView() const = 0; - virtual void OnContainerAnimationStarted() = 0; - virtual void OnContainerAnimationEnded() = 0; -}; - -} // namespace arc - -#endif // UI_ARC_NOTIFICATION_ARC_NOTIFICATION_CONTENT_VIEW_DELEGATE_H_ diff --git a/chromium/ui/arc/notification/arc_notification_content_view_unittest.cc b/chromium/ui/arc/notification/arc_notification_content_view_unittest.cc index d69a751faf4..2a2fb6f029c 100644 --- a/chromium/ui/arc/notification/arc_notification_content_view_unittest.cc +++ b/chromium/ui/arc/notification/arc_notification_content_view_unittest.cc @@ -8,10 +8,15 @@ #include <string> #include <utility> +#include "ash/message_center/message_center_view.h" #include "ash/shell.h" +#include "ash/system/status_area_widget.h" +#include "ash/system/status_area_widget_test_helper.h" +#include "ash/system/web_notification/web_notification_tray.h" #include "ash/test/ash_test_base.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/bind_test_util.h" #include "components/exo/buffer.h" #include "components/exo/keyboard.h" #include "components/exo/keyboard_delegate.h" @@ -28,6 +33,7 @@ #include "ui/arc/notification/arc_notification_surface.h" #include "ui/arc/notification/arc_notification_surface_manager_impl.h" #include "ui/arc/notification/arc_notification_view.h" +#include "ui/arc/notification/mock_arc_notification_item.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" #include "ui/events/keycodes/dom/dom_code.h" @@ -45,7 +51,6 @@ namespace arc { namespace { -constexpr char kNotificationIdPrefix[] = "ARC_NOTIFICATION_"; constexpr gfx::Rect kNotificationSurfaceBounds(100, 100, 300, 300); class MockKeyboardDelegate : public exo::KeyboardDelegate { @@ -69,63 +74,6 @@ aura::Window* GetFocusedWindow() { } // anonymous namespace -class MockArcNotificationItem : public ArcNotificationItem { - public: - MockArcNotificationItem(const std::string& notification_key) - : notification_key_(notification_key), - notification_id_(kNotificationIdPrefix + notification_key), - weak_factory_(this) {} - - // Methods for testing. - size_t count_close() { return count_close_; } - base::WeakPtr<MockArcNotificationItem> GetWeakPtr() { - return weak_factory_.GetWeakPtr(); - } - - // Overriding methods for testing. - void Close(bool by_user) override { count_close_++; } - const gfx::ImageSkia& GetSnapshot() const override { return snapshot_; } - const std::string& GetNotificationKey() const override { - return notification_key_; - } - const std::string& GetNotificationId() const override { - return notification_id_; - } - - // Overriding methods for returning dummy data or doing nothing. - void OnClosedFromAndroid() override {} - void Click() override {} - void ToggleExpansion() override {} - void OpenSettings() override {} - void AddObserver(Observer* observer) override {} - void RemoveObserver(Observer* observer) override {} - void IncrementWindowRefCount() override {} - void DecrementWindowRefCount() override {} - bool IsOpeningSettingsSupported() const override { return true; } - mojom::ArcNotificationExpandState GetExpandState() const override { - return mojom::ArcNotificationExpandState::FIXED_SIZE; - } - mojom::ArcNotificationShownContents GetShownContents() const override { - return mojom::ArcNotificationShownContents::CONTENTS_SHOWN; - } - gfx::Rect GetSwipeInputRect() const override { return gfx::Rect(); } - const base::string16& GetAccessibleName() const override { - return base::EmptyString16(); - }; - void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data) override {} - bool IsManuallyExpandedOrCollapsed() const override { return false; } - - private: - std::string notification_key_; - std::string notification_id_; - gfx::ImageSkia snapshot_; - size_t count_close_ = 0; - - base::WeakPtrFactory<MockArcNotificationItem> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(MockArcNotificationItem); -}; - class DummyEvent : public ui::Event { public: DummyEvent() : Event(ui::ET_UNKNOWN, base::TimeTicks(), 0) {} @@ -140,6 +88,8 @@ class ArcNotificationContentViewTest : public ash::AshTestBase { void SetUp() override { ash::AshTestBase::SetUp(); + ash::MessageCenterView::disable_animation_for_testing = true; + wm_helper_ = std::make_unique<exo::WMHelper>(); exo::WMHelper::SetInstance(wm_helper_.get()); DCHECK(exo::WMHelper::HasInstance()); @@ -167,13 +117,14 @@ class ArcNotificationContentViewTest : public ash::AshTestBase { ash::AshTestBase::TearDown(); } - void PressCloseButton() { + void PressCloseButton(ArcNotificationView* notification_view) { DummyEvent dummy_event; auto* control_buttons_view = - GetArcNotificationContentView()->control_buttons_view_; + ¬ification_view->content_view_->control_buttons_view_; ASSERT_TRUE(control_buttons_view); views::Button* close_button = control_buttons_view->close_button(); ASSERT_NE(nullptr, close_button); + close_button->RequestFocus(); control_buttons_view->ButtonPressed(close_button, dummy_event); } @@ -193,8 +144,8 @@ class ArcNotificationContentViewTest : public ash::AshTestBase { static_cast<ArcNotificationView*>( message_center::MessageViewFactory::Create(notification, true))); notification_view->set_owned_by_client(); - views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); + views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.context = ash::Shell::GetPrimaryRootWindow(); auto wrapper_widget = std::make_unique<views::Widget>(); @@ -228,6 +179,9 @@ class ArcNotificationContentViewTest : public ash::AshTestBase { } Notification CreateNotification(MockArcNotificationItem* notification_item) { + message_center::RichNotificationData optional_fields; + optional_fields.settings_button_handler = + message_center::SettingsButtonHandler::DELEGATE; return Notification( message_center::NOTIFICATION_TYPE_CUSTOM, notification_item->GetNotificationId(), base::UTF8ToUTF16("title"), @@ -235,7 +189,7 @@ class ArcNotificationContentViewTest : public ash::AshTestBase { GURL(), message_center::NotifierId(message_center::NotifierId::ARC_APPLICATION, "ARC_NOTIFICATION"), - message_center::RichNotificationData(), + optional_fields, new ArcNotificationDelegate(notification_item->GetWeakPtr())); } @@ -249,8 +203,7 @@ class ArcNotificationContentViewTest : public ash::AshTestBase { message_center::NotificationControlButtonsView* GetControlButtonsView() const { DCHECK(GetArcNotificationContentView()); - DCHECK(GetArcNotificationContentView()->control_buttons_view_); - return GetArcNotificationContentView()->control_buttons_view_; + return &GetArcNotificationContentView()->control_buttons_view_; } views::Widget* GetControlButtonsWidget() const { DCHECK(GetControlButtonsView()->GetWidget()); @@ -258,9 +211,7 @@ class ArcNotificationContentViewTest : public ash::AshTestBase { } ArcNotificationContentView* GetArcNotificationContentView() const { - views::View* view = notification_view_->contents_view_; - EXPECT_EQ(ArcNotificationContentView::kViewClassName, view->GetClassName()); - return static_cast<ArcNotificationContentView*>(view); + return notification_view_->content_view_; } void ActivateArcNotification() { GetArcNotificationContentView()->Activate(); @@ -339,13 +290,68 @@ TEST_F(ArcNotificationContentViewTest, CloseButton) { EXPECT_TRUE(MessageCenter::Get()->FindVisibleNotificationById( notification_item->GetNotificationId())); - PressCloseButton(); + PressCloseButton(notification_view()); EXPECT_FALSE(MessageCenter::Get()->FindVisibleNotificationById( notification_item->GetNotificationId())); CloseNotificationView(); } +// Tests pressing close button when hosted in MessageCenterView. +TEST_F(ArcNotificationContentViewTest, CloseButtonInMessageCenterView) { + std::string notification_key("notification id"); + + // Override MessageView factory to capture the created notification view in + // |notification_view|. + ArcNotificationView* notification_view = nullptr; + message_center::MessageViewFactory::SetCustomNotificationViewFactory( + base::BindLambdaForTesting( + [¬ification_view](const message_center::Notification& notification) + -> std::unique_ptr<message_center::MessageView> { + auto* arc_delegate = + static_cast<ArcNotificationDelegate*>(notification.delegate()); + std::unique_ptr<message_center::MessageView> created_view = + arc_delegate->CreateCustomMessageView(notification); + notification_view = + static_cast<ArcNotificationView*>(created_view.get()); + return created_view; + })); + + // Show MessageCenterView and activate its widget. + auto* notification_tray = + ash::StatusAreaWidgetTestHelper::GetStatusAreaWidget() + ->web_notification_tray(); + notification_tray->ShowBubble(false /* show_by_click */); + notification_tray->GetBubbleView() + ->GetWidget() + ->widget_delegate() + ->set_can_activate(true); + notification_tray->GetBubbleView()->GetWidget()->Activate(); + + auto notification_item = + std::make_unique<MockArcNotificationItem>(notification_key); + PrepareSurface(notification_key); + Notification notification = CreateNotification(notification_item.get()); + + // Sets a close callback so that the underlying item is destroyed when close + // button is pressed. This simulates ArcNotificationItemImpl behavior. + notification_item->SetCloseCallback(base::BindLambdaForTesting( + [¬ification_item]() { notification_item.reset(); })); + + MessageCenter::Get()->AddNotification( + std::make_unique<Notification>(notification)); + ASSERT_TRUE(notification_view); + + // Cache notification id because |notification_item| will be gone when the + // close button is pressed. + const std::string notification_id = notification_item->GetNotificationId(); + EXPECT_TRUE( + MessageCenter::Get()->FindVisibleNotificationById(notification_id)); + PressCloseButton(notification_view); + EXPECT_FALSE( + MessageCenter::Get()->FindVisibleNotificationById(notification_id)); +} + TEST_F(ArcNotificationContentViewTest, ReuseSurfaceAfterClosing) { std::string notification_key("notification id"); diff --git a/chromium/ui/arc/notification/arc_notification_delegate.cc b/chromium/ui/arc/notification/arc_notification_delegate.cc index 4a3ddb0ac03..a689675b463 100644 --- a/chromium/ui/arc/notification/arc_notification_delegate.cc +++ b/chromium/ui/arc/notification/arc_notification_delegate.cc @@ -25,12 +25,7 @@ ArcNotificationDelegate::CreateCustomMessageView( const message_center::Notification& notification) { DCHECK(item_); DCHECK_EQ(item_->GetNotificationId(), notification.id()); - - auto view = std::make_unique<ArcNotificationContentView>(item_.get()); - auto content_view_delegate = view->CreateContentViewDelegate(); - return std::make_unique<ArcNotificationView>(item_.get(), std::move(view), - std::move(content_view_delegate), - notification); + return std::make_unique<ArcNotificationView>(item_.get(), notification); } void ArcNotificationDelegate::Close(bool by_user) { @@ -38,7 +33,9 @@ void ArcNotificationDelegate::Close(bool by_user) { item_->Close(by_user); } -void ArcNotificationDelegate::Click() { +void ArcNotificationDelegate::Click( + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) { DCHECK(item_); item_->Click(); } diff --git a/chromium/ui/arc/notification/arc_notification_delegate.h b/chromium/ui/arc/notification/arc_notification_delegate.h index c90da5dc3c5..34481382069 100644 --- a/chromium/ui/arc/notification/arc_notification_delegate.h +++ b/chromium/ui/arc/notification/arc_notification_delegate.h @@ -32,7 +32,8 @@ class ArcNotificationDelegate : public message_center::NotificationDelegate { // message_center::NotificationDelegate overrides: void Close(bool by_user) override; - void Click() override; + void Click(const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) override; void SettingsClick() override; private: diff --git a/chromium/ui/arc/notification/arc_notification_item.h b/chromium/ui/arc/notification/arc_notification_item.h index cbe94104602..e31015c3832 100644 --- a/chromium/ui/arc/notification/arc_notification_item.h +++ b/chromium/ui/arc/notification/arc_notification_item.h @@ -18,9 +18,6 @@ class ArcNotificationItem { // Invoked when the notification data for this item has changed. virtual void OnItemDestroying() = 0; - // Invoked when the notification data for the item is updated. - virtual void OnItemUpdated() = 0; - protected: virtual ~Observer() = default; }; @@ -32,7 +29,8 @@ class ArcNotificationItem { virtual void OnClosedFromAndroid() = 0; // Called when the notification is updated on Android-side. This is called // from ArcNotificationManager. - virtual void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data) = 0; + virtual void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data, + const std::string& app_id) = 0; // Called when the notification is closed on Chrome-side. This is called from // ArcNotificationDelegate. @@ -47,10 +45,6 @@ class ArcNotificationItem { // Called when the user wants to toggle expansio of notification. This is // called from ArcNotificationContentView. virtual void ToggleExpansion() = 0; - // Returns true if this notification has an intrinsic setting which shown - // inside the notification content area. This is called from - // ArcNotificationContentView. - virtual bool IsOpeningSettingsSupported() const = 0; // Adds an observer. virtual void AddObserver(Observer* observer) = 0; @@ -68,6 +62,8 @@ class ArcNotificationItem { // Returns the current snapshot. virtual const gfx::ImageSkia& GetSnapshot() const = 0; // Returns the current expand state. + virtual mojom::ArcNotificationType GetNotificationType() const = 0; + // Returns the current expand state. virtual mojom::ArcNotificationExpandState GetExpandState() const = 0; virtual bool IsManuallyExpandedOrCollapsed() const = 0; @@ -81,8 +77,6 @@ class ArcNotificationItem { virtual const std::string& GetNotificationKey() const = 0; // Returns the notification ID used in the Chrome message center. virtual const std::string& GetNotificationId() const = 0; - // Returnes the accessible name of the notification. - virtual const base::string16& GetAccessibleName() const = 0; }; } // namespace arc diff --git a/chromium/ui/arc/notification/arc_notification_item_impl.cc b/chromium/ui/arc/notification/arc_notification_item_impl.cc index a889206a543..40b317f87d3 100644 --- a/chromium/ui/arc/notification/arc_notification_item_impl.cc +++ b/chromium/ui/arc/notification/arc_notification_item_impl.cc @@ -7,7 +7,7 @@ #include <utility> #include <vector> -#include "base/memory/ptr_util.h" +#include "ash/shelf/shelf_constants.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/arc/notification/arc_notification_delegate.h" @@ -22,7 +22,6 @@ namespace arc { namespace { -constexpr char kNotifierId[] = "ARC_NOTIFICATION"; constexpr char kNotificationIdPrefix[] = "ARC_NOTIFICATION_"; // Converts from Android notification priority to Chrome notification priority. @@ -68,7 +67,8 @@ ArcNotificationItemImpl::~ArcNotificationItemImpl() { } void ArcNotificationItemImpl::OnUpdatedFromAndroid( - mojom::ArcNotificationDataPtr data) { + mojom::ArcNotificationDataPtr data, + const std::string& app_id) { DCHECK(CalledOnValidThread()); DCHECK_EQ(notification_key_, data->key); @@ -77,21 +77,17 @@ void ArcNotificationItemImpl::OnUpdatedFromAndroid( rich_data.priority = ConvertAndroidPriority(data->priority); if (data->small_icon) rich_data.small_image = gfx::Image::CreateFrom1xBitmap(*data->small_icon); - if (data->accessible_name.has_value()) { - accessible_name_ = base::UTF8ToUTF16(*data->accessible_name); - } else { - accessible_name_ = base::JoinString( - {base::UTF8ToUTF16(data->title), base::UTF8ToUTF16(data->message)}, - base::ASCIIToUTF16("\n")); - } - rich_data.accessible_name = accessible_name_; - if (IsOpeningSettingsSupported()) { + + rich_data.accessible_name = base::UTF8ToUTF16( + data->accessible_name.value_or(data->title + "\n" + data->message)); + if (manager_->IsOpeningSettingsSupported()) { rich_data.settings_button_handler = message_center::SettingsButtonHandler::DELEGATE; } message_center::NotifierId notifier_id( - message_center::NotifierId::ARC_APPLICATION, kNotifierId); + message_center::NotifierId::ARC_APPLICATION, + app_id.empty() ? ash::kDefaultArcNotifierId : app_id); notifier_id.profile_id = profile_id_.GetUserEmail(); auto notification = std::make_unique<message_center::Notification>( @@ -112,6 +108,7 @@ void ArcNotificationItemImpl::OnUpdatedFromAndroid( manually_expanded_or_collapsed_ = true; } + type_ = data->type; expand_state_ = data->expand_state; shown_contents_ = data->shown_contents; swipe_input_rect_ = @@ -128,9 +125,6 @@ void ArcNotificationItemImpl::OnUpdatedFromAndroid( gfx::ImageSkiaRep(*data->snapshot_image, data->snapshot_image_scale)); } - for (auto& observer : observers_) - observer.OnItemUpdated(); - message_center_->AddNotification(std::move(notification)); } @@ -159,10 +153,6 @@ void ArcNotificationItemImpl::OpenSettings() { manager_->OpenNotificationSettings(notification_key_); } -bool ArcNotificationItemImpl::IsOpeningSettingsSupported() const { - return manager_->IsOpeningSettingsSupported(); -} - void ArcNotificationItemImpl::ToggleExpansion() { switch (expand_state_) { case mojom::ArcNotificationExpandState::EXPANDED: @@ -208,6 +198,11 @@ const gfx::ImageSkia& ArcNotificationItemImpl::GetSnapshot() const { return snapshot_; } +mojom::ArcNotificationType ArcNotificationItemImpl::GetNotificationType() + const { + return type_; +} + mojom::ArcNotificationExpandState ArcNotificationItemImpl::GetExpandState() const { return expand_state_; @@ -234,8 +229,4 @@ const std::string& ArcNotificationItemImpl::GetNotificationId() const { return notification_id_; } -const base::string16& ArcNotificationItemImpl::GetAccessibleName() const { - return accessible_name_; -} - } // namespace arc diff --git a/chromium/ui/arc/notification/arc_notification_item_impl.h b/chromium/ui/arc/notification/arc_notification_item_impl.h index 33bea528b40..b1557fdcfc8 100644 --- a/chromium/ui/arc/notification/arc_notification_item_impl.h +++ b/chromium/ui/arc/notification/arc_notification_item_impl.h @@ -32,24 +32,24 @@ class ArcNotificationItemImpl : public ArcNotificationItem { // ArcNotificationItem overrides: void OnClosedFromAndroid() override; - void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data) override; + void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data, + const std::string& app_id) override; void Close(bool by_user) override; void Click() override; void OpenSettings() override; - bool IsOpeningSettingsSupported() const override; void ToggleExpansion() override; void AddObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override; void IncrementWindowRefCount() override; void DecrementWindowRefCount() override; const gfx::ImageSkia& GetSnapshot() const override; + mojom::ArcNotificationType GetNotificationType() const override; mojom::ArcNotificationExpandState GetExpandState() const override; bool IsManuallyExpandedOrCollapsed() const override; mojom::ArcNotificationShownContents GetShownContents() const override; gfx::Rect GetSwipeInputRect() const override; const std::string& GetNotificationKey() const override; const std::string& GetNotificationId() const override; - const base::string16& GetAccessibleName() const override; private: // Return true if it's on the thread this instance is created on. @@ -60,6 +60,8 @@ class ArcNotificationItemImpl : public ArcNotificationItem { // The snapshot of the latest notification. gfx::ImageSkia snapshot_; + // The type of the latest notification. + mojom::ArcNotificationType type_ = mojom::ArcNotificationType::SIMPLE; // The expand state of the latest notification. mojom::ArcNotificationExpandState expand_state_ = mojom::ArcNotificationExpandState::FIXED_SIZE; @@ -70,8 +72,6 @@ class ArcNotificationItemImpl : public ArcNotificationItem { gfx::Rect swipe_input_rect_ = gfx::Rect(); // The reference counter of the window. int window_ref_count_ = 0; - // The accessible name of the latest notification. - base::string16 accessible_name_; base::ObserverList<Observer> observers_; diff --git a/chromium/ui/arc/notification/arc_notification_manager.cc b/chromium/ui/arc/notification/arc_notification_manager.cc index 98553c4b090..c015fc7162d 100644 --- a/chromium/ui/arc/notification/arc_notification_manager.cc +++ b/chromium/ui/arc/notification/arc_notification_manager.cc @@ -7,8 +7,6 @@ #include <memory> #include <utility> -#include "ash/shell.h" -#include "ash/system/toast/toast_manager.h" #include "base/memory/ptr_util.h" #include "base/memory/singleton.h" #include "base/stl_util.h" @@ -130,7 +128,8 @@ void ArcNotificationManager::OnNotificationPosted( const std::string& key = data->key; auto it = items_.find(key); if (it == items_.end()) { - // Show a notification on the primary logged-in user's desktop. + // Show a notification on the primary logged-in user's desktop and badge the + // app icon in the shelf if the icon exists. // TODO(yoshiki): Reconsider when ARC supports multi-user. auto item = std::make_unique<ArcNotificationItemImpl>( this, message_center_, key, main_profile_id_); @@ -139,7 +138,9 @@ void ArcNotificationManager::OnNotificationPosted( DCHECK(result.second); it = result.first; } - it->second->OnUpdatedFromAndroid(std::move(data)); + const std::string app_id = + data->package_name ? GetAppId(data->package_name.value()) : std::string(); + it->second->OnUpdatedFromAndroid(std::move(data), app_id); } void ArcNotificationManager::OnNotificationUpdated( @@ -154,7 +155,8 @@ void ArcNotificationManager::OnNotificationUpdated( if (it == items_.end()) return; - it->second->OnUpdatedFromAndroid(std::move(data)); + const std::string app_id = GetAppId(data->package_name.value()); + it->second->OnUpdatedFromAndroid(std::move(data), app_id); } void ArcNotificationManager::OnNotificationRemoved(const std::string& key) { @@ -340,17 +342,8 @@ void ArcNotificationManager::SendNotificationToggleExpansionOnChrome( key, mojom::ArcNotificationEvent::TOGGLE_EXPANSION); } -void ArcNotificationManager::OnToastPosted(mojom::ArcToastDataPtr data) { - const base::string16 text16( - base::UTF8ToUTF16(data->text.has_value() ? *data->text : std::string())); - const base::string16 dismiss_text16(base::UTF8ToUTF16( - data->dismiss_text.has_value() ? *data->dismiss_text : std::string())); - ash::Shell::Get()->toast_manager()->Show( - ash::ToastData(data->id, text16, data->duration, dismiss_text16)); -} - -void ArcNotificationManager::OnToastCancelled(mojom::ArcToastDataPtr data) { - ash::Shell::Get()->toast_manager()->Cancel(data->id); +void ArcNotificationManager::Shutdown() { + get_app_id_callback_.Reset(); } bool ArcNotificationManager::ShouldIgnoreNotification( @@ -361,4 +354,12 @@ bool ArcNotificationManager::ShouldIgnoreNotification( *data->package_name == kPlayStorePackageName && IsRobotAccountMode(); } +std::string ArcNotificationManager::GetAppId( + const std::string& package_name) const { + if (get_app_id_callback_.is_null()) + return std::string(); + + return get_app_id_callback_.Run(package_name); +} + } // namespace arc diff --git a/chromium/ui/arc/notification/arc_notification_manager.h b/chromium/ui/arc/notification/arc_notification_manager.h index 07058352b78..42279600213 100644 --- a/chromium/ui/arc/notification/arc_notification_manager.h +++ b/chromium/ui/arc/notification/arc_notification_manager.h @@ -51,6 +51,12 @@ class ArcNotificationManager ~ArcNotificationManager() override; + void set_get_app_id_callback( + base::RepeatingCallback<std::string(const std::string&)> + get_app_id_callback) { + get_app_id_callback_ = std::move(get_app_id_callback); + } + // ConnectionObserver<mojom::NotificationsInstance> implementation: void OnConnectionReady() override; void OnConnectionClosed() override; @@ -59,8 +65,6 @@ class ArcNotificationManager void OnNotificationPosted(mojom::ArcNotificationDataPtr data) override; void OnNotificationUpdated(mojom::ArcNotificationDataPtr data) override; void OnNotificationRemoved(const std::string& key) override; - void OnToastPosted(mojom::ArcToastDataPtr data) override; - void OnToastCancelled(mojom::ArcToastDataPtr data) override; // Methods called from ArcNotificationItem: void SendNotificationRemovedFromChrome(const std::string& key); @@ -73,6 +77,9 @@ class ArcNotificationManager bool IsOpeningSettingsSupported() const; void SendNotificationToggleExpansionOnChrome(const std::string& key); + // Overridden from KeyedService: + void Shutdown() override; + private: ArcNotificationManager(ArcBridgeService* bridge_service, const AccountId& main_profile_id, @@ -80,6 +87,9 @@ class ArcNotificationManager bool ShouldIgnoreNotification(mojom::ArcNotificationData* data); + // Calls |get_app_id_callback_| to retrieve the app id from ArcAppListPrefs. + std::string GetAppId(const std::string& package_name) const; + ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. const AccountId main_profile_id_; message_center::MessageCenter* const message_center_; @@ -88,6 +98,8 @@ class ArcNotificationManager std::unordered_map<std::string, std::unique_ptr<ArcNotificationItem>>; ItemMap items_; + base::RepeatingCallback<std::string(const std::string&)> get_app_id_callback_; + bool ready_ = false; DISALLOW_COPY_AND_ASSIGN(ArcNotificationManager); diff --git a/chromium/ui/arc/notification/arc_notification_manager_unittest.cc b/chromium/ui/arc/notification/arc_notification_manager_unittest.cc index 98f586105d2..1ef695c1b7a 100644 --- a/chromium/ui/arc/notification/arc_notification_manager_unittest.cc +++ b/chromium/ui/arc/notification/arc_notification_manager_unittest.cc @@ -8,7 +8,6 @@ #include <utility> #include <vector> -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "components/arc/arc_bridge_service.h" @@ -85,6 +84,7 @@ class ArcNotificationManagerTest : public testing::Test { data->key = key; data->title = "TITLE"; data->message = "MESSAGE"; + data->package_name = "PACKAGE_NAME"; arc_notification_manager()->OnNotificationPosted(std::move(data)); diff --git a/chromium/ui/arc/notification/arc_notification_surface_manager_impl.cc b/chromium/ui/arc/notification/arc_notification_surface_manager_impl.cc index 29778a785b7..72c834d5bb3 100644 --- a/chromium/ui/arc/notification/arc_notification_surface_manager_impl.cc +++ b/chromium/ui/arc/notification/arc_notification_surface_manager_impl.cc @@ -7,7 +7,6 @@ #include <string> #include <utility> -#include "base/memory/ptr_util.h" #include "components/exo/notification_surface.h" #include "ui/arc/notification/arc_notification_surface_impl.h" diff --git a/chromium/ui/arc/notification/arc_notification_view.cc b/chromium/ui/arc/notification/arc_notification_view.cc index 6977d6d385f..1cee5514afb 100644 --- a/chromium/ui/arc/notification/arc_notification_view.cc +++ b/chromium/ui/arc/notification/arc_notification_view.cc @@ -7,7 +7,7 @@ #include <algorithm> #include "ui/accessibility/ax_action_data.h" -#include "ui/arc/notification/arc_notification_content_view_delegate.h" +#include "ui/arc/notification/arc_notification_content_view.h" #include "ui/arc/notification/arc_notification_item.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" @@ -23,33 +23,26 @@ namespace arc { // static -const char ArcNotificationView::kViewClassName[] = "ArcNotificationView"; +const char ArcNotificationView::kMessageViewSubClassName[] = + "ArcNotificationView"; ArcNotificationView::ArcNotificationView( ArcNotificationItem* item, - std::unique_ptr<views::View> contents_view, - std::unique_ptr<ArcNotificationContentViewDelegate> contents_view_delegate, const message_center::Notification& notification) : message_center::MessageView(notification), item_(item), - contents_view_(contents_view.get()), - contents_view_delegate_(std::move(contents_view_delegate)) { + content_view_(new ArcNotificationContentView(item_, notification, this)) { DCHECK_EQ(message_center::NOTIFICATION_TYPE_CUSTOM, notification.type()); item_->AddObserver(this); - DCHECK(contents_view); - AddChildView(contents_view.release()); + AddChildView(content_view_); - DCHECK(contents_view_delegate_); - - if (contents_view_->background()) { + if (content_view_->background()) { background_view()->background()->SetNativeControlColor( - contents_view_->background()->get_color()); + content_view_->background()->get_color()); } - UpdateControlButtonsVisibilityWithNotification(notification); - focus_painter_ = views::Painter::CreateSolidFocusPainter( message_center::kFocusBorderColor, gfx::Insets(0, 1, 3, 2)); } @@ -70,39 +63,23 @@ void ArcNotificationView::OnContentBlured() { void ArcNotificationView::UpdateWithNotification( const message_center::Notification& notification) { message_center::MessageView::UpdateWithNotification(notification); - - UpdateControlButtonsVisibilityWithNotification(notification); + content_view_->Update(this, notification); } void ArcNotificationView::SetDrawBackgroundAsActive(bool active) { - // Do nothing if |contents_view_| has a background. - if (contents_view_->background()) + // Do nothing if |content_view_| has a background. + if (content_view_->background()) return; message_center::MessageView::SetDrawBackgroundAsActive(active); } -bool ArcNotificationView::IsCloseButtonFocused() const { - if (!GetControlButtonsView()) - return false; - return GetControlButtonsView()->IsCloseButtonFocused(); -} - -void ArcNotificationView::RequestFocusOnCloseButton() { - if (GetControlButtonsView()) { - GetControlButtonsView()->RequestFocusOnCloseButton(); - if (contents_view_delegate_) - contents_view_delegate_->UpdateControlButtonsVisibility(); - } -} - -const char* ArcNotificationView::GetClassName() const { - return kViewClassName; +void ArcNotificationView::UpdateControlButtonsVisibility() { + content_view_->UpdateControlButtonsVisibility(); } -void ArcNotificationView::UpdateControlButtonsVisibility() { - if (contents_view_delegate_) - contents_view_delegate_->UpdateControlButtonsVisibility(); +const char* ArcNotificationView::GetMessageViewSubClassName() const { + return kMessageViewSubClassName; } void ArcNotificationView::GetAccessibleNodeData(ui::AXNodeData* node_data) { @@ -112,7 +89,7 @@ void ArcNotificationView::GetAccessibleNodeData(ui::AXNodeData* node_data) { message_center::NotificationControlButtonsView* ArcNotificationView::GetControlButtonsView() const { - return contents_view_delegate_->GetControlButtonsView(); + return content_view_->GetControlButtonsView(); } bool ArcNotificationView::IsExpanded() const { @@ -120,6 +97,16 @@ bool ArcNotificationView::IsExpanded() const { item_->GetExpandState() == mojom::ArcNotificationExpandState::EXPANDED; } +bool ArcNotificationView::IsAutoExpandingAllowed() const { + if (!item_) + return false; + + // Disallow auto-expanding if the notificaiton is bundled. This is consistent + // behavior with Android since expanded height of bundle notification might be + // too long vertically. + return item_->GetNotificationType() != mojom::ArcNotificationType::BUNDLED; +} + void ArcNotificationView::SetExpanded(bool expanded) { if (!item_) return; @@ -141,75 +128,68 @@ bool ArcNotificationView::IsManuallyExpandedOrCollapsed() const { } void ArcNotificationView::OnContainerAnimationStarted() { - if (contents_view_delegate_) - contents_view_delegate_->OnContainerAnimationStarted(); + content_view_->OnContainerAnimationStarted(); } void ArcNotificationView::OnContainerAnimationEnded() { - if (contents_view_delegate_) - contents_view_delegate_->OnContainerAnimationEnded(); + content_view_->OnContainerAnimationEnded(); } void ArcNotificationView::OnSlideChanged() { - if (contents_view_delegate_) - contents_view_delegate_->OnSlideChanged(); + content_view_->OnSlideChanged(); } gfx::Size ArcNotificationView::CalculatePreferredSize() const { const gfx::Insets insets = GetInsets(); const int contents_width = message_center::kNotificationWidth; - const int contents_height = contents_view_->GetHeightForWidth(contents_width); + const int contents_height = content_view_->GetHeightForWidth(contents_width); return gfx::Size(contents_width + insets.width(), contents_height + insets.height()); } void ArcNotificationView::Layout() { // Setting the bounds before calling the parent to prevent double Layout. - contents_view_->SetBoundsRect(GetContentsBounds()); + content_view_->SetBoundsRect(GetContentsBounds()); message_center::MessageView::Layout(); // If the content view claims focus, defer focus handling to the content view. - if (contents_view_->IsFocusable()) + if (content_view_->IsFocusable()) SetFocusBehavior(FocusBehavior::NEVER); } bool ArcNotificationView::HasFocus() const { // In case that focus handling is defered to the content view, asking the // content view about focus. - if (contents_view_ && contents_view_->IsFocusable()) - return contents_view_->HasFocus(); - else - return message_center::MessageView::HasFocus(); + return content_view_->IsFocusable() ? content_view_->HasFocus() + : message_center::MessageView::HasFocus(); } void ArcNotificationView::RequestFocus() { - if (contents_view_ && contents_view_->IsFocusable()) - contents_view_->RequestFocus(); + if (content_view_->IsFocusable()) + content_view_->RequestFocus(); else message_center::MessageView::RequestFocus(); } void ArcNotificationView::OnPaint(gfx::Canvas* canvas) { MessageView::OnPaint(canvas); - if (contents_view_ && contents_view_->IsFocusable()) - views::Painter::PaintFocusPainter(contents_view_, canvas, + if (content_view_->IsFocusable()) { + views::Painter::PaintFocusPainter(content_view_, canvas, focus_painter_.get()); + } } bool ArcNotificationView::OnKeyPressed(const ui::KeyEvent& event) { - if (contents_view_) { - ui::InputMethod* input_method = contents_view_->GetInputMethod(); - if (input_method) { - ui::TextInputClient* text_input_client = - input_method->GetTextInputClient(); - if (text_input_client && - text_input_client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) { - // If the focus is in an edit box, we skip the special key handling for - // back space and return keys. So that these key events are sent to the - // arc container correctly without being handled by the message center. - return false; - } + ui::InputMethod* input_method = content_view_->GetInputMethod(); + if (input_method) { + ui::TextInputClient* text_input_client = input_method->GetTextInputClient(); + if (text_input_client && + text_input_client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) { + // If the focus is in an edit box, we skip the special key handling for + // back space and return keys. So that these key events are sent to the + // arc container correctly without being handled by the message center. + return false; } } @@ -235,19 +215,4 @@ void ArcNotificationView::OnItemDestroying() { item_ = nullptr; } -void ArcNotificationView::OnItemUpdated() {} - -// TODO(yoshiki): move this to MessageView and share the code among -// NotificationView and NotificationViewMD. -void ArcNotificationView::UpdateControlButtonsVisibilityWithNotification( - const message_center::Notification& notification) { - if (!GetControlButtonsView()) - return; - - GetControlButtonsView()->ShowSettingsButton( - notification.should_show_settings_button()); - GetControlButtonsView()->ShowCloseButton(!GetPinned()); - UpdateControlButtonsVisibility(); -} - } // namespace message_center diff --git a/chromium/ui/arc/notification/arc_notification_view.h b/chromium/ui/arc/notification/arc_notification_view.h index e54838bb0b5..2e9e64a4dc6 100644 --- a/chromium/ui/arc/notification/arc_notification_view.h +++ b/chromium/ui/arc/notification/arc_notification_view.h @@ -15,20 +15,17 @@ class Painter; namespace arc { -class ArcNotificationContentViewDelegate; +class ArcNotificationContentView; // View for custom notification with NOTIFICATION_TYPE_CUSTOM which hosts the // ArcNotificationContentView which shows content of the notification. class ArcNotificationView : public message_center::MessageView, public ArcNotificationItem::Observer { public: - static const char kViewClassName[]; + static const char kMessageViewSubClassName[]; // |content_view| is a view to be hosted in this view. ArcNotificationView(ArcNotificationItem* item, - std::unique_ptr<views::View> content_view, - std::unique_ptr<ArcNotificationContentViewDelegate> - contents_view_delegate, const message_center::Notification& notification); ~ArcNotificationView() override; @@ -41,23 +38,22 @@ class ArcNotificationView : public message_center::MessageView, void UpdateWithNotification( const message_center::Notification& notification) override; void SetDrawBackgroundAsActive(bool active) override; - bool IsCloseButtonFocused() const override; - void RequestFocusOnCloseButton() override; void UpdateControlButtonsVisibility() override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; message_center::NotificationControlButtonsView* GetControlButtonsView() const override; bool IsExpanded() const override; void SetExpanded(bool expanded) override; + bool IsAutoExpandingAllowed() const override; bool IsManuallyExpandedOrCollapsed() const override; void OnContainerAnimationStarted() override; void OnContainerAnimationEnded() override; + const char* GetMessageViewSubClassName() const final; // views::SlideOutController::Delegate: void OnSlideChanged() override; // Overridden from views::View: - const char* GetClassName() const override; gfx::Size CalculatePreferredSize() const override; void Layout() override; bool HasFocus() const override; @@ -69,11 +65,11 @@ class ArcNotificationView : public message_center::MessageView, // ArcNotificationItem::Observer void OnItemDestroying() override; - void OnItemUpdated() override; private: friend class ArcNotificationContentViewTest; friend class ArcNotificationViewTest; + friend class ArcAccessibilityHelperBridgeTest; // TODO(yoshiki): Mmove this to message_center::MessageView. void UpdateControlButtonsVisibilityWithNotification( @@ -82,8 +78,7 @@ class ArcNotificationView : public message_center::MessageView, ArcNotificationItem* item_; // The view for the custom content. Owned by view hierarchy. - views::View* contents_view_ = nullptr; - std::unique_ptr<ArcNotificationContentViewDelegate> contents_view_delegate_; + ArcNotificationContentView* const content_view_; std::unique_ptr<views::Painter> focus_painter_; diff --git a/chromium/ui/arc/notification/arc_notification_view_unittest.cc b/chromium/ui/arc/notification/arc_notification_view_unittest.cc index e988fdd763b..bac3ab667ca 100644 --- a/chromium/ui/arc/notification/arc_notification_view_unittest.cc +++ b/chromium/ui/arc/notification/arc_notification_view_unittest.cc @@ -4,12 +4,13 @@ #include <memory> +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkColor.h" -#include "ui/arc/notification/arc_notification_content_view_delegate.h" +#include "ui/arc/notification/arc_notification_content_view.h" #include "ui/arc/notification/arc_notification_item.h" #include "ui/arc/notification/arc_notification_view.h" #include "ui/base/ime/dummy_text_input_client.h" @@ -22,6 +23,7 @@ #include "ui/message_center/message_center.h" #include "ui/message_center/public/cpp/notification.h" #include "ui/message_center/views/message_view_factory.h" +#include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/views/background.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/test/views_test_base.h" @@ -34,59 +36,6 @@ namespace arc { namespace { constexpr char kNotificationIdPrefix[] = "ARC_NOTIFICATION_"; -const SkColor kBackgroundColor = SK_ColorGREEN; - -class TestNotificationContentsView : public views::View { - public: - TestNotificationContentsView() { - SetFocusBehavior(FocusBehavior::ALWAYS); - SetBackground(views::CreateSolidBackground(kBackgroundColor)); - SetPreferredSize(gfx::Size(100, 100)); - } - ~TestNotificationContentsView() override = default; - - void Reset() { - mouse_event_count_ = 0; - keyboard_event_count_ = 0; - } - - // views::View - bool OnMousePressed(const ui::MouseEvent& event) override { - ++mouse_event_count_; - return true; - } - void OnMouseMoved(const ui::MouseEvent& event) override { - ++mouse_event_count_; - } - void OnMouseReleased(const ui::MouseEvent& event) override { - ++mouse_event_count_; - } - bool OnKeyPressed(const ui::KeyEvent& event) override { - ++keyboard_event_count_; - return false; - } - - int mouse_event_count() const { return mouse_event_count_; } - int keyboard_event_count() const { return keyboard_event_count_; } - - private: - int mouse_event_count_ = 0; - int keyboard_event_count_ = 0; - - DISALLOW_COPY_AND_ASSIGN(TestNotificationContentsView); -}; - -class TestContentViewDelegate : public ArcNotificationContentViewDelegate { - public: - void UpdateControlButtonsVisibility() override {} - void OnSlideChanged() override {} - message_center::NotificationControlButtonsView* GetControlButtonsView() - const override { - return nullptr; - } - void OnContainerAnimationStarted() override {} - void OnContainerAnimationEnded() override {} -}; class MockArcNotificationItem : public ArcNotificationItem { public: @@ -113,7 +62,9 @@ class MockArcNotificationItem : public ArcNotificationItem { void RemoveObserver(Observer* observer) override {} void IncrementWindowRefCount() override {} void DecrementWindowRefCount() override {} - bool IsOpeningSettingsSupported() const override { return true; } + mojom::ArcNotificationType GetNotificationType() const override { + return mojom::ArcNotificationType::SIMPLE; + } mojom::ArcNotificationExpandState GetExpandState() const override { return mojom::ArcNotificationExpandState::FIXED_SIZE; } @@ -121,10 +72,8 @@ class MockArcNotificationItem : public ArcNotificationItem { return mojom::ArcNotificationShownContents::CONTENTS_SHOWN; } gfx::Rect GetSwipeInputRect() const override { return gfx::Rect(); } - const base::string16& GetAccessibleName() const override { - return base::EmptyString16(); - }; - void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data) override {} + void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data, + const std::string& app_id) override {} bool IsManuallyExpandedOrCollapsed() const override { return false; } private: @@ -135,14 +84,6 @@ class MockArcNotificationItem : public ArcNotificationItem { DISALLOW_COPY_AND_ASSIGN(MockArcNotificationItem); }; -std::unique_ptr<message_center::MessageView> CreateCustomMessageViewForTest( - ArcNotificationItem* item, - const Notification& notification) { - return std::make_unique<ArcNotificationView>( - item, std::make_unique<TestNotificationContentsView>(), - std::make_unique<TestContentViewDelegate>(), notification); -} - class TestTextInputClient : public ui::DummyTextInputClient { public: TestTextInputClient() : ui::DummyTextInputClient(ui::TEXT_INPUT_TYPE_TEXT) {} @@ -159,21 +100,21 @@ class TestTextInputClient : public ui::DummyTextInputClient { } // namespace -class ArcNotificationViewTest : public views::ViewsTestBase { +class ArcNotificationViewTest : public ash::AshTestBase { public: ArcNotificationViewTest() = default; ~ArcNotificationViewTest() override = default; // views::ViewsTestBase void SetUp() override { - views::ViewsTestBase::SetUp(); - - MessageCenter::Initialize(); + ash::AshTestBase::SetUp(); const std::string notification_id("notification id"); item_ = std::make_unique<MockArcNotificationItem>(notification_id); message_center::MessageViewFactory::SetCustomNotificationViewFactory( - base::BindRepeating(&CreateCustomMessageViewForTest, item_.get())); + base::BindRepeating( + &ArcNotificationViewTest::CreateCustomMessageViewForTest, + base::Unretained(this), item_.get())); notification_ = std::make_unique<Notification>( message_center::NOTIFICATION_TYPE_CUSTOM, notification_id, @@ -189,23 +130,24 @@ class ArcNotificationViewTest : public views::ViewsTestBase { UpdateNotificationViews(); views::Widget::InitParams init_params( - CreateParams(views::Widget::InitParams::TYPE_POPUP)); + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.context = CurrentContext(); + init_params.parent = ash::Shell::GetPrimaryRootWindow()->GetChildById( + ash::kShellWindowId_DefaultContainer); + init_params.ownership = + views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; views::Widget* widget = new views::Widget(); widget->Init(init_params); widget->SetContentsView(notification_view_.get()); widget->SetSize(notification_view_->GetPreferredSize()); widget->Show(); + EXPECT_EQ(widget, notification_view_->GetWidget()); } void TearDown() override { widget()->Close(); notification_view_.reset(); - views::ViewsTestBase::TearDown(); - MessageCenter::Shutdown(); - } - - SkColor GetBackgroundColor() const { - return notification_view_->background_view()->background()->get_color(); + ash::AshTestBase::TearDown(); } void PerformClick(const gfx::Point& point) { @@ -226,11 +168,6 @@ class ArcNotificationViewTest : public views::ViewsTestBase { widget()->OnKeyEvent(&event2); } - void KeyPress(ui::KeyboardCode key_code) { - ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE); - widget()->OnKeyEvent(&event); - } - void UpdateNotificationViews() { MessageCenter::Get()->AddNotification( std::make_unique<Notification>(*notification())); @@ -249,10 +186,8 @@ class ArcNotificationViewTest : public views::ViewsTestBase { } void DispatchGesture(const ui::GestureEventDetails& details) { - ui::test::EventGenerator generator( - notification_view()->GetWidget()->GetNativeWindow()); - ui::GestureEvent event(0, 0, 0, ui::EventTimeForNow(), details); - generator.Dispatch(&event); + ui::GestureEvent event2(0, 0, 0, ui::EventTimeForNow(), details); + widget()->OnGestureEvent(&event2); } void BeginScroll() { @@ -269,14 +204,22 @@ class ArcNotificationViewTest : public views::ViewsTestBase { } Notification* notification() { return notification_.get(); } - TestNotificationContentsView* contents_view() { - return static_cast<TestNotificationContentsView*>( - notification_view_->contents_view_); + ArcNotificationContentView* content_view() { + return notification_view_->content_view_; } views::Widget* widget() { return notification_view_->GetWidget(); } ArcNotificationView* notification_view() { return notification_view_.get(); } private: + std::unique_ptr<message_center::MessageView> CreateCustomMessageViewForTest( + ArcNotificationItem* item, + const Notification& notification) { + auto message_view = + std::make_unique<ArcNotificationView>(item, notification); + message_view->content_view_->SetPreferredSize(gfx::Size(100, 100)); + return message_view; + } + std::unique_ptr<Notification> notification_; std::unique_ptr<ArcNotificationView> notification_view_; @@ -285,28 +228,20 @@ class ArcNotificationViewTest : public views::ViewsTestBase { DISALLOW_COPY_AND_ASSIGN(ArcNotificationViewTest); }; -TEST_F(ArcNotificationViewTest, Background) { - EXPECT_EQ(kBackgroundColor, GetBackgroundColor()); -} - TEST_F(ArcNotificationViewTest, Events) { widget()->Show(); - contents_view()->RequestFocus(); - EXPECT_EQ(0, contents_view()->mouse_event_count()); gfx::Point cursor_location(1, 1); - views::View::ConvertPointToWidget(contents_view(), &cursor_location); - PerformClick(cursor_location); - EXPECT_EQ(2, contents_view()->mouse_event_count()); - - ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location, - ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); - widget()->OnMouseEvent(&move); - EXPECT_EQ(3, contents_view()->mouse_event_count()); - - EXPECT_EQ(0, contents_view()->keyboard_event_count()); - KeyPress(ui::VKEY_A); - EXPECT_EQ(1, contents_view()->keyboard_event_count()); + views::View::ConvertPointToWidget(content_view(), &cursor_location); + EXPECT_EQ(content_view(), + widget()->GetRootView()->GetEventHandlerForPoint(cursor_location)); + + content_view()->RequestFocus(); + ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE); + EXPECT_EQ(content_view(), + static_cast<ui::EventTargeter*>( + widget()->GetRootView()->GetEffectiveViewTargeter()) + ->FindTargetForEvent(widget()->GetRootView(), &key_event)); } TEST_F(ArcNotificationViewTest, SlideOut) { @@ -316,6 +251,7 @@ TEST_F(ArcNotificationViewTest, SlideOut) { std::string notification_id = notification()->id(); BeginScroll(); + EXPECT_EQ(0.f, GetNotificationSlideAmount()); ScrollBy(-10); EXPECT_FALSE(IsRemoved(notification_id)); EXPECT_EQ(-10.f, GetNotificationSlideAmount()); @@ -324,6 +260,7 @@ TEST_F(ArcNotificationViewTest, SlideOut) { EXPECT_EQ(0.f, GetNotificationSlideAmount()); BeginScroll(); + EXPECT_EQ(0.f, GetNotificationSlideAmount()); ScrollBy(-200); EXPECT_FALSE(IsRemoved(notification_id)); EXPECT_EQ(-200.f, GetNotificationSlideAmount()); @@ -339,6 +276,7 @@ TEST_F(ArcNotificationViewTest, SlideOutNested) { std::string notification_id = notification()->id(); BeginScroll(); + EXPECT_EQ(0.f, GetNotificationSlideAmount()); ScrollBy(-10); EXPECT_FALSE(IsRemoved(notification_id)); EXPECT_EQ(-10.f, GetNotificationSlideAmount()); @@ -347,6 +285,7 @@ TEST_F(ArcNotificationViewTest, SlideOutNested) { EXPECT_EQ(0.f, GetNotificationSlideAmount()); BeginScroll(); + EXPECT_EQ(0.f, GetNotificationSlideAmount()); ScrollBy(-200); EXPECT_FALSE(IsRemoved(notification_id)); EXPECT_EQ(-200.f, GetNotificationSlideAmount()); @@ -367,10 +306,12 @@ TEST_F(ArcNotificationViewTest, SlideOutPinned) { std::string notification_id = notification()->id(); BeginScroll(); + EXPECT_EQ(0.f, GetNotificationSlideAmount()); ScrollBy(-200); EXPECT_FALSE(IsRemoved(notification_id)); EXPECT_LT(-200.f, GetNotificationSlideAmount()); EndScroll(); + EXPECT_EQ(0.f, GetNotificationSlideAmount()); EXPECT_FALSE(IsRemoved(notification_id)); } @@ -378,9 +319,9 @@ TEST_F(ArcNotificationViewTest, SlideOutPinned) { TEST_F(ArcNotificationViewTest, PressBackspaceKey) { std::string notification_id = notification()->id(); - contents_view()->RequestFocus(); + content_view()->RequestFocus(); - ui::InputMethod* input_method = contents_view()->GetInputMethod(); + ui::InputMethod* input_method = content_view()->GetInputMethod(); ASSERT_TRUE(input_method); TestTextInputClient text_input_client; input_method->SetFocusedTextInputClient(&text_input_client); @@ -395,9 +336,9 @@ TEST_F(ArcNotificationViewTest, PressBackspaceKey) { TEST_F(ArcNotificationViewTest, PressBackspaceKeyOnEditBox) { std::string notification_id = notification()->id(); - contents_view()->RequestFocus(); + content_view()->RequestFocus(); - ui::InputMethod* input_method = contents_view()->GetInputMethod(); + ui::InputMethod* input_method = content_view()->GetInputMethod(); ASSERT_TRUE(input_method); TestTextInputClient text_input_client; input_method->SetFocusedTextInputClient(&text_input_client); @@ -419,13 +360,13 @@ TEST_F(ArcNotificationViewTest, ChangeContentHeight) { EXPECT_EQ("360x100", size.ToString()); // Allow small notifications. - contents_view()->SetPreferredSize(gfx::Size(10, 10)); + content_view()->SetPreferredSize(gfx::Size(10, 10)); size = notification_view()->GetPreferredSize(); size.Enlarge(0, -notification_view()->GetInsets().height()); EXPECT_EQ("360x10", size.ToString()); // The long notification. - contents_view()->SetPreferredSize(gfx::Size(1000, 1000)); + content_view()->SetPreferredSize(gfx::Size(1000, 1000)); size = notification_view()->GetPreferredSize(); size.Enlarge(0, -notification_view()->GetInsets().height()); EXPECT_EQ("360x1000", size.ToString()); diff --git a/chromium/ui/arc/notification/mock_arc_notification_item.cc b/chromium/ui/arc/notification/mock_arc_notification_item.cc new file mode 100644 index 00000000000..388d6c71bad --- /dev/null +++ b/chromium/ui/arc/notification/mock_arc_notification_item.cc @@ -0,0 +1,85 @@ +// 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/arc/notification/mock_arc_notification_item.h" + +#include <utility> + +#include "base/bind_helpers.h" + +namespace arc { + +namespace { + +constexpr char kNotificationIdPrefix[] = "ARC_NOTIFICATION_"; + +} // namespace + +MockArcNotificationItem::MockArcNotificationItem( + const std::string& notification_key) + : notification_key_(notification_key), + notification_id_(kNotificationIdPrefix + notification_key), + weak_factory_(this) {} + +MockArcNotificationItem::~MockArcNotificationItem() { + for (auto& observer : observers_) + observer.OnItemDestroying(); +} + +void MockArcNotificationItem::SetCloseCallback( + base::OnceClosure close_callback) { + close_callback_ = std::move(close_callback); +} + +void MockArcNotificationItem::Close(bool by_user) { + count_close_++; + + if (close_callback_) + base::ResetAndReturn(&close_callback_).Run(); +} + +const gfx::ImageSkia& MockArcNotificationItem::GetSnapshot() const { + return snapshot_; +} + +const std::string& MockArcNotificationItem::GetNotificationKey() const { + return notification_key_; +} + +const std::string& MockArcNotificationItem::GetNotificationId() const { + return notification_id_; +} + +void MockArcNotificationItem::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void MockArcNotificationItem::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +mojom::ArcNotificationType MockArcNotificationItem::GetNotificationType() + const { + return mojom::ArcNotificationType::SIMPLE; +} + +mojom::ArcNotificationExpandState MockArcNotificationItem::GetExpandState() + const { + return mojom::ArcNotificationExpandState::FIXED_SIZE; +} + +mojom::ArcNotificationShownContents MockArcNotificationItem::GetShownContents() + const { + return mojom::ArcNotificationShownContents::CONTENTS_SHOWN; +} + +gfx::Rect MockArcNotificationItem::GetSwipeInputRect() const { + return gfx::Rect(); +} + +bool MockArcNotificationItem::IsManuallyExpandedOrCollapsed() const { + return false; +} + +} // namespace arc diff --git a/chromium/ui/arc/notification/mock_arc_notification_item.h b/chromium/ui/arc/notification/mock_arc_notification_item.h new file mode 100644 index 00000000000..db2d22d055c --- /dev/null +++ b/chromium/ui/arc/notification/mock_arc_notification_item.h @@ -0,0 +1,70 @@ +// 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_ARC_NOTIFICATION_MOCK_ARC_NOTIFICATION_ITEM_H_ +#define UI_ARC_NOTIFICATION_MOCK_ARC_NOTIFICATION_ITEM_H_ + +#include <string> + +#include "base/callback.h" +#include "base/observer_list.h" +#include "ui/arc/notification/arc_notification_item.h" + +namespace arc { + +class MockArcNotificationItem : public ArcNotificationItem { + public: + MockArcNotificationItem(const std::string& notification_key); + ~MockArcNotificationItem() override; + + // Methods for testing. + size_t count_close() { return count_close_; } + base::WeakPtr<MockArcNotificationItem> GetWeakPtr() { + return weak_factory_.GetWeakPtr(); + } + + void SetCloseCallback(base::OnceClosure close_callback); + + // Overriding methods for testing. + void Close(bool by_user) override; + const gfx::ImageSkia& GetSnapshot() const override; + const std::string& GetNotificationKey() const override; + const std::string& GetNotificationId() const override; + + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + + // Overriding methods for returning dummy data or doing nothing. + void OnClosedFromAndroid() override {} + void Click() override {} + void ToggleExpansion() override {} + void OpenSettings() override {} + void IncrementWindowRefCount() override {} + void DecrementWindowRefCount() override {} + mojom::ArcNotificationType GetNotificationType() const override; + mojom::ArcNotificationExpandState GetExpandState() const override; + mojom::ArcNotificationShownContents GetShownContents() const override; + gfx::Rect GetSwipeInputRect() const override; + + void OnUpdatedFromAndroid(mojom::ArcNotificationDataPtr data, + const std::string& app_id) override {} + bool IsManuallyExpandedOrCollapsed() const override; + + private: + std::string notification_key_; + std::string notification_id_; + gfx::ImageSkia snapshot_; + size_t count_close_ = 0; + + base::ObserverList<Observer> observers_; + base::OnceClosure close_callback_; + + base::WeakPtrFactory<MockArcNotificationItem> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MockArcNotificationItem); +}; + +} // namespace arc + +#endif // UI_ARC_NOTIFICATION_MOCK_ARC_NOTIFICATION_ITEM_H_ diff --git a/chromium/ui/aura/BUILD.gn b/chromium/ui/aura/BUILD.gn index 04f009d70b7..2f74c3a96c8 100644 --- a/chromium/ui/aura/BUILD.gn +++ b/chromium/ui/aura/BUILD.gn @@ -43,6 +43,8 @@ jumbo_component("aura") { "mus/client_surface_embedder.h", "mus/drag_drop_controller_host.h", "mus/drag_drop_controller_mus.h", + "mus/embed_root.h", + "mus/embed_root_delegate.h", "mus/focus_synchronizer.h", "mus/focus_synchronizer_delegate.h", "mus/focus_synchronizer_observer.h", @@ -110,6 +112,7 @@ jumbo_component("aura") { "mus/capture_synchronizer.cc", "mus/client_surface_embedder.cc", "mus/drag_drop_controller_mus.cc", + "mus/embed_root.cc", "mus/focus_synchronizer.cc", "mus/in_flight_change.cc", "mus/input_method_mus.cc", @@ -151,6 +154,7 @@ jumbo_component("aura") { "//components/discardable_memory/client", "//components/discardable_memory/public/interfaces", "//components/viz/client", + "//components/viz/common", "//components/viz/host", "//components/viz/service", "//gpu/ipc/client", @@ -197,6 +201,10 @@ jumbo_component("aura") { if (use_ozone) { deps += [ "//ui/ozone" ] + sources += [ + "mus/platform_event_source_mus_ozone.cc", + "mus/platform_event_source_mus_ozone.h", + ] } if (is_android) { @@ -258,6 +266,8 @@ jumbo_static_library("test_support") { "test/ui_controls_factory_aura.h", "test/window_event_dispatcher_test_api.cc", "test/window_event_dispatcher_test_api.h", + "test/window_occlusion_tracker_test_api.cc", + "test/window_occlusion_tracker_test_api.h", "test/window_test_api.cc", "test/window_test_api.h", ] @@ -276,6 +286,7 @@ jumbo_static_library("test_support") { "//base/test:test_support", "//cc:test_support", "//components/viz/test:test_support", + "//services/service_manager/public/cpp", "//services/ui/public/cpp/input_devices", "//services/ui/public/interfaces", "//skia", @@ -348,6 +359,7 @@ executable("demo") { test("aura_unittests") { sources = [ + "../compositor_extra/shadow_unittest.cc", "gestures/gesture_recognizer_unittest.cc", "hit_test_data_provider_aura_unittest.cc", "mus/drag_drop_controller_mus_unittest.cc", @@ -360,6 +372,7 @@ test("aura_unittests") { "mus/window_port_mus_unittest.cc", "mus/window_tree_client_unittest.cc", "mus/window_tree_host_mus_unittest.cc", + "test/aura_test_suite.h", "test/run_all_unittests.cc", "window_event_dispatcher_unittest.cc", "window_occlusion_tracker_unittest.cc", @@ -373,7 +386,7 @@ test("aura_unittests") { "//base/test:test_support", "//components/viz/client", "//mojo/common", - "//mojo/edk/system", + "//mojo/edk", "//net", "//services/ui/common:task_runner_test_base", "//services/ui/public/cpp", @@ -381,6 +394,7 @@ test("aura_unittests") { "//testing/gtest", "//ui/base:test_support", "//ui/compositor:test_support", + "//ui/compositor_extra", "//ui/display:test_support", "//ui/events:gesture_detection", "//ui/events:test_support", diff --git a/chromium/ui/aura/OWNERS b/chromium/ui/aura/OWNERS index ff3f87628e1..7958658c4cd 100644 --- a/chromium/ui/aura/OWNERS +++ b/chromium/ui/aura/OWNERS @@ -1,5 +1,5 @@ sky@chromium.org sadrul@chromium.org -per-file *x11.cc=erg@chromium.org -per-file *x11.h=erg@chromium.org +per-file *x11.cc=thomasanderson@chromium.org +per-file *x11.h=thomasanderson@chromium.org diff --git a/chromium/ui/aura/client/aura_constants.cc b/chromium/ui/aura/client/aura_constants.cc index c171556d287..e4217f53689 100644 --- a/chromium/ui/aura/client/aura_constants.cc +++ b/chromium/ui/aura/client/aura_constants.cc @@ -47,7 +47,7 @@ DEFINE_UI_CLASS_PROPERTY_KEY(bool, kConstrainedWindowKey, false); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kCreatedByUserGesture, false); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kDrawAttentionKey, false); DEFINE_UI_CLASS_PROPERTY_KEY(FocusClient*, kFocusClientKey, nullptr); -DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHasBackdrop, false); +DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHasOverviewIcon, false); 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); diff --git a/chromium/ui/aura/client/aura_constants.h b/chromium/ui/aura/client/aura_constants.h index f0f8b6cbf53..b2b1ffe3a11 100644 --- a/chromium/ui/aura/client/aura_constants.h +++ b/chromium/ui/aura/client/aura_constants.h @@ -77,9 +77,9 @@ AURA_EXPORT extern const WindowProperty<bool>* const kDrawAttentionKey; // A property key to store the focus client on the window. AURA_EXPORT extern const WindowProperty<FocusClient*>* const kFocusClientKey; -// A bool property key to specify if the window should have a backdrop window -// (typically black) that covers the desktop behind the window. -AURA_EXPORT extern const WindowProperty<bool>* const kHasBackdrop; +// A bool property key to specify if the window has a icon set for displaying in +// overivew mode. +AURA_EXPORT extern const WindowProperty<bool>* const kHasOverviewIcon; // A property key to store the host window of a window. This lets // WebContentsViews find the windows that should constrain NPAPI plugins. diff --git a/chromium/ui/aura/env.cc b/chromium/ui/aura/env.cc index b62fcc15aba..b32f9f5ddbf 100644 --- a/chromium/ui/aura/env.cc +++ b/chromium/ui/aura/env.cc @@ -6,7 +6,6 @@ #include "base/command_line.h" #include "base/lazy_instance.h" -#include "base/memory/ptr_util.h" #include "base/threading/thread_local.h" #include "services/ui/public/interfaces/window_tree.mojom.h" #include "ui/aura/client/aura_constants.h" @@ -25,8 +24,6 @@ #include "ui/events/platform/platform_event_source.h" #if defined(USE_OZONE) -#include "ui/gfx/client_native_pixmap_factory.h" -#include "ui/ozone/public/client_native_pixmap_factory_ozone.h" #include "ui/ozone/public/ozone_platform.h" #include "ui/ozone/public/ozone_switches.h" #endif @@ -50,10 +47,6 @@ Env::~Env() { if (is_override_input_injector_factory_) ui::SetSystemInputInjectorFactory(nullptr); -#if defined(USE_OZONE) - gfx::ClientNativePixmapFactory::ResetInstance(); -#endif - for (EnvObserver& observer : observers_) observer.OnWillDestroyEnv(); @@ -158,6 +151,9 @@ void Env::ScheduleEmbed( //////////////////////////////////////////////////////////////////////////////// // Env, private: +// static +bool Env::initial_throttle_input_on_resize_ = true; + Env::Env(Mode mode) : mode_(mode), env_controller_(new EnvInputStateController), @@ -172,10 +168,6 @@ Env::Env(Mode mode) } void Env::Init() { -#if defined(USE_OZONE) - ui::CreateClientNativePixmapFactoryOzone(); - DCHECK(gfx::ClientNativePixmapFactory::GetInstance()); -#endif if (mode_ == Mode::MUS) { EnableMusOSExchangeDataProvider(); EnableMusOverrideInputInjector(); diff --git a/chromium/ui/aura/env.h b/chromium/ui/aura/env.h index bc615fdb3a1..7167a9f1655 100644 --- a/chromium/ui/aura/env.h +++ b/chromium/ui/aura/env.h @@ -18,10 +18,6 @@ #include "ui/events/system_input_injector.h" #include "ui/gfx/geometry/point.h" -#if defined(USE_OZONE) -#include "ui/ozone/public/client_native_pixmap_factory_ozone.h" -#endif - namespace base { class UnguessableToken; } @@ -112,6 +108,12 @@ class AURA_EXPORT Env : public ui::EventTarget, } ui::ContextFactory* context_factory() { return context_factory_; } + // Sets |initial_throttle_input_on_resize| the next time Env is created. This + // is only useful in tests that need to disable input resize. + static void set_initial_throttle_input_on_resize_for_testing( + bool throttle_input) { + initial_throttle_input_on_resize_ = throttle_input; + } void set_throttle_input_on_resize_for_testing(bool throttle_input) { throttle_input_on_resize_ = throttle_input; } @@ -215,7 +217,8 @@ class AURA_EXPORT Env : public ui::EventTarget, // creating a different WindowPort implementation. bool in_mus_shutdown_ = false; - bool throttle_input_on_resize_ = true; + static bool initial_throttle_input_on_resize_; + bool throttle_input_on_resize_ = initial_throttle_input_on_resize_; DISALLOW_COPY_AND_ASSIGN(Env); }; diff --git a/chromium/ui/aura/event_injector.cc b/chromium/ui/aura/event_injector.cc index f13e06742c1..c9d9242b60d 100644 --- a/chromium/ui/aura/event_injector.cc +++ b/chromium/ui/aura/event_injector.cc @@ -55,13 +55,13 @@ ui::EventDispatchDetails EventInjector::Inject(WindowTreeHost* host, event->AsLocatedEvent()->location_f()); } - if (!remote_event_dispatcher_) { + if (!event_injector_) { env->window_tree_client_->connector()->BindInterface( - ui::mojom::kServiceName, &remote_event_dispatcher_); + ui::mojom::kServiceName, &event_injector_); } - remote_event_dispatcher_->DispatchEvent( + event_injector_->InjectEvent( host->GetDisplayId(), MapEvent(*event), - base::Bind([](bool result) { DCHECK(result); })); + base::BindOnce([](bool result) { DCHECK(result); })); return ui::EventDispatchDetails(); } diff --git a/chromium/ui/aura/event_injector.h b/chromium/ui/aura/event_injector.h index acfcf1cd2c3..bc336047f51 100644 --- a/chromium/ui/aura/event_injector.h +++ b/chromium/ui/aura/event_injector.h @@ -5,7 +5,7 @@ #ifndef UI_AURA_EVENT_INJECTOR_H_ #define UI_AURA_EVENT_INJECTOR_H_ -#include "services/ui/public/interfaces/remote_event_dispatcher.mojom.h" +#include "services/ui/public/interfaces/event_injector.mojom.h" #include "ui/aura/aura_export.h" namespace ui { @@ -28,7 +28,7 @@ class AURA_EXPORT EventInjector { ui::EventDispatchDetails Inject(WindowTreeHost* host, ui::Event* event); private: - ui::mojom::RemoteEventDispatcherPtr remote_event_dispatcher_; + ui::mojom::EventInjectorPtr event_injector_; DISALLOW_COPY_AND_ASSIGN(EventInjector); }; diff --git a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc index 827c9dca4c2..39c305eaae4 100644 --- a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc +++ b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc @@ -9,7 +9,6 @@ #include "base/command_line.h" #include "base/location.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" diff --git a/chromium/ui/aura/hit_test_data_provider_aura.cc b/chromium/ui/aura/hit_test_data_provider_aura.cc index 0f7600aae12..0f96540ac90 100644 --- a/chromium/ui/aura/hit_test_data_provider_aura.cc +++ b/chromium/ui/aura/hit_test_data_provider_aura.cc @@ -22,13 +22,10 @@ viz::mojom::HitTestRegionPtr CreateHitTestRegion(const aura::Window* window, hit_test_region->frame_sink_id = window->GetFrameSinkId(); // Checking |layer| may not be correct, since the actual layer that embeds // the surface may be a descendent of |layer|, instead of |layer| itself. - if (window->IsEmbeddingClient()) { - DCHECK(window->GetLocalSurfaceId().is_valid()); - hit_test_region->local_surface_id = window->GetLocalSurfaceId(); + if (window->IsEmbeddingClient()) hit_test_region->flags = flags | viz::mojom::kHitTestChildSurface; - } else { + else hit_test_region->flags = flags | viz::mojom::kHitTestMine; - } hit_test_region->rect = rect; hit_test_region->transform = layer->transform(); 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 96a6d82decd..4b0363e6177 100644 --- a/chromium/ui/aura/hit_test_data_provider_aura_unittest.cc +++ b/chromium/ui/aura/hit_test_data_provider_aura_unittest.cc @@ -15,21 +15,6 @@ namespace aura { namespace { -const int kMouseInset = -5; -const int kTouchInset = -10; - -// Custom WindowTargeter that expands hit-test regions of child windows. -class TestWindowTargeter : public WindowTargeter { - public: - TestWindowTargeter() { - SetInsets(gfx::Insets(kMouseInset), gfx::Insets(kTouchInset)); - } - ~TestWindowTargeter() override {} - - private: - DISALLOW_COPY_AND_ASSIGN(TestWindowTargeter); -}; - // Custom WindowTargeter that replaces hit-test area on a window with a frame // rectangle and a hole in the middle 1/3. // ---------------------- @@ -189,7 +174,12 @@ TEST_F(HitTestDataProviderAuraTest, Stacking) { // Tests that the hit-test regions get expanded with a custom event targeter. TEST_F(HitTestDataProviderAuraTest, CustomTargeter) { - window3()->SetEventTargeter(std::make_unique<TestWindowTargeter>()); + constexpr int kMouseInset = -5; + constexpr int kTouchInset = -10; + auto targeter = std::make_unique<aura::WindowTargeter>(); + targeter->SetInsets(gfx::Insets(kMouseInset), gfx::Insets(kTouchInset)); + window3()->SetEventTargeter(std::move(targeter)); + window2()->SetEmbedFrameSinkId(viz::FrameSinkId(1, 2)); const auto 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 787fbc5e7cc..40abe881763 100644 --- a/chromium/ui/aura/local/window_port_local.cc +++ b/chromium/ui/aura/local/window_port_local.cc @@ -140,6 +140,16 @@ void WindowPortLocal::AllocateLocalSurfaceId() { frame_sink_->SetLocalSurfaceId(local_surface_id_); } +bool WindowPortLocal::IsLocalSurfaceIdAllocationSuppressed() const { + return parent_local_surface_id_allocator_.is_allocation_suppressed(); +} + +viz::ScopedSurfaceIdAllocator WindowPortLocal::GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) { + return viz::ScopedSurfaceIdAllocator(&parent_local_surface_id_allocator_, + std::move(allocation_task)); +} + const viz::LocalSurfaceId& WindowPortLocal::GetLocalSurfaceId() { if (!local_surface_id_.is_valid()) AllocateLocalSurfaceId(); @@ -153,7 +163,8 @@ void WindowPortLocal::OnSurfaceChanged(const viz::SurfaceInfo& surface_info) { DCHECK_EQ(surface_info.id().local_surface_id(), local_surface_id_); window_->layer()->SetShowPrimarySurface( surface_info.id(), window_->bounds().size(), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), + false /* stretch_content_to_fill_bounds */); window_->layer()->SetFallbackSurfaceId(surface_info.id()); } diff --git a/chromium/ui/aura/local/window_port_local.h b/chromium/ui/aura/local/window_port_local.h index 58abd724537..8c2b4b35ac8 100644 --- a/chromium/ui/aura/local/window_port_local.h +++ b/chromium/ui/aura/local/window_port_local.h @@ -47,6 +47,9 @@ class AURA_EXPORT WindowPortLocal : public WindowPort { std::unique_ptr<ui::PropertyData> data) override; std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink() override; void AllocateLocalSurfaceId() override; + bool IsLocalSurfaceIdAllocationSuppressed() const override; + viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) override; const viz::LocalSurfaceId& GetLocalSurfaceId() override; void OnEventTargetingPolicyChanged() override; bool ShouldRestackTransientChildren() override; diff --git a/chromium/ui/aura/mus/client_surface_embedder.cc b/chromium/ui/aura/mus/client_surface_embedder.cc index 7863b68f365..74f31fd2e70 100644 --- a/chromium/ui/aura/mus/client_surface_embedder.cc +++ b/chromium/ui/aura/mus/client_surface_embedder.cc @@ -4,7 +4,6 @@ #include "ui/aura/mus/client_surface_embedder.h" -#include "base/memory/ptr_util.h" #include "ui/aura/window.h" #include "ui/gfx/geometry/dip_util.h" @@ -37,7 +36,8 @@ void ClientSurfaceEmbedder::SetPrimarySurfaceId( const viz::SurfaceId& surface_id) { surface_layer_->SetShowPrimarySurface( surface_id, window_->bounds().size(), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), + false /* stretch_content_to_fill_bounds */); } void ClientSurfaceEmbedder::SetFallbackSurfaceInfo( diff --git a/chromium/ui/aura/mus/drag_drop_controller_mus.cc b/chromium/ui/aura/mus/drag_drop_controller_mus.cc index 532b0364807..fa518e272af 100644 --- a/chromium/ui/aura/mus/drag_drop_controller_mus.cc +++ b/chromium/ui/aura/mus/drag_drop_controller_mus.cc @@ -9,7 +9,6 @@ #include <vector> #include "base/auto_reset.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "mojo/public/cpp/bindings/map.h" diff --git a/chromium/ui/aura/mus/drag_drop_controller_mus_unittest.cc b/chromium/ui/aura/mus/drag_drop_controller_mus_unittest.cc index d5be3749b80..24227cf7076 100644 --- a/chromium/ui/aura/mus/drag_drop_controller_mus_unittest.cc +++ b/chromium/ui/aura/mus/drag_drop_controller_mus_unittest.cc @@ -7,7 +7,6 @@ #include <memory> #include "base/callback_forward.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/task_scheduler/post_task.h" #include "base/threading/thread_task_runner_handle.h" diff --git a/chromium/ui/aura/mus/embed_root.cc b/chromium/ui/aura/mus/embed_root.cc new file mode 100644 index 00000000000..058bacaaeb4 --- /dev/null +++ b/chromium/ui/aura/mus/embed_root.cc @@ -0,0 +1,133 @@ +// 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/embed_root.h" + +#include "base/auto_reset.h" +#include "base/bind.h" +#include "ui/aura/client/focus_change_observer.h" +#include "ui/aura/client/focus_client.h" +#include "ui/aura/mus/embed_root_delegate.h" +#include "ui/aura/mus/window_tree_client.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/aura/window_tracker.h" +#include "ui/aura/window_tree_host.h" + +namespace aura { +namespace { + +// FocusClient implementation used for embedded windows. This has minimal +// checks as to what can get focus. +class EmbeddedFocusClient : public client::FocusClient, public WindowObserver { + public: + explicit EmbeddedFocusClient(Window* root) : root_(root) { + client::SetFocusClient(root, this); + } + + ~EmbeddedFocusClient() override { + client::SetFocusClient(root_, nullptr); + if (focused_window_) + focused_window_->RemoveObserver(this); + } + + // client::FocusClient: + void AddObserver(client::FocusChangeObserver* observer) override { + observers_.AddObserver(observer); + } + void RemoveObserver(client::FocusChangeObserver* observer) override { + observers_.RemoveObserver(observer); + } + void FocusWindow(Window* window) override { + if (IsValidWindowForFocus(window) && window != GetFocusedWindow()) + FocusWindowImpl(window); + } + void ResetFocusWithinActiveWindow(Window* window) override { + // This is never called in the embedding case. + NOTREACHED(); + } + Window* GetFocusedWindow() override { return focused_window_; } + + private: + bool IsValidWindowForFocus(Window* window) const { + return !window || (root_->Contains(window) && window->CanFocus()); + } + + void FocusWindowImpl(Window* window) { + Window* previously_focused_window = focused_window_; + + if (previously_focused_window) + previously_focused_window->RemoveObserver(this); + focused_window_ = window; + if (focused_window_) + focused_window_->AddObserver(this); + + WindowTracker window_tracker; + if (previously_focused_window) + window_tracker.Add(previously_focused_window); + for (auto& observer : observers_) { + observer.OnWindowFocused( + focused_window_, window_tracker.Contains(previously_focused_window) + ? previously_focused_window + : nullptr); + } + } + + // WindowObserver: + void OnWindowDestroying(Window* window) override { + DCHECK_EQ(window, focused_window_); + } + + // Root of the hierarchy this is the FocusClient for. + Window* const root_; + + Window* focused_window_ = nullptr; + + base::ObserverList<client::FocusChangeObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(EmbeddedFocusClient); +}; + +} // namespace + +EmbedRoot::~EmbedRoot() { + window_tree_client_->OnEmbedRootDestroyed(this); + // Makes use of window_tree_host_->window(), so needs to be destroyed before + // |window_tree_host_|. + focus_client_.reset(); +} + +Window* EmbedRoot::window() { + return window_tree_host_ ? window_tree_host_->window() : nullptr; +} + +EmbedRoot::EmbedRoot(WindowTreeClient* window_tree_client, + EmbedRootDelegate* delegate, + ui::ClientSpecificId window_id) + : window_tree_client_(window_tree_client), + delegate_(delegate), + weak_factory_(this) { + window_tree_client_->tree_->ScheduleEmbedForExistingClient( + window_id, base::BindOnce(&EmbedRoot::OnScheduledEmbedForExistingClient, + weak_factory_.GetWeakPtr())); +} + +void EmbedRoot::OnScheduledEmbedForExistingClient( + const base::UnguessableToken& token) { + token_ = token; + delegate_->OnEmbedTokenAvailable(token); +} + +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); + delegate_->OnEmbed(window()); +} + +void EmbedRoot::OnUnembed() { + delegate_->OnUnembed(); +} + +} // namespace aura diff --git a/chromium/ui/aura/mus/embed_root.h b/chromium/ui/aura/mus/embed_root.h new file mode 100644 index 00000000000..f1aa46dffb7 --- /dev/null +++ b/chromium/ui/aura/mus/embed_root.h @@ -0,0 +1,80 @@ +// 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_EMBED_ROOT_H_ +#define UI_AURA_MUS_EMBED_ROOT_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/unguessable_token.h" +#include "services/ui/common/types.h" +#include "ui/aura/aura_export.h" + +namespace aura { + +class EmbedRootDelegate; +class Window; +class WindowTreeClient; +class WindowTreeHost; + +namespace client { +class FocusClient; +} + +// EmbedRoot represents a secondary embedding from the perspective of the +// embedded client. More specifically an EmbedRoot allows a remote client to +// embed this client in one of the remote client's Windows. +// +// See EmbedRootDelegate for details on how to use. +// +// EmbedRoot is created by way of WindowTreeClient::CreateEmbedRoot(). +class AURA_EXPORT EmbedRoot { + public: + ~EmbedRoot(); + + // Token for the embedding. Empty until OnEmbedTokenAvailable() is called on + // the delegate. + const base::UnguessableToken& token() const { return token_; } + + // Window for the embedding, null until the OnEmbed() is called on the + // delegate. + aura::Window* window(); + + private: + friend class WindowTreeClient; + friend class WindowTreeClientPrivate; + + EmbedRoot(WindowTreeClient* window_tree_client, + EmbedRootDelegate* delegate, + ui::ClientSpecificId window_id); + + // Callback from WindowTreeClient once the token has been determined. + void OnScheduledEmbedForExistingClient(const base::UnguessableToken& token); + + // Called from WindowTreeClient when the embedding is established. + void OnEmbed(std::unique_ptr<WindowTreeHost> window_tree_host); + + // Called from WindowTreeClient when unembedded from the Window. + void OnUnembed(); + + WindowTreeClient* window_tree_client_; + + EmbedRootDelegate* delegate_; + + base::UnguessableToken token_; + + std::unique_ptr<client::FocusClient> focus_client_; + + std::unique_ptr<WindowTreeHost> window_tree_host_; + + base::WeakPtrFactory<EmbedRoot> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(EmbedRoot); +}; + +} // namespace aura + +#endif // UI_AURA_MUS_EMBED_ROOT_H_ diff --git a/chromium/ui/aura/mus/embed_root_delegate.h b/chromium/ui/aura/mus/embed_root_delegate.h new file mode 100644 index 00000000000..e2c6cd49e77 --- /dev/null +++ b/chromium/ui/aura/mus/embed_root_delegate.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_AURA_MUS_EMBED_ROOT_DELEGATE_H_ +#define UI_AURA_MUS_EMBED_ROOT_DELEGATE_H_ + +#include "ui/aura/aura_export.h" + +namespace base { +class UnguessableToken; +} + +namespace aura { + +class Window; + +// Called from EmbedRoot at key points during the life-time of the embedding. +class AURA_EXPORT EmbedRootDelegate { + public: + // Called first in the process of establishing an embedding. |token| is used + // by a remote client to embed this client in one of the remote client's + // windows. The delegate will typically pass the supplied token over mojo and + // the remote client will then call WindowTreeClient::EmbedUsingToken(). + virtual void OnEmbedTokenAvailable(const base::UnguessableToken& token) = 0; + + // Called once the embedding has been established. |window| is the root of + // the embedding and owned by the remote client. + virtual void OnEmbed(Window* window) = 0; + + // Called if the remote client embeds another client in the root. The delegate + // will typically delete the EmbedRoot shortly after receiving this. + virtual void OnUnembed() = 0; + + protected: + virtual ~EmbedRootDelegate() {} +}; + +} // namespace aura + +#endif // UI_AURA_MUS_EMBED_ROOT_DELEGATE_H_ diff --git a/chromium/ui/aura/mus/focus_synchronizer.h b/chromium/ui/aura/mus/focus_synchronizer.h index b2908cb7ce9..ebacfcea6b4 100644 --- a/chromium/ui/aura/mus/focus_synchronizer.h +++ b/chromium/ui/aura/mus/focus_synchronizer.h @@ -27,7 +27,7 @@ namespace client { class FocusClient; } -// FocusSynchronizer is resonsible for keeping focus in sync between aura +// FocusSynchronizer is responsible for keeping focus in sync between aura // and the mus server. FocusSynchronizer may be configured in two distinct // ways: // . SetSingletonFocusClient(). Use this when a single FocusClient is shared diff --git a/chromium/ui/aura/mus/input_method_mus.cc b/chromium/ui/aura/mus/input_method_mus.cc index bfd2555a828..ef87de618fa 100644 --- a/chromium/ui/aura/mus/input_method_mus.cc +++ b/chromium/ui/aura/mus/input_method_mus.cc @@ -6,7 +6,6 @@ #include <utility> -#include "base/memory/ptr_util.h" #include "services/ui/public/interfaces/constants.mojom.h" #include "services/ui/public/interfaces/ime/ime.mojom.h" #include "services/ui/public/interfaces/window_tree_constants.mojom.h" @@ -45,7 +44,7 @@ void InputMethodMus::Init(service_manager::Connector* connector) { ui::EventDispatchDetails InputMethodMus::DispatchKeyEvent( ui::KeyEvent* event, - std::unique_ptr<EventResultCallback> ack_callback) { + EventResultCallback ack_callback) { DCHECK(event->type() == ui::ET_KEY_PRESSED || event->type() == ui::ET_KEY_RELEASED); @@ -53,8 +52,9 @@ ui::EventDispatchDetails InputMethodMus::DispatchKeyEvent( if (!GetTextInputClient()) { ui::EventDispatchDetails dispatch_details = DispatchKeyEventPostIME(event); if (ack_callback) { - ack_callback->Run(event->handled() ? EventResult::HANDLED - : EventResult::UNHANDLED); + std::move(ack_callback) + .Run(event->handled() ? EventResult::HANDLED + : EventResult::UNHANDLED); } return dispatch_details; } @@ -75,15 +75,9 @@ void InputMethodMus::OnBlur() { UpdateTextInputType(); } -bool InputMethodMus::OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) { - // This method is not called on non-Windows platforms. See the comments for - // ui::InputMethod::OnUntranslatedIMEMessage(). - return false; -} - ui::EventDispatchDetails InputMethodMus::DispatchKeyEvent(ui::KeyEvent* event) { - ui::EventDispatchDetails dispatch_details = DispatchKeyEvent(event, nullptr); + ui::EventDispatchDetails dispatch_details = + DispatchKeyEvent(event, EventResultCallback()); // Mark the event as handled so that EventGenerator doesn't attempt to // deliver event as well. event->SetHandled(); @@ -130,7 +124,7 @@ bool InputMethodMus::IsCandidatePopupOpen() const { ui::EventDispatchDetails InputMethodMus::SendKeyEventToInputMethod( const ui::KeyEvent& event, - std::unique_ptr<EventResultCallback> ack_callback) { + EventResultCallback ack_callback) { if (!input_method_) { // This code path is hit in tests that don't connect to the server. DCHECK(!ack_callback); @@ -143,8 +137,8 @@ ui::EventDispatchDetails InputMethodMus::SendKeyEventToInputMethod( pending_callbacks_.push_back(std::move(ack_callback)); input_method_->ProcessKeyEvent( ui::Event::Clone(event), - base::Bind(&InputMethodMus::ProcessKeyEventCallback, - base::Unretained(this), event)); + base::BindOnce(&InputMethodMus::ProcessKeyEventCallback, + base::Unretained(this), event)); return ui::EventDispatchDetails(); } @@ -197,9 +191,9 @@ void InputMethodMus::UpdateTextInputType() { } void InputMethodMus::AckPendingCallbacksUnhandled() { - for (auto& callback_ptr : pending_callbacks_) { - if (callback_ptr) - callback_ptr->Run(EventResult::UNHANDLED); + for (auto& callback : pending_callbacks_) { + if (callback) + std::move(callback).Run(EventResult::UNHANDLED); } pending_callbacks_.clear(); } @@ -210,15 +204,16 @@ void InputMethodMus::ProcessKeyEventCallback( // Remove the callback as DispatchKeyEventPostIME() may lead to calling // AckPendingCallbacksUnhandled(), which mutates |pending_callbacks_|. DCHECK(!pending_callbacks_.empty()); - std::unique_ptr<EventResultCallback> ack_callback = - std::move(pending_callbacks_.front()); + 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) - ack_callback->Run(handled ? EventResult::HANDLED : EventResult::UNHANDLED); + if (ack_callback) { + std::move(ack_callback) + .Run(handled ? EventResult::HANDLED : EventResult::UNHANDLED); + } } } // namespace aura diff --git a/chromium/ui/aura/mus/input_method_mus.h b/chromium/ui/aura/mus/input_method_mus.h index 027c7331ef1..8a4956469d1 100644 --- a/chromium/ui/aura/mus/input_method_mus.h +++ b/chromium/ui/aura/mus/input_method_mus.h @@ -27,21 +27,19 @@ class Window; class AURA_EXPORT InputMethodMus : public ui::InputMethodBase { public: - using EventResultCallback = base::Callback<void(ui::mojom::EventResult)>; + using EventResultCallback = base::OnceCallback<void(ui::mojom::EventResult)>; InputMethodMus(ui::internal::InputMethodDelegate* delegate, Window* window); ~InputMethodMus() override; void Init(service_manager::Connector* connector); - ui::EventDispatchDetails DispatchKeyEvent( - ui::KeyEvent* event, - std::unique_ptr<EventResultCallback> ack_callback) WARN_UNUSED_RESULT; + ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event, + EventResultCallback ack_callback) + WARN_UNUSED_RESULT; // Overridden from ui::InputMethod: void OnFocus() override; void OnBlur() override; - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) override; ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; void OnTextInputTypeChanged(const ui::TextInputClient* client) override; void OnCaretBoundsChanged(const ui::TextInputClient* client) override; @@ -56,7 +54,7 @@ class AURA_EXPORT InputMethodMus : public ui::InputMethodBase { // Called from DispatchKeyEvent() to call to the InputMethod. ui::EventDispatchDetails SendKeyEventToInputMethod( const ui::KeyEvent& event, - std::unique_ptr<EventResultCallback> ack_callback) WARN_UNUSED_RESULT; + EventResultCallback ack_callback) WARN_UNUSED_RESULT; // Overridden from ui::InputMethodBase: void OnDidChangeFocusedClient(ui::TextInputClient* focused_before, @@ -89,7 +87,7 @@ class AURA_EXPORT InputMethodMus : public ui::InputMethodBase { // Callbacks supplied to DispatchKeyEvent() are added here while awaiting // the response from the server. These are removed when the response is // received (ProcessKeyEventCallback()). - base::circular_deque<std::unique_ptr<EventResultCallback>> pending_callbacks_; + base::circular_deque<EventResultCallback> pending_callbacks_; DISALLOW_COPY_AND_ASSIGN(InputMethodMus); }; diff --git a/chromium/ui/aura/mus/input_method_mus_unittest.cc b/chromium/ui/aura/mus/input_method_mus_unittest.cc index 582e0a2b41a..fa7cefb2614 100644 --- a/chromium/ui/aura/mus/input_method_mus_unittest.cc +++ b/chromium/ui/aura/mus/input_method_mus_unittest.cc @@ -33,7 +33,7 @@ class TestInputMethodDelegate : public ui::internal::InputMethodDelegate { using ProcessKeyEventCallback = base::OnceCallback<void(bool)>; using ProcessKeyEventCallbacks = std::vector<ProcessKeyEventCallback>; -using EventResultCallback = base::Callback<void(ui::mojom::EventResult)>; +using EventResultCallback = base::OnceCallback<void(ui::mojom::EventResult)>; // InputMethod implementation that queues up the callbacks supplied to // ProcessKeyEvent(). @@ -104,9 +104,8 @@ TEST_F(InputMethodMusTest, PendingCallbackRunFromDestruction) { TestInputMethod test_input_method; InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); - std::unique_ptr<EventResultCallback> callback = - std::make_unique<EventResultCallback>(base::Bind( - &RunFunctionWithEventResult, &was_event_result_callback_run)); + EventResultCallback callback = base::BindOnce( + &RunFunctionWithEventResult, &was_event_result_callback_run); ui::EventDispatchDetails details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( @@ -118,7 +117,7 @@ TEST_F(InputMethodMusTest, PendingCallbackRunFromDestruction) { // Add a null callback as well, to make sure null is deal with. details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( &input_method_mus, ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0), - nullptr); + InputMethodMus::EventResultCallback()); ASSERT_TRUE(!details.dispatcher_destroyed && !details.target_destroyed); // The event should have been queued. EXPECT_EQ(2u, test_input_method.process_key_event_callbacks()->size()); @@ -140,9 +139,8 @@ TEST_F(InputMethodMusTest, PendingCallbackRunFromOnDidChangeFocusedClient) { InputMethodMus input_method_mus(&input_method_delegate, &window); TestInputMethod test_input_method; InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); - std::unique_ptr<EventResultCallback> callback = - std::make_unique<EventResultCallback>(base::Bind( - &RunFunctionWithEventResult, &was_event_result_callback_run)); + EventResultCallback callback = base::BindOnce(&RunFunctionWithEventResult, + &was_event_result_callback_run); ui::EventDispatchDetails details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( &input_method_mus, @@ -206,9 +204,8 @@ TEST_F(InputMethodMusTest, ChangeTextInputTypeWhileProcessingCallback) { &test_input_client); TestInputMethod test_input_method; InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); - std::unique_ptr<EventResultCallback> callback = - std::make_unique<EventResultCallback>(base::Bind( - &RunFunctionWithEventResult, &was_event_result_callback_run)); + EventResultCallback callback = base::BindOnce(&RunFunctionWithEventResult, + &was_event_result_callback_run); const ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0); ui::EventDispatchDetails details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( diff --git a/chromium/ui/aura/mus/mus_context_factory.cc b/chromium/ui/aura/mus/mus_context_factory.cc index f2b4dc0c60e..73badbb19db 100644 --- a/chromium/ui/aura/mus/mus_context_factory.cc +++ b/chromium/ui/aura/mus/mus_context_factory.cc @@ -5,7 +5,6 @@ #include "ui/aura/mus/mus_context_factory.h" #include "base/command_line.h" -#include "base/memory/ptr_util.h" #include "cc/base/switches.h" #include "components/viz/common/gpu/context_provider.h" #include "components/viz/host/renderer_settings_creation.h" diff --git a/chromium/ui/aura/mus/os_exchange_data_provider_mus_unittest.cc b/chromium/ui/aura/mus/os_exchange_data_provider_mus_unittest.cc index 7ce7c71f376..225a515a754 100644 --- a/chromium/ui/aura/mus/os_exchange_data_provider_mus_unittest.cc +++ b/chromium/ui/aura/mus/os_exchange_data_provider_mus_unittest.cc @@ -7,7 +7,6 @@ #include <memory> #include "base/files/file_util.h" -#include "base/memory/ptr_util.h" #include "base/pickle.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_task_environment.h" diff --git a/chromium/ui/aura/mus/platform_event_source_mus_ozone.cc b/chromium/ui/aura/mus/platform_event_source_mus_ozone.cc new file mode 100644 index 00000000000..dfd4133673b --- /dev/null +++ b/chromium/ui/aura/mus/platform_event_source_mus_ozone.cc @@ -0,0 +1,26 @@ +// 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/platform_event_source_mus_ozone.h" + +#include "ui/events/event.h" +#include "ui/events/platform/platform_event_observer.h" + +namespace aura { + +PlatformEventSourceMus::PlatformEventSourceMus() = default; + +PlatformEventSourceMus::~PlatformEventSourceMus() = default; + +void PlatformEventSourceMus::OnWillProcessEvent(ui::Event* event) { + for (ui::PlatformEventObserver& observer : observers()) + observer.WillProcessEvent(event); +} + +void PlatformEventSourceMus::OnDidProcessEvent(ui::Event* event) { + for (ui::PlatformEventObserver& observer : observers()) + observer.DidProcessEvent(event); +} + +} // namespace aura diff --git a/chromium/ui/aura/mus/platform_event_source_mus_ozone.h b/chromium/ui/aura/mus/platform_event_source_mus_ozone.h new file mode 100644 index 00000000000..8557688c8d7 --- /dev/null +++ b/chromium/ui/aura/mus/platform_event_source_mus_ozone.h @@ -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. + +#ifndef UI_AURA_MUS_PLATFORM_EVENT_SOURCE_MUS_OZONE_H_ +#define UI_AURA_MUS_PLATFORM_EVENT_SOURCE_MUS_OZONE_H_ + +#include "ui/events/platform/platform_event_source.h" + +namespace ui { +class Event; +} + +namespace aura { + +// PlatformEventSource implementation for mus with ozone. WindowTreeClient owns +// and installs this. WindowTreeClient calls this to notify observers as +// necessary. +class PlatformEventSourceMus : public ui::PlatformEventSource { + public: + PlatformEventSourceMus(); + ~PlatformEventSourceMus() override; + + // These two functions are called from WindowTreeClient before/after + // dispatching events. They forward to observers. + void OnWillProcessEvent(ui::Event* event); + void OnDidProcessEvent(ui::Event* event); + + private: + DISALLOW_COPY_AND_ASSIGN(PlatformEventSourceMus); +}; + +} // namespace aura + +#endif // UI_AURA_MUS_PLATFORM_EVENT_SOURCE_MUS_OZONE_H_ diff --git a/chromium/ui/aura/mus/property_converter.cc b/chromium/ui/aura/mus/property_converter.cc index 7a0a67e0177..ae2ce3a787d 100644 --- a/chromium/ui/aura/mus/property_converter.cc +++ b/chromium/ui/aura/mus/property_converter.cc @@ -4,7 +4,6 @@ #include "ui/aura/mus/property_converter.h" -#include "base/memory/ptr_util.h" #include "mojo/public/cpp/bindings/type_converter.h" #include "services/ui/public/cpp/property_type_converters.h" #include "services/ui/public/interfaces/window_manager.mojom.h" diff --git a/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc b/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc index 9d011c0e723..249cc0ad150 100644 --- a/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc +++ b/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc @@ -8,7 +8,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "services/ui/common/task_runner_test_base.h" diff --git a/chromium/ui/aura/mus/window_port_mus.cc b/chromium/ui/aura/mus/window_port_mus.cc index 45dd67e18ef..19596818882 100644 --- a/chromium/ui/aura/mus/window_port_mus.cc +++ b/chromium/ui/aura/mus/window_port_mus.cc @@ -4,7 +4,6 @@ #include "ui/aura/mus/window_port_mus.h" -#include "base/memory/ptr_util.h" #include "components/viz/client/local_surface_id_provider.h" #include "components/viz/host/host_frame_sink_manager.h" #include "ui/aura/client/aura_constants.h" @@ -96,11 +95,19 @@ void WindowPortMus::SetHitTestMask(const base::Optional<gfx::Rect>& rect) { window_tree_client_->SetHitTestMask(this, rect); } -void WindowPortMus::Embed( - ui::mojom::WindowTreeClientPtr client, +void WindowPortMus::Embed(ui::mojom::WindowTreeClientPtr client, + uint32_t flags, + ui::mojom::WindowTree::EmbedCallback callback) { + window_tree_client_->Embed(window_, std::move(client), flags, + std::move(callback)); +} + +void WindowPortMus::EmbedUsingToken( + const base::UnguessableToken& token, uint32_t flags, - const ui::mojom::WindowTree::EmbedCallback& callback) { - window_tree_client_->Embed(window_, std::move(client), flags, callback); + ui::mojom::WindowTree::EmbedCallback callback) { + window_tree_client_->EmbedUsingToken(window_, token, flags, + std::move(callback)); } std::unique_ptr<viz::ClientLayerTreeFrameSink> @@ -409,6 +416,16 @@ void WindowPortMus::AllocateLocalSurfaceId() { UpdatePrimarySurfaceId(); } +bool WindowPortMus::IsLocalSurfaceIdAllocationSuppressed() const { + return parent_local_surface_id_allocator_.is_allocation_suppressed(); +} + +viz::ScopedSurfaceIdAllocator WindowPortMus::GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) { + return viz::ScopedSurfaceIdAllocator(&parent_local_surface_id_allocator_, + std::move(allocation_task)); +} + const viz::LocalSurfaceId& WindowPortMus::GetLocalSurfaceId() { if (base::FeatureList::IsEnabled(features::kMash)) return local_surface_id_; @@ -647,7 +664,8 @@ void WindowPortMus::OnSurfaceChanged(const viz::SurfaceInfo& surface_info) { DCHECK_EQ(surface_info.id().local_surface_id(), local_surface_id_); window_->layer()->SetShowPrimarySurface( surface_info.id(), window_->bounds().size(), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), + false /* stretch_content_to_fill_bounds */); window_->layer()->SetFallbackSurfaceId(surface_info.id()); } diff --git a/chromium/ui/aura/mus/window_port_mus.h b/chromium/ui/aura/mus/window_port_mus.h index 8228ff89d3c..b1b272a4408 100644 --- a/chromium/ui/aura/mus/window_port_mus.h +++ b/chromium/ui/aura/mus/window_port_mus.h @@ -93,7 +93,10 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { // details on arguments. void Embed(ui::mojom::WindowTreeClientPtr client, uint32_t flags, - const ui::mojom::WindowTree::EmbedCallback& callback); + ui::mojom::WindowTree::EmbedCallback callback); + void EmbedUsingToken(const base::UnguessableToken& token, + uint32_t flags, + ui::mojom::WindowTree::EmbedCallback callback); std::unique_ptr<viz::ClientLayerTreeFrameSink> RequestLayerTreeFrameSink( scoped_refptr<viz::ContextProvider> context_provider, @@ -273,6 +276,9 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { std::unique_ptr<ui::PropertyData> data) override; std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink() override; void AllocateLocalSurfaceId() override; + bool IsLocalSurfaceIdAllocationSuppressed() const override; + viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) override; const viz::LocalSurfaceId& GetLocalSurfaceId() override; void OnEventTargetingPolicyChanged() override; bool ShouldRestackTransientChildren() override; diff --git a/chromium/ui/aura/mus/window_tree_client.cc b/chromium/ui/aura/mus/window_tree_client.cc index a75ee126a46..4bbe28f100e 100644 --- a/chromium/ui/aura/mus/window_tree_client.cc +++ b/chromium/ui/aura/mus/window_tree_client.cc @@ -38,6 +38,8 @@ #include "ui/aura/env_input_state_controller.h" #include "ui/aura/mus/capture_synchronizer.h" #include "ui/aura/mus/drag_drop_controller_mus.h" +#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/in_flight_change.h" #include "ui/aura/mus/input_method_mus.h" @@ -68,6 +70,10 @@ #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/size.h" +#if defined(USE_OZONE) +#include "ui/aura/mus/platform_event_source_mus_ozone.h" +#endif + namespace aura { namespace { @@ -80,7 +86,7 @@ struct WindowPortPropertyDataMus : public ui::PropertyData { // message loop starts, or upon destruction. class EventAckHandler : public base::RunLoop::NestingObserver { public: - explicit EventAckHandler(std::unique_ptr<EventResultCallback> ack_callback) + explicit EventAckHandler(EventResultCallback ack_callback) : ack_callback_(std::move(ack_callback)) { DCHECK(ack_callback_); base::RunLoop::AddNestingObserverOnCurrentThread(this); @@ -89,11 +95,22 @@ class EventAckHandler : public base::RunLoop::NestingObserver { ~EventAckHandler() override { base::RunLoop::RemoveNestingObserverOnCurrentThread(this); if (ack_callback_) { - ack_callback_->Run(handled_ ? ui::mojom::EventResult::HANDLED - : ui::mojom::EventResult::UNHANDLED); + NotifyPlatformEventSource(); + std::move(ack_callback_) + .Run(handled_ ? ui::mojom::EventResult::HANDLED + : ui::mojom::EventResult::UNHANDLED); } } +#if defined(USE_OZONE) + void SetPlatformEventSourceAndEvent( + PlatformEventSourceMus* platform_event_source, + ui::Event* event) { + event_ = event; + platform_event_source_ = platform_event_source; + } +#endif + void set_handled(bool handled) { handled_ = handled; } // base::RunLoop::NestingObserver: @@ -101,14 +118,25 @@ class EventAckHandler : public base::RunLoop::NestingObserver { // Acknowledge the event immediately if a nested run loop starts. // Otherwise we appear unresponsive for the life of the nested run loop. if (ack_callback_) { - ack_callback_->Run(ui::mojom::EventResult::HANDLED); - ack_callback_.reset(); + NotifyPlatformEventSource(); + std::move(ack_callback_).Run(ui::mojom::EventResult::HANDLED); } } private: - std::unique_ptr<EventResultCallback> ack_callback_; + void NotifyPlatformEventSource() { +#if defined(USE_OZONE) + if (platform_event_source_) + platform_event_source_->OnDidProcessEvent(event_); +#endif + } + + EventResultCallback ack_callback_; bool handled_ = false; +#if defined(USE_OZONE) + ui::Event* event_ = nullptr; + PlatformEventSourceMus* platform_event_source_ = nullptr; +#endif DISALLOW_COPY_AND_ASSIGN(EventAckHandler); }; @@ -174,66 +202,108 @@ ui::Id GetServerIdForWindow(Window* window) { return window ? WindowMus::Get(window)->server_id() : kInvalidServerId; } +// The server operates in pixels. Adjust translations for device scale factors. +gfx::Transform ConvertTransformFromServer(WindowMus* window, + const gfx::Transform& transform) { + const float scale = window->GetDeviceScaleFactor(); + if (scale == 1.0f) + return transform; + + gfx::Transform dip_transform = transform; + dip_transform.matrix().set(0, 3, dip_transform.matrix().get(0, 3) / scale); + dip_transform.matrix().set(1, 3, dip_transform.matrix().get(1, 3) / scale); + dip_transform.matrix().set(2, 3, dip_transform.matrix().get(2, 3) / scale); + return dip_transform; +} + +// See the comment for ConvertTransformFromServer(). +gfx::Transform ConvertTransformToServer(WindowMus* window, + const gfx::Transform& transform) { + const float scale = window->GetDeviceScaleFactor(); + if (scale == 1.0f) + return transform; + + gfx::Transform pixel_transform = transform; + pixel_transform.matrix().set(0, 3, transform.matrix().get(0, 3) * scale); + pixel_transform.matrix().set(1, 3, transform.matrix().get(1, 3) * scale); + pixel_transform.matrix().set(2, 3, transform.matrix().get(2, 3) * scale); + return pixel_transform; +} + } // namespace -WindowTreeClient::WindowTreeClient( +// static +std::unique_ptr<WindowTreeClient> WindowTreeClient::CreateForWindowManager( service_manager::Connector* connector, WindowTreeClientDelegate* delegate, WindowManagerDelegate* window_manager_delegate, - mojo::InterfaceRequest<ui::mojom::WindowTreeClient> request, - scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, - bool create_discardable_memory) - : connector_(connector), - next_window_id_(1), - next_change_id_(1), - delegate_(delegate), - window_manager_delegate_(window_manager_delegate), - binding_(this), - tree_(nullptr), - in_destructor_(false), - weak_factory_(this) { - DCHECK(delegate_); - // Allow for a null request in tests. - if (request.is_pending()) - binding_.Bind(std::move(request)); - // Some tests may not create a TransientWindowClient. - if (client::GetTransientWindowClient()) - client::GetTransientWindowClient()->AddObserver(this); - if (window_manager_delegate) - window_manager_delegate->SetWindowManagerClient(this); - if (connector) { // |connector| can be null in tests. - if (!io_task_runner) { - // |io_task_runner| is null in most case. But for the browser process, - // the |io_task_runner| is the browser's IO thread. - io_thread_ = std::make_unique<base::Thread>("IOThread"); - base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0); - thread_options.priority = base::ThreadPriority::NORMAL; - CHECK(io_thread_->StartWithOptions(thread_options)); - io_task_runner = io_thread_->task_runner(); - } + bool automatically_create_display_roots, + bool create_discardable_memory) { + std::unique_ptr<WindowTreeClient> wtc( + new WindowTreeClient(connector, delegate, window_manager_delegate, + nullptr, nullptr, create_discardable_memory)); - if (base::FeatureList::IsEnabled(features::kMash)) { - gpu_ = - ui::Gpu::Create(connector, ui::mojom::kServiceName, io_task_runner); - compositor_context_factory_ = - std::make_unique<MusContextFactory>(gpu_.get()); - initial_context_factory_ = Env::GetInstance()->context_factory(); - Env::GetInstance()->set_context_factory( - compositor_context_factory_.get()); - } + ui::mojom::WindowManagerWindowTreeFactoryPtr factory; + connector->BindInterface(ui::mojom::kServiceName, &factory); + ui::mojom::WindowTreePtr window_tree; + ui::mojom::WindowTreeClientPtr client; + wtc->binding_.Bind(MakeRequest(&client)); + factory->CreateWindowTree(MakeRequest(&window_tree), std::move(client), + automatically_create_display_roots); + wtc->SetWindowTree(std::move(window_tree)); + wtc->CreatePlatformEventSourceIfNecessary(); + return wtc; +} - // WindowServerTest will create more than one WindowTreeClient. We will not - // create the discardable memory manager for those tests. - if (create_discardable_memory) { - discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr; - connector->BindInterface(ui::mojom::kServiceName, &manager_ptr); - discardable_shared_memory_manager_ = std::make_unique< - discardable_memory::ClientDiscardableSharedMemoryManager>( - std::move(manager_ptr), std::move(io_task_runner)); - base::DiscardableMemoryAllocator::SetInstance( - discardable_shared_memory_manager_.get()); - } - } +// static +std::unique_ptr<WindowTreeClient> WindowTreeClient::CreateForEmbedding( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + ui::mojom::WindowTreeClientRequest request, + bool create_discardable_memory) { + std::unique_ptr<WindowTreeClient> wtc( + new WindowTreeClient(connector, delegate, nullptr, std::move(request), + nullptr, create_discardable_memory)); + return wtc; +} + +// static +std::unique_ptr<WindowTreeClient> WindowTreeClient::CreateForWindowTreeFactory( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + bool create_discardable_memory, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) { + std::unique_ptr<WindowTreeClient> wtc( + new WindowTreeClient(connector, delegate, nullptr, nullptr, nullptr, + create_discardable_memory)); + ui::mojom::WindowTreeFactoryPtr factory; + connector->BindInterface(ui::mojom::kServiceName, &factory); + ui::mojom::WindowTreePtr window_tree; + ui::mojom::WindowTreeClientPtr client; + wtc->binding_.Bind(MakeRequest(&client)); + factory->CreateWindowTree(MakeRequest(&window_tree), std::move(client)); + wtc->SetWindowTree(std::move(window_tree)); + return wtc; +} + +// static +std::unique_ptr<WindowTreeClient> +WindowTreeClient::CreateForWindowTreeHostFactory( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + bool create_discardable_memory) { + std::unique_ptr<WindowTreeClient> wtc( + new WindowTreeClient(connector, delegate, nullptr, nullptr, nullptr, + create_discardable_memory)); + ui::mojom::WindowTreeHostFactoryPtr factory; + connector->BindInterface(ui::mojom::kServiceName, &factory); + + ui::mojom::WindowTreeHostPtr window_tree_host; + ui::mojom::WindowTreeClientPtr client; + wtc->binding_.Bind(MakeRequest(&client)); + factory->CreateWindowTreeHost(MakeRequest(&window_tree_host), + std::move(client)); + return wtc; } WindowTreeClient::~WindowTreeClient() { @@ -278,46 +348,14 @@ WindowTreeClient::~WindowTreeClient() { for (auto& pair : windows) WindowPortForShutdown::Install(pair.second->GetWindow()); + // EmbedRoots keep a reference to this; so they must all be destroyed before + // the destructor completes. + DCHECK(embed_roots_.empty()); + env->WindowTreeClientDestroyed(this); CHECK(windows_.empty()); } -void WindowTreeClient::ConnectViaWindowTreeFactory() { - ui::mojom::WindowTreeFactoryPtr factory; - connector_->BindInterface(ui::mojom::kServiceName, &factory); - ui::mojom::WindowTreePtr window_tree; - ui::mojom::WindowTreeClientPtr client; - binding_.Bind(MakeRequest(&client)); - factory->CreateWindowTree(MakeRequest(&window_tree), std::move(client)); - SetWindowTree(std::move(window_tree)); -} - -void WindowTreeClient::ConnectAsWindowManager( - bool automatically_create_display_roots) { - DCHECK(window_manager_delegate_); - - ui::mojom::WindowManagerWindowTreeFactoryPtr factory; - connector_->BindInterface(ui::mojom::kServiceName, &factory); - ui::mojom::WindowTreePtr window_tree; - ui::mojom::WindowTreeClientPtr client; - binding_.Bind(MakeRequest(&client)); - factory->CreateWindowTree(MakeRequest(&window_tree), std::move(client), - automatically_create_display_roots); - SetWindowTree(std::move(window_tree)); -} - -void WindowTreeClient::ConnectViaWindowTreeHostFactory() { - ui::mojom::WindowTreeHostFactoryPtr factory; - connector_->BindInterface(ui::mojom::kServiceName, &factory); - - ui::mojom::WindowTreeHostPtr window_tree_host; - ui::mojom::WindowTreeClientPtr client; - binding_.Bind(MakeRequest(&client)); - factory->CreateWindowTreeHost(MakeRequest(&window_tree_host), - std::move(client)); - WaitForInitialDisplays(); -} - void WindowTreeClient::SetCanFocus(Window* window, bool can_focus) { DCHECK(tree_); DCHECK(window); @@ -360,11 +398,10 @@ void WindowTreeClient::SetHitTestMask( tree_->SetHitTestMask(window->server_id(), out_rect); } -void WindowTreeClient::Embed( - Window* window, - ui::mojom::WindowTreeClientPtr client, - uint32_t flags, - const ui::mojom::WindowTree::EmbedCallback& callback) { +void WindowTreeClient::Embed(Window* window, + ui::mojom::WindowTreeClientPtr client, + uint32_t flags, + ui::mojom::WindowTree::EmbedCallback callback) { DCHECK(tree_); // Window::Init() must be called before Embed() (otherwise the server hasn't // been told about the window). @@ -373,19 +410,39 @@ void WindowTreeClient::Embed( // The window server removes all children before embedding. In other words, // it's generally an error to Embed() with existing children. So, fail // early. - callback.Run(false); + std::move(callback).Run(false); return; } tree_->Embed(WindowMus::Get(window)->server_id(), std::move(client), flags, - callback); + std::move(callback)); } void WindowTreeClient::ScheduleEmbed( ui::mojom::WindowTreeClientPtr client, base::OnceCallback<void(const base::UnguessableToken&)> callback) { - tree_->ScheduleEmbed(std::move(client), - base::AdaptCallbackForRepeating(std::move(callback))); + tree_->ScheduleEmbed(std::move(client), std::move(callback)); +} + +void WindowTreeClient::EmbedUsingToken( + Window* window, + const base::UnguessableToken& token, + uint32_t flags, + ui::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( @@ -397,6 +454,81 @@ void WindowTreeClient::AttachCompositorFrameSink( std::move(client)); } +std::unique_ptr<EmbedRoot> WindowTreeClient::CreateEmbedRoot( + EmbedRootDelegate* delegate) { + std::unique_ptr<EmbedRoot> embed_root = + base::WrapUnique(new EmbedRoot(this, delegate, next_window_id_++)); + embed_roots_.insert(embed_root.get()); + return embed_root; +} + +WindowTreeClient::WindowTreeClient( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + WindowManagerDelegate* window_manager_delegate, + mojo::InterfaceRequest<ui::mojom::WindowTreeClient> request, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, + bool create_discardable_memory) + : connector_(connector), + next_window_id_(1), + next_change_id_(1), + delegate_(delegate), + window_manager_delegate_(window_manager_delegate), + binding_(this), + tree_(nullptr), + in_destructor_(false), + weak_factory_(this) { + DCHECK(delegate_); + // Allow for a null request in tests. + if (request.is_pending()) + binding_.Bind(std::move(request)); + // Some tests may not create a TransientWindowClient. + if (client::GetTransientWindowClient()) + client::GetTransientWindowClient()->AddObserver(this); + if (window_manager_delegate) + window_manager_delegate->SetWindowManagerClient(this); + if (connector) { // |connector| can be null in tests. + if (!io_task_runner) { + // |io_task_runner| is null in most case. But for the browser process, + // the |io_task_runner| is the browser's IO thread. + io_thread_ = std::make_unique<base::Thread>("IOThread"); + base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0); + thread_options.priority = base::ThreadPriority::NORMAL; + CHECK(io_thread_->StartWithOptions(thread_options)); + io_task_runner = io_thread_->task_runner(); + } + + if (base::FeatureList::IsEnabled(features::kMash)) { + gpu_ = + ui::Gpu::Create(connector, ui::mojom::kServiceName, io_task_runner); + compositor_context_factory_ = + std::make_unique<MusContextFactory>(gpu_.get()); + initial_context_factory_ = Env::GetInstance()->context_factory(); + Env::GetInstance()->set_context_factory( + compositor_context_factory_.get()); + } + + // WindowServerTest will create more than one WindowTreeClient. We will not + // create the discardable memory manager for those tests. + if (create_discardable_memory) { + discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr; + connector->BindInterface(ui::mojom::kServiceName, &manager_ptr); + discardable_shared_memory_manager_ = std::make_unique< + discardable_memory::ClientDiscardableSharedMemoryManager>( + std::move(manager_ptr), std::move(io_task_runner)); + base::DiscardableMemoryAllocator::SetInstance( + discardable_shared_memory_manager_.get()); + } + } +} + +void WindowTreeClient::CreatePlatformEventSourceIfNecessary() { +#if defined(USE_OZONE) + if (!ui::PlatformEventSource::GetInstance()) + platform_event_source_ = std::make_unique<PlatformEventSourceMus>(); +#endif +} + void WindowTreeClient::RegisterWindowMus(WindowMus* window) { DCHECK(windows_.find(window->server_id()) == windows_.end()); windows_[window->server_id()] = window; @@ -649,10 +781,10 @@ void WindowTreeClient::SetWindowTree(ui::mojom::WindowTreePtr window_tree_ptr) { WindowTreeConnectionEstablished(tree_ptr_.get()); tree_ptr_->GetCursorLocationMemory( - base::Bind(&WindowTreeClient::OnReceivedCursorLocationMemory, - weak_factory_.GetWeakPtr())); + base::BindOnce(&WindowTreeClient::OnReceivedCursorLocationMemory, + weak_factory_.GetWeakPtr())); - tree_ptr_.set_connection_error_handler(base::Bind( + tree_ptr_.set_connection_error_handler(base::BindOnce( &WindowTreeClient::OnConnectionLost, weak_factory_.GetWeakPtr())); if (window_manager_delegate_) { @@ -720,6 +852,18 @@ void WindowTreeClient::OnEmbedImpl( delegate_->OnEmbed(std::move(window_tree_host)); } +EmbedRoot* WindowTreeClient::GetEmbedRootWithRootWindow(aura::Window* window) { + for (EmbedRoot* embed_root : embed_roots_) { + if (embed_root->window() == window) + return embed_root; + } + return nullptr; +} + +void WindowTreeClient::OnEmbedRootDestroyed(EmbedRoot* embed_root) { + embed_roots_.erase(embed_root); +} + WindowTreeHostMus* WindowTreeClient::WmNewDisplayAddedImpl( const display::Display& display, ui::mojom::WindowDataPtr root_data, @@ -741,11 +885,10 @@ WindowTreeHostMus* WindowTreeClient::WmNewDisplayAddedImpl( return window_tree_host_ptr; } -std::unique_ptr<EventResultCallback> -WindowTreeClient::CreateEventResultCallback(int32_t event_id) { - return std::make_unique<EventResultCallback>( - base::Bind(&ui::mojom::WindowTree::OnWindowInputEventAck, - base::Unretained(tree_), event_id)); +EventResultCallback WindowTreeClient::CreateEventResultCallback( + int32_t event_id) { + return base::BindOnce(&ui::mojom::WindowTree::OnWindowInputEventAck, + base::Unretained(tree_), event_id); } void WindowTreeClient::OnReceivedCursorLocationMemory( @@ -777,7 +920,7 @@ void WindowTreeClient::SetWindowBoundsFromServer( void WindowTreeClient::SetWindowTransformFromServer( WindowMus* window, const gfx::Transform& transform) { - window->SetTransformFromServer(transform); + window->SetTransformFromServer(ConvertTransformFromServer(window, transform)); } void WindowTreeClient::SetWindowVisibleFromServer(WindowMus* window, @@ -882,7 +1025,7 @@ void WindowTreeClient::OnWindowMusCreated(WindowMus* window) { base::FeatureList::IsEnabled(features::kMash) ? display_init_params->mirrors : std::vector<display::Display>(), - base::Bind(&OnAckMustSucceed, FROM_HERE)); + base::BindOnce(&OnAckMustSucceed, FROM_HERE)); } } } @@ -950,7 +1093,7 @@ void WindowTreeClient::OnWindowMusBoundsChanged(WindowMus* window, return; } - float device_scale_factor = window->GetDeviceScaleFactor(); + const float device_scale_factor = window->GetDeviceScaleFactor(); ScheduleInFlightBoundsChange( window, gfx::ConvertRectToPixel(device_scale_factor, old_bounds), gfx::ConvertRectToPixel(device_scale_factor, new_bounds)); @@ -962,7 +1105,8 @@ void WindowTreeClient::OnWindowMusTransformChanged( const gfx::Transform& new_transform) { const uint32_t change_id = ScheduleInFlightChange( std::make_unique<InFlightTransformChange>(this, window, old_transform)); - tree_->SetWindowTransform(change_id, window->server_id(), new_transform); + tree_->SetWindowTransform(change_id, window->server_id(), + ConvertTransformToServer(window, new_transform)); } void WindowTreeClient::OnWindowMusAddChild(WindowMus* parent, @@ -1175,6 +1319,20 @@ void WindowTreeClient::OnEmbed( focused_window_id, drawn, local_surface_id); } +void WindowTreeClient::OnEmbedFromToken( + const base::UnguessableToken& token, + ui::mojom::WindowDataPtr root, + int64_t display_id, + const base::Optional<viz::LocalSurfaceId>& local_surface_id) { + for (EmbedRoot* embed_root : embed_roots_) { + if (embed_root->token() == token) { + embed_root->OnEmbed(CreateWindowTreeHost(WindowMusType::EMBED, *root, + display_id, local_surface_id)); + break; + } + } +} + void WindowTreeClient::OnEmbeddedAppDisconnected(ui::Id window_id) { WindowMus* window = GetWindowByServerId(window_id); if (window) @@ -1186,6 +1344,13 @@ void WindowTreeClient::OnUnembed(ui::Id window_id) { if (!window) return; + EmbedRoot* embed_root = GetEmbedRootWithRootWindow(window->GetWindow()); + if (embed_root) { + embed_root->OnUnembed(); + if (!GetWindowByServerId(window_id)) + return; // EmbedRoot was deleted, resulting in deleting window. + } + delegate_->OnUnembed(window->GetWindow()); delete window; } @@ -1404,11 +1569,15 @@ void WindowTreeClient::OnWindowDeleted(ui::Id window_id) { if (roots_.count(window)) { // Roots are associated with WindowTreeHosts. The WindowTreeHost owns the // root, so we have to delete the WindowTreeHost to indirectly delete the - // Window. Additionally clients may want to do extra processing before the - // delete, so call to the delegate to handle it. Let the window know it is - // going to be deleted so we don't callback to the server. + // Window. Clients may want to do extra processing before the delete, + // notify the appropriate delegate to handle the deletion. Let the window + // know it is going to be deleted so we don't callback to the server. window->PrepareForDestroy(); - delegate_->OnEmbedRootDestroyed(GetWindowTreeHostMus(window)); + EmbedRoot* embed_root = GetEmbedRootWithRootWindow(window->GetWindow()); + if (embed_root) + embed_root->OnUnembed(); + else + delegate_->OnEmbedRootDestroyed(GetWindowTreeHostMus(window)); } else { window->DestroyFromServer(); } @@ -1516,7 +1685,6 @@ void WindowTreeClient::OnWindowInputEvent( } } - EventAckHandler ack_handler(CreateEventResultCallback(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()); @@ -1532,7 +1700,7 @@ void WindowTreeClient::OnWindowInputEvent( if (mapped_event->type() == ui::ET_MOUSE_MOVED || mapped_event->type() == ui::ET_MOUSE_DRAGGED) { mapped_event_with_native = std::make_unique<ui::MouseEvent>( - static_cast<const base::NativeEvent&>(mapped_event.get())); + static_cast<const ui::PlatformEvent&>(mapped_event.get())); // MouseEvent(NativeEvent) sets the root_location to location. mapped_event_with_native->set_root_location_f( event_location_in_screen_pixel_layout); @@ -1543,6 +1711,13 @@ void WindowTreeClient::OnWindowInputEvent( event_to_dispatch = mapped_event_with_native.get(); } #endif + // |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)); +#if defined(USE_OZONE) + ack_handler.SetPlatformEventSourceAndEvent(platform_event_source_.get(), + event_to_dispatch); +#endif WindowMus* display_root_window = GetWindowByServerId(display_root_window_id); if (display_root_window && event->IsLocatedEvent() && @@ -1562,6 +1737,11 @@ void WindowTreeClient::OnWindowInputEvent( // focused window, which may have changed by the time we process the event. ui::Event::DispatcherApi(event_to_dispatch).set_target(window->GetWindow()); } +#if defined(USE_OZONE) + if (platform_event_source_) + platform_event_source_->OnWillProcessEvent(event_to_dispatch); +#endif + GetWindowTreeHostMus(window)->SendEventToSink(event_to_dispatch); ack_handler.set_handled(event_to_dispatch->handled()); @@ -1630,8 +1810,8 @@ void WindowTreeClient::OnDragEnter(ui::Id window_id, uint32_t key_state, const gfx::Point& position, uint32_t effect_bitmask, - const OnDragEnterCallback& callback) { - callback.Run(drag_drop_controller_->OnDragEnter( + OnDragEnterCallback callback) { + std::move(callback).Run(drag_drop_controller_->OnDragEnter( GetWindowByServerId(window_id), key_state, position, effect_bitmask)); } @@ -1639,8 +1819,8 @@ void WindowTreeClient::OnDragOver(ui::Id window_id, uint32_t key_state, const gfx::Point& position, uint32_t effect_bitmask, - const OnDragOverCallback& callback) { - callback.Run(drag_drop_controller_->OnDragOver( + OnDragOverCallback callback) { + std::move(callback).Run(drag_drop_controller_->OnDragOver( GetWindowByServerId(window_id), key_state, position, effect_bitmask)); } @@ -1656,8 +1836,8 @@ void WindowTreeClient::OnCompleteDrop(ui::Id window_id, uint32_t key_state, const gfx::Point& position, uint32_t effect_bitmask, - const OnCompleteDropCallback& callback) { - callback.Run(drag_drop_controller_->OnCompleteDrop( + OnCompleteDropCallback callback) { + std::move(callback).Run(drag_drop_controller_->OnCompleteDrop( GetWindowByServerId(window_id), key_state, position, effect_bitmask)); } @@ -1716,7 +1896,7 @@ void WindowTreeClient::SetBlockingContainers( } window_manager_client_->SetBlockingContainers( std::move(transport_all_blocking_containers), - base::Bind(&OnAckMustSucceed, FROM_HERE)); + base::BindOnce(&OnAckMustSucceed, FROM_HERE)); } void WindowTreeClient::GetWindowManager( @@ -1909,16 +2089,11 @@ void WindowTreeClient::WmBuildDragImage(const gfx::Point& screen_location, drag_image_offset, source); } -void WindowTreeClient::WmMoveDragImage( - const gfx::Point& screen_location, - const WmMoveDragImageCallback& callback) { - if (!window_manager_delegate_) { - callback.Run(); - return; - } - - window_manager_delegate_->OnWmMoveDragImage(screen_location); - callback.Run(); +void WindowTreeClient::WmMoveDragImage(const gfx::Point& screen_location, + WmMoveDragImageCallback callback) { + if (window_manager_delegate_) + window_manager_delegate_->OnWmMoveDragImage(screen_location); + std::move(callback).Run(); } void WindowTreeClient::WmDestroyDragImage() { @@ -2165,8 +2340,8 @@ void WindowTreeClient::InjectEvent(const ui::Event& event, int64_t display_id) { // Check event_injector_ so we don't crash if access to the interface was // refused. if (event_injector_) { - event_injector_->DispatchEvent(display_id, ui::Event::Clone(event), - base::DoNothing()); + event_injector_->InjectEvent(display_id, ui::Event::Clone(event), + base::DoNothing()); } } @@ -2197,7 +2372,8 @@ void WindowTreeClient::SetDisplayConfiguration( : display::kInvalidDisplayId; window_manager_client_->SetDisplayConfiguration( displays, std::move(viewport_metrics), primary_display_id, - internal_display_id, mirrors, base::Bind(&OnAckMustSucceed, FROM_HERE)); + internal_display_id, mirrors, + base::BindOnce(&OnAckMustSucceed, FROM_HERE)); } } @@ -2216,7 +2392,7 @@ void WindowTreeClient::AddDisplayReusingWindowTreeHost( window_manager_client_->SetDisplayRoot( display, std::move(viewport_metrics), is_primary_display, display_root_window->server_id(), mirrors, - base::Bind(&OnAckMustSucceed, FROM_HERE)); + base::BindOnce(&OnAckMustSucceed, FROM_HERE)); window_tree_host->compositor()->SetLocalSurfaceId( display_root_window->GetOrAllocateLocalSurfaceId( window_tree_host->GetBoundsInPixels().size())); @@ -2240,7 +2416,7 @@ void WindowTreeClient::SwapDisplayRoots(WindowTreeHostMus* window_tree_host1, if (window_manager_client_) { window_manager_client_->SwapDisplayRoots( - display_id1, display_id2, base::Bind(&OnAckMustSucceed, FROM_HERE)); + display_id1, display_id2, base::BindOnce(&OnAckMustSucceed, FROM_HERE)); } } diff --git a/chromium/ui/aura/mus/window_tree_client.h b/chromium/ui/aura/mus/window_tree_client.h index 1a887b9a322..8cd0e54f3c7 100644 --- a/chromium/ui/aura/mus/window_tree_client.h +++ b/chromium/ui/aura/mus/window_tree_client.h @@ -15,6 +15,7 @@ #include "base/atomicops.h" #include "base/compiler_specific.h" +#include "base/containers/flat_set.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" @@ -23,7 +24,7 @@ #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "mojo/public/cpp/bindings/strong_binding.h" -#include "services/ui/public/interfaces/remote_event_dispatcher.mojom.h" +#include "services/ui/public/interfaces/event_injector.mojom.h" #include "services/ui/public/interfaces/window_tree.mojom.h" #include "ui/aura/aura_export.h" #include "ui/aura/client/transient_window_client_observer.h" @@ -64,12 +65,15 @@ struct PropertyData; namespace aura { class CaptureSynchronizer; class DragDropControllerMus; +class EmbedRoot; +class EmbedRootDelegate; class FocusSynchronizer; class InFlightBoundsChange; class InFlightChange; class InFlightFocusChange; class InFlightPropertyChange; class InFlightVisibleChange; +class PlatformEventSourceMus; class MusContextFactory; class WindowMus; class WindowPortMus; @@ -79,13 +83,15 @@ class WindowTreeClientObserver; class WindowTreeClientTestObserver; class WindowTreeHostMus; -using EventResultCallback = base::Callback<void(ui::mojom::EventResult)>; +using EventResultCallback = base::OnceCallback<void(ui::mojom::EventResult)>; -// Manages the connection with mus. +// Used to enable Aura to act as the client-library for the Window Service. // -// WindowTreeClient is owned by the creator. Generally when the delegate gets -// one of OnEmbedRootDestroyed() or OnLostConnection() it should delete the -// WindowTreeClient. +// WindowTreeClient is created in a handful of distinct ways. See the various +// Create functions for details. +// +// Generally when the delegate gets one of OnEmbedRootDestroyed() or +// OnLostConnection() it should delete the WindowTreeClient. // // When WindowTreeClient is deleted all windows are deleted (and observers // notified). @@ -99,31 +105,38 @@ class AURA_EXPORT WindowTreeClient public WindowTreeHostMusDelegate, public client::TransientWindowClientObserver { public: - // |create_discardable_memory| If it is true, WindowTreeClient will setup the - // dicardable shared memory manager for this process. In some test, more than - // one WindowTreeClient will be created, so we need pass false to avoid - // setup the discardable shared memory manager more than once. - WindowTreeClient( + // Creates a WindowTreeClient to act as the window manager. See mojom for + // details on |automatically_create_display_roots|. + // TODO(sky): move |create_discardable_memory| out of this class. + static std::unique_ptr<WindowTreeClient> CreateForWindowManager( service_manager::Connector* connector, WindowTreeClientDelegate* delegate, - WindowManagerDelegate* window_manager_delegate = nullptr, - ui::mojom::WindowTreeClientRequest request = nullptr, - scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr, + WindowManagerDelegate* window_manager_delegate, + bool automatically_create_display_roots = true, bool create_discardable_memory = true); - ~WindowTreeClient() override; - // Establishes the connection by way of the WindowTreeFactory. - void ConnectViaWindowTreeFactory(); + // Creates a WindowTreeClient for use in embedding. + static std::unique_ptr<WindowTreeClient> CreateForEmbedding( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + ui::mojom::WindowTreeClientRequest request, + bool create_discardable_memory = true); - // Establishes the connection by way of WindowManagerWindowTreeFactory. - // See mojom for details on |automatically_create_display_roots|. - void ConnectAsWindowManager(bool automatically_create_display_roots = true); + // Creates a WindowTreeClient useful for creating top-level windows. + static std::unique_ptr<WindowTreeClient> CreateForWindowTreeFactory( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + bool create_discardable_memory = true, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr); + + // Creates a WindowTreeClient such that the Window Service creates a single + // WindowTreeHost. This is useful for testing and examples. + static std::unique_ptr<WindowTreeClient> CreateForWindowTreeHostFactory( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + bool create_discardable_memory = true); - // Connects to mus such that a single WindowTreeHost is created. This blocks - // until the WindowTreeHost is obtained and calls - // WindowTreeClientDelegate::OnEmbed() with the WindowTreeHost. This entry - // point is mostly useful for testing and examples. - void ConnectViaWindowTreeHostFactory(); + ~WindowTreeClient() override; void DisableDragDropClient() { install_drag_drop_client_ = false; } @@ -156,7 +169,11 @@ class AURA_EXPORT WindowTreeClient void Embed(Window* window, ui::mojom::WindowTreeClientPtr client, uint32_t flags, - const ui::mojom::WindowTree::EmbedCallback& callback); + ui::mojom::WindowTree::EmbedCallback callback); + void EmbedUsingToken(Window* window, + const base::UnguessableToken& token, + uint32_t flags, + ui::mojom::WindowTree::EmbedCallback callback); // Schedules an embed of a client. See // mojom::WindowTreeClient::ScheduleEmbed() for details. @@ -164,6 +181,9 @@ class AURA_EXPORT WindowTreeClient ui::mojom::WindowTreeClientPtr client, base::OnceCallback<void(const base::UnguessableToken&)> callback); + // Creates a new EmbedRoot. See EmbedRoot for details. + std::unique_ptr<EmbedRoot> CreateEmbedRoot(EmbedRootDelegate* delegate); + void AttachCompositorFrameSink( ui::Id window_id, viz::mojom::CompositorFrameSinkRequest compositor_frame_sink, @@ -194,6 +214,7 @@ class AURA_EXPORT WindowTreeClient void RemoveTestObserver(WindowTreeClientTestObserver* observer); private: + friend class EmbedRoot; friend class InFlightBoundsChange; friend class InFlightFocusChange; friend class InFlightPropertyChange; @@ -212,6 +233,21 @@ class AURA_EXPORT WindowTreeClient // TODO(sky): this assumes change_ids never wrap, which is a bad assumption. using InFlightMap = std::map<uint32_t, std::unique_ptr<InFlightChange>>; + // |create_discardable_memory| specifies whether WindowTreeClient will setup + // the dicardable shared memory manager for this process. In some tests, more + // than one WindowTreeClient will be created, so we need to pass false to + // avoid setting up the discardable shared memory manager more than once. + WindowTreeClient( + service_manager::Connector* connector, + WindowTreeClientDelegate* delegate, + WindowManagerDelegate* window_manager_delegate = nullptr, + ui::mojom::WindowTreeClientRequest request = nullptr, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = nullptr, + bool create_discardable_memory = true); + + // Creates a PlatformEventSourceMus if not created yet. + void CreatePlatformEventSourceIfNecessary(); + void RegisterWindowMus(WindowMus* window); WindowMus* GetWindowByServerId(ui::Id id); @@ -300,6 +336,12 @@ class AURA_EXPORT WindowTreeClient bool drawn, const base::Optional<viz::LocalSurfaceId>& local_surface_id); + // Returns the EmbedRoot whose root is |window|, or null if there isn't one. + EmbedRoot* GetEmbedRootWithRootWindow(aura::Window* window); + + // Called from EmbedRoot's destructor. + void OnEmbedRootDestroyed(EmbedRoot* embed_root); + // Called by WmNewDisplayAdded(). WindowTreeHostMus* WmNewDisplayAddedImpl( const display::Display& display, @@ -307,8 +349,7 @@ class AURA_EXPORT WindowTreeClient bool parent_drawn, const base::Optional<viz::LocalSurfaceId>& local_surface_id); - std::unique_ptr<EventResultCallback> CreateEventResultCallback( - int32_t event_id); + EventResultCallback CreateEventResultCallback(int32_t event_id); void OnReceivedCursorLocationMemory(mojo::ScopedSharedBufferHandle handle); @@ -364,6 +405,11 @@ class AURA_EXPORT WindowTreeClient ui::Id focused_window_id, bool drawn, const base::Optional<viz::LocalSurfaceId>& local_surface_id) override; + void OnEmbedFromToken( + const base::UnguessableToken& token, + ui::mojom::WindowDataPtr root, + int64_t display_id, + const base::Optional<viz::LocalSurfaceId>& local_surface_id) override; void OnEmbeddedAppDisconnected(ui::Id window_id) override; void OnUnembed(ui::Id window_id) override; void OnCaptureChanged(ui::Id new_capture_window_id, @@ -432,18 +478,18 @@ class AURA_EXPORT WindowTreeClient uint32_t event_flags, const gfx::Point& position, uint32_t effect_bitmask, - const OnDragEnterCallback& callback) override; + OnDragEnterCallback callback) override; void OnDragOver(ui::Id window_id, uint32_t event_flags, const gfx::Point& position, uint32_t effect_bitmask, - const OnDragOverCallback& callback) override; + OnDragOverCallback callback) override; void OnDragLeave(ui::Id window_id) override; void OnCompleteDrop(ui::Id window_id, uint32_t event_flags, const gfx::Point& position, uint32_t effect_bitmask, - const OnCompleteDropCallback& callback) override; + OnCompleteDropCallback callback) override; void OnPerformDragDropCompleted(uint32_t change_id, bool success, uint32_t action_taken) override; @@ -489,7 +535,7 @@ class AURA_EXPORT WindowTreeClient const gfx::Vector2d& drag_image_offset, ui::mojom::PointerKind source) override; void WmMoveDragImage(const gfx::Point& screen_location, - const WmMoveDragImageCallback& callback) override; + WmMoveDragImageCallback callback) override; void WmDestroyDragImage() override; void WmPerformMoveLoop(uint32_t change_id, ui::Id window_id, @@ -616,6 +662,8 @@ class AURA_EXPORT WindowTreeClient std::set<WindowMus*> roots_; + base::flat_set<EmbedRoot*> embed_roots_; + IdToWindowMap windows_; std::map<ui::ClientSpecificId, std::set<Window*>> embedded_windows_; @@ -649,7 +697,7 @@ class AURA_EXPORT WindowTreeClient // WindowManagerClient set this, but not |window_manager_internal_client_|. ui::mojom::WindowManagerClient* window_manager_client_ = nullptr; - ui::mojom::RemoteEventDispatcherPtr event_injector_; + ui::mojom::EventInjectorPtr event_injector_; bool has_pointer_watcher_ = false; @@ -692,6 +740,10 @@ class AURA_EXPORT WindowTreeClient // removed. bool install_drag_drop_client_ = true; +#if defined(USE_OZONE) + std::unique_ptr<PlatformEventSourceMus> platform_event_source_; +#endif + base::WeakPtrFactory<WindowTreeClient> weak_factory_; DISALLOW_COPY_AND_ASSIGN(WindowTreeClient); diff --git a/chromium/ui/aura/mus/window_tree_client_unittest.cc b/chromium/ui/aura/mus/window_tree_client_unittest.cc index 1ed6c0c3428..1059364d435 100644 --- a/chromium/ui/aura/mus/window_tree_client_unittest.cc +++ b/chromium/ui/aura/mus/window_tree_client_unittest.cc @@ -27,6 +27,8 @@ #include "ui/aura/client/transient_window_client.h" #include "ui/aura/mus/capture_synchronizer.h" #include "ui/aura/mus/client_surface_embedder.h" +#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/property_converter.h" #include "ui/aura/mus/window_mus.h" @@ -36,6 +38,7 @@ #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/aura_test_suite.h" #include "ui/aura/test/mus/test_window_tree.h" #include "ui/aura/test/mus/window_tree_client_private.h" #include "ui/aura/test/test_window_delegate.h" @@ -53,6 +56,8 @@ #include "ui/display/screen.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" +#include "ui/events/platform/platform_event_observer.h" +#include "ui/events/platform/platform_event_source.h" #include "ui/events/test/test_event_handler.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/geometry/rect.h" @@ -2894,7 +2899,7 @@ TEST_F(WindowTreeClientDestructionTest, WindowsFromOtherConnectionsDeleted) { EXPECT_TRUE(window_tracker.windows().empty()); } -TEST_F(WindowTreeClientWmTest, ObservedPointerEvents) { +TEST_F(WindowTreeClientWmTestHighDPI, ObservedPointerEvents) { const gfx::Rect bounds(1, 2, 101, 102); std::unique_ptr<DisplayInitParams> display_params = std::make_unique<DisplayInitParams>(); @@ -2966,4 +2971,109 @@ TEST_F(WindowTreeClientWmTest, ObservedPointerEvents) { last_event->root_location()); } +class TestEmbedRootDelegate : public EmbedRootDelegate { + public: + TestEmbedRootDelegate() = default; + ~TestEmbedRootDelegate() override = default; + + // EmbedRootDelegate: + void OnEmbedTokenAvailable(const base::UnguessableToken& token) override {} + void OnEmbed(Window* window) override {} + void OnUnembed() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestEmbedRootDelegate); +}; + +// Verifies we don't crash when focus changes to a window in an EmbedRoot. +TEST_F(WindowTreeClientClientTest, ChangeFocusInEmbedRootWindow) { + TestEmbedRootDelegate embed_root_delegate; + std::unique_ptr<EmbedRoot> embed_root = + window_tree_client_impl()->CreateEmbedRoot(&embed_root_delegate); + WindowTreeClientPrivate(window_tree_client_impl()) + .CallOnEmbedFromToken(embed_root.get()); + ASSERT_TRUE(embed_root->window()); + window_tree_client()->OnWindowFocused(server_id(embed_root->window())); +} + +#if defined(USE_OZONE) + +class TestPlatformEventObserver : public ui::PlatformEventObserver { + public: + TestPlatformEventObserver() = default; + ~TestPlatformEventObserver() override = default; + + int will_process_count() const { return will_process_count_; } + int did_process_count() const { return did_process_count_; } + ui::EventType will_process_type() const { return will_process_type_; } + ui::EventType did_process_type() const { return did_process_type_; } + + // PlatformEventObserver: + void WillProcessEvent(const ui::PlatformEvent& event) override { + will_process_count_++; + will_process_type_ = static_cast<const ui::Event*>(event)->type(); + } + void DidProcessEvent(const ui::PlatformEvent& event) override { + did_process_count_++; + did_process_type_ = static_cast<const ui::Event*>(event)->type(); + } + + private: + int will_process_count_ = 0; + int did_process_count_ = 0; + ui::EventType will_process_type_ = ui::ET_UNKNOWN; + ui::EventType did_process_type_ = ui::ET_UNKNOWN; + + DISALLOW_COPY_AND_ASSIGN(TestPlatformEventObserver); +}; + +// Base class that installs a new version of Env configured for Mus in SetUp() +// (and installs a new version of Env configured for Local during TearDown()). +// This is necessary as when Env is created with a Model of Local it installs +// a PlatformEventSource, not the one that WindowTreeClient installs. +class WindowTreeClientWmOzoneTest : public test::AuraMusWmTestBase { + public: + WindowTreeClientWmOzoneTest() = default; + ~WindowTreeClientWmOzoneTest() override = default; + + // test::AuraMusWmTestBase: + void SetUp() override { + env_reinstaller_ = std::make_unique<test::EnvReinstaller>(); + env_ = Env::CreateInstance(Env::Mode::MUS); + AuraMusWmTestBase::SetUp(); + } + + void TearDown() override { + AuraMusWmTestBase::TearDown(); + env_.reset(); + env_reinstaller_.reset(); + } + + private: + std::unique_ptr<test::EnvReinstaller> env_reinstaller_; + std::unique_ptr<Env> env_; + + DISALLOW_COPY_AND_ASSIGN(WindowTreeClientWmOzoneTest); +}; + +// Used to verify PlatformEventSource is correctly wired up in ozone. +TEST_F(WindowTreeClientWmOzoneTest, PlatformEventSourceInstalled) { + ASSERT_TRUE(ui::PlatformEventSource::GetInstance()); + TestPlatformEventObserver test_observer; + ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver( + &test_observer); + ui::MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), ui::EF_NONE, 0); + window_tree_client()->OnWindowInputEvent(1, server_id(root_window()), 0, + ui::Id(), gfx::PointF(), + ui::Event::Clone(event), 0); + ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver( + &test_observer); + EXPECT_EQ(1, test_observer.will_process_count()); + EXPECT_EQ(1, test_observer.did_process_count()); + EXPECT_EQ(ui::ET_MOUSE_MOVED, test_observer.will_process_type()); + EXPECT_EQ(ui::ET_MOUSE_MOVED, test_observer.did_process_type()); +} +#endif + } // namespace aura diff --git a/chromium/ui/aura/mus/window_tree_host_mus.cc b/chromium/ui/aura/mus/window_tree_host_mus.cc index 3acab45d4e2..639f26292a8 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus.cc +++ b/chromium/ui/aura/mus/window_tree_host_mus.cc @@ -4,7 +4,6 @@ #include "ui/aura/mus/window_tree_host_mus.h" -#include "base/memory/ptr_util.h" #include "ui/aura/env.h" #include "ui/aura/mus/input_method_mus.h" #include "ui/aura/mus/window_port_mus.h" @@ -87,6 +86,8 @@ WindowTreeHostMus::WindowTreeHostMus(WindowTreeHostMusInitParams init_params) this, use_default_accelerated_widget, bounds_in_pixels)); if (!init_params.use_classic_ime) { + // NOTE: This creates one InputMethodMus per display, despite the + // call to SetSharedInputMethod() below. input_method_ = std::make_unique<InputMethodMus>(this, window()); input_method_->Init(init_params.window_tree_client->connector()); SetSharedInputMethod(input_method_.get()); 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 3d7714e6f7f..39a7302ed1d 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus_unittest.cc +++ b/chromium/ui/aura/mus/window_tree_host_mus_unittest.cc @@ -4,7 +4,6 @@ #include "ui/aura/mus/window_tree_host_mus.h" -#include "base/memory/ptr_util.h" #include "ui/aura/mus/window_port_mus.h" #include "ui/aura/mus/window_tree_host_mus_init_params.h" #include "ui/aura/test/aura_mus_test_base.h" diff --git a/chromium/ui/aura/scoped_keyboard_hook.cc b/chromium/ui/aura/scoped_keyboard_hook.cc index 96d9d778439..68348c16f75 100644 --- a/chromium/ui/aura/scoped_keyboard_hook.cc +++ b/chromium/ui/aura/scoped_keyboard_hook.cc @@ -9,6 +9,8 @@ namespace aura { +ScopedKeyboardHook::ScopedKeyboardHook() = default; + ScopedKeyboardHook::ScopedKeyboardHook( base::WeakPtr<WindowTreeHost> window_tree_host) : window_tree_host_(window_tree_host) { @@ -21,4 +23,8 @@ ScopedKeyboardHook::~ScopedKeyboardHook() { window_tree_host_->ReleaseSystemKeyEventCapture(); } +bool ScopedKeyboardHook::IsKeyLocked(int native_key_code) { + return window_tree_host_ && window_tree_host_->IsKeyLocked(native_key_code); +} + } // namespace aura diff --git a/chromium/ui/aura/scoped_keyboard_hook.h b/chromium/ui/aura/scoped_keyboard_hook.h index d061544aff2..57975d68c91 100644 --- a/chromium/ui/aura/scoped_keyboard_hook.h +++ b/chromium/ui/aura/scoped_keyboard_hook.h @@ -20,7 +20,13 @@ class WindowTreeHost; class AURA_EXPORT ScopedKeyboardHook { public: explicit ScopedKeyboardHook(base::WeakPtr<WindowTreeHost> weak_ptr); - ~ScopedKeyboardHook(); + virtual ~ScopedKeyboardHook(); + + // True if |native_key_code| is reserved for an active KeyboardLock request. + virtual bool IsKeyLocked(int native_key_code); + + protected: + ScopedKeyboardHook(); private: THREAD_CHECKER(thread_checker_); diff --git a/chromium/ui/aura/test/ui_controls_factory_ozone.cc b/chromium/ui/aura/test/ui_controls_factory_ozone.cc index c30653ca6c0..38391290fe3 100644 --- a/chromium/ui/aura/test/ui_controls_factory_ozone.cc +++ b/chromium/ui/aura/test/ui_controls_factory_ozone.cc @@ -8,9 +8,14 @@ #include "base/macros.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/ui/public/interfaces/constants.mojom.h" +#include "services/ui/public/interfaces/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" +#include "ui/aura/test/env_test_helper.h" #include "ui/aura/test/ui_controls_factory_aura.h" #include "ui/aura/window_tree_host.h" #include "ui/base/test/ui_controls_aura.h" @@ -21,6 +26,15 @@ namespace aura { namespace test { namespace { +// Callback from Window Service with the result of posting an event. |result| +// is true if event successfully processed and |closure| is an optional closure +// to run when done (used in client code to wait for ack). +void OnWindowServiceProcessedEvent(base::OnceClosure closure, bool result) { + DCHECK(result); + if (closure) + std::move(closure).Run(); +} + class UIControlsOzone : public ui_controls::UIControlsAura { public: UIControlsOzone(WindowTreeHost* host) : host_(host) {} @@ -31,73 +45,83 @@ class UIControlsOzone : public ui_controls::UIControlsAura { bool shift, bool alt, bool command) override { - return SendKeyPressNotifyWhenDone( - window, key, control, shift, alt, command, base::Closure()); + return SendKeyPressNotifyWhenDone(window, key, control, shift, alt, command, + base::OnceClosure()); } - bool SendKeyPressNotifyWhenDone( - gfx::NativeWindow window, - ui::KeyboardCode key, - bool control, - bool shift, - bool alt, - bool command, - const base::Closure& closure) override { + bool SendKeyPressNotifyWhenDone(gfx::NativeWindow window, + ui::KeyboardCode key, + bool control, + bool shift, + bool alt, + bool command, + base::OnceClosure closure) override { int flags = button_down_mask_; if (control) { flags |= ui::EF_CONTROL_DOWN; - PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, flags); + PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_CONTROL, flags, + base::OnceClosure()); } if (shift) { flags |= ui::EF_SHIFT_DOWN; - PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, flags); + PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, flags, + base::OnceClosure()); } if (alt) { flags |= ui::EF_ALT_DOWN; - PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_MENU, flags); + PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_MENU, flags, + base::OnceClosure()); } if (command) { flags |= ui::EF_COMMAND_DOWN; - PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LWIN, flags); + PostKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LWIN, flags, + base::OnceClosure()); } - PostKeyEvent(ui::ET_KEY_PRESSED, key, flags); - PostKeyEvent(ui::ET_KEY_RELEASED, key, flags); + PostKeyEvent(ui::ET_KEY_PRESSED, key, flags, base::OnceClosure()); + const bool has_modifier = control || shift || alt || command; + // Pass the real closure to the last generated KeyEvent. + PostKeyEvent(ui::ET_KEY_RELEASED, key, flags, + has_modifier ? base::OnceClosure() : std::move(closure)); if (alt) { flags &= ~ui::EF_ALT_DOWN; - PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_MENU, flags); + PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_MENU, flags, + (shift || control || command) ? base::OnceClosure() + : std::move(closure)); } if (shift) { flags &= ~ui::EF_SHIFT_DOWN; - PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, flags); + PostKeyEvent( + ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, flags, + (control || command) ? base::OnceClosure() : std::move(closure)); } if (control) { flags &= ~ui::EF_CONTROL_DOWN; - PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, flags); + PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_CONTROL, flags, + command ? base::OnceClosure() : std::move(closure)); } if (command) { flags &= ~ui::EF_COMMAND_DOWN; - PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_LWIN, flags); + PostKeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_LWIN, flags, + std::move(closure)); } - RunClosureAfterAllPendingUIEvents(closure); return true; } bool SendMouseMove(long screen_x, long screen_y) override { - return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::Closure()); + return SendMouseMoveNotifyWhenDone(screen_x, screen_y, base::OnceClosure()); } - bool SendMouseMoveNotifyWhenDone( - long screen_x, - long screen_y, - const base::Closure& closure) override { + 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()); @@ -116,18 +140,17 @@ class UIControlsOzone : public ui_controls::UIControlsAura { else event_type = ui::ET_MOUSE_MOVED; - PostMouseEvent(event_type, host_location, button_down_mask_, 0); + PostMouseEvent(event_type, host_location, button_down_mask_, 0, + std::move(closure)); - RunClosureAfterAllPendingUIEvents(closure); return true; } bool SendMouseEvents(ui_controls::MouseButton type, int state) override { - return SendMouseEventsNotifyWhenDone(type, state, base::Closure()); + return SendMouseEventsNotifyWhenDone(type, state, base::OnceClosure()); } - bool SendMouseEventsNotifyWhenDone( - ui_controls::MouseButton type, - int state, - const base::Closure& closure) override { + bool SendMouseEventsNotifyWhenDone(ui_controls::MouseButton type, + int state, + base::OnceClosure closure) override { gfx::Point root_location = aura::Env::GetInstance()->last_mouse_location(); aura::client::ScreenPositionClient* screen_position_client = aura::client::GetScreenPositionClient(host_->window()); @@ -158,65 +181,91 @@ class UIControlsOzone : public ui_controls::UIControlsAura { if (state & ui_controls::DOWN) { button_down_mask_ |= flag; - PostMouseEvent(ui::ET_MOUSE_PRESSED, host_location, - button_down_mask_ | flag, flag); + // Pass the real closure to the last generated MouseEvent. + PostMouseEvent( + ui::ET_MOUSE_PRESSED, host_location, button_down_mask_ | flag, flag, + (state & ui_controls::UP) ? base::OnceClosure() : std::move(closure)); } if (state & ui_controls::UP) { button_down_mask_ &= ~flag; PostMouseEvent(ui::ET_MOUSE_RELEASED, host_location, - button_down_mask_ | flag, flag); + button_down_mask_ | flag, flag, std::move(closure)); } - RunClosureAfterAllPendingUIEvents(closure); return true; } bool SendMouseClick(ui_controls::MouseButton type) override { return SendMouseEvents(type, ui_controls::UP | ui_controls::DOWN); } - void RunClosureAfterAllPendingUIEvents( - const base::Closure& closure) override { - if (!closure.is_null()) - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, closure); - } private: - void SendEventToSink(ui::Event* event) { - ui::EventSourceTestApi event_source_test(host_->GetEventSource()); - ui::EventDispatchDetails details = event_source_test.SendEventToSink(event); - if (details.dispatcher_destroyed) + void SendEventToSink(ui::Event* event, base::OnceClosure closure) { + if (aura::Env::GetInstance()->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), + base::BindOnce(&OnWindowServiceProcessedEvent, std::move(closure))); return; + } + + // Post the task before processing the event. This is necessary in case + // processing the event results in a nested message loop. + if (closure) { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + std::move(closure)); + } + + ui::EventSourceTestApi event_source_test(host_->GetEventSource()); + ignore_result(event_source_test.SendEventToSink(event)); } - void PostKeyEvent(ui::EventType type, ui::KeyboardCode key_code, int flags) { + void PostKeyEvent(ui::EventType type, + ui::KeyboardCode key_code, + int flags, + base::OnceClosure closure) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&UIControlsOzone::PostKeyEventTask, - base::Unretained(this), type, key_code, flags)); + FROM_HERE, base::BindOnce(&UIControlsOzone::PostKeyEventTask, + base::Unretained(this), type, key_code, flags, + std::move(closure))); } void PostKeyEventTask(ui::EventType type, ui::KeyboardCode key_code, - int flags) { + int flags, + base::OnceClosure closure) { // Do not rewrite injected events. See crbug.com/136465. flags |= ui::EF_FINAL; ui::KeyEvent key_event(type, key_code, flags); - SendEventToSink(&key_event); + SendEventToSink(&key_event, std::move(closure)); } void PostMouseEvent(ui::EventType type, const gfx::Point& host_location, int flags, - int changed_button_flags) { + int changed_button_flags, + base::OnceClosure closure) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&UIControlsOzone::PostMouseEventTask, base::Unretained(this), - type, host_location, flags, changed_button_flags)); + base::BindOnce(&UIControlsOzone::PostMouseEventTask, + base::Unretained(this), type, host_location, flags, + changed_button_flags, std::move(closure))); } void PostMouseEventTask(ui::EventType type, const gfx::Point& host_location, int flags, - int changed_button_flags) { + int changed_button_flags, + base::OnceClosure closure) { ui::MouseEvent mouse_event(type, host_location, host_location, ui::EventTimeForNow(), flags, changed_button_flags); @@ -224,10 +273,25 @@ class UIControlsOzone : public ui_controls::UIControlsAura { // This hack is necessary to set the repeat count for clicks. ui::MouseEvent mouse_event2(&mouse_event); - SendEventToSink(&mouse_event2); + SendEventToSink(&mouse_event2, std::move(closure)); + } + + // Returns the ui::mojom::EventInjector, which is used to send events + // to the Window Service for dispatch. + ui::mojom::EventInjector* GetEventInjector() { + DCHECK_EQ(aura::Env::Mode::MUS, aura::Env::GetInstance()->mode()); + if (!event_injector_) { + DCHECK(aura::test::EnvTestHelper().GetWindowTreeClient()); + aura::test::EnvTestHelper() + .GetWindowTreeClient() + ->connector() + ->BindInterface(ui::mojom::kServiceName, &event_injector_); + } + return event_injector_.get(); } WindowTreeHost* host_; + ui::mojom::EventInjectorPtr event_injector_; // 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 diff --git a/chromium/ui/aura/window.cc b/chromium/ui/aura/window.cc index 0f224d897e0..c8b3654b21c 100644 --- a/chromium/ui/aura/window.cc +++ b/chromium/ui/aura/window.cc @@ -14,7 +14,6 @@ #include "base/callback.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -274,9 +273,7 @@ void Window::SetTransform(const gfx::Transform& transform) { WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; for (WindowObserver& observer : observers_) observer.OnWindowTargetTransformChanging(this, transform); - gfx::Transform old_transform = layer()->transform(); layer()->SetTransform(transform); - port_->OnDidChangeTransform(old_transform, transform); } void Window::SetLayoutManager(LayoutManager* layout_manager) { @@ -767,6 +764,17 @@ void Window::SetBoundsInternal(const gfx::Rect& new_bounds) { } } +void Window::SetDeviceScaleFactor(float device_scale_factor) { + float old_device_scale_factor = layer()->device_scale_factor(); + layer()->OnDeviceScaleFactorChanged(device_scale_factor); + + // If we are currently not the layer's delegate, we will not get the device + // scale factor changed notification from the layer (this typically happens + // after animating hidden). We must notify ourselves. + if (layer()->delegate() != this) + OnDeviceScaleFactorChanged(old_device_scale_factor, device_scale_factor); +} + void Window::SetVisible(bool visible) { if (visible == layer()->GetTargetVisibility()) return; // No change. @@ -1100,6 +1108,15 @@ void Window::AllocateLocalSurfaceId() { port_->AllocateLocalSurfaceId(); } +bool Window::IsLocalSurfaceIdAllocationSuppressed() const { + return port_->IsLocalSurfaceIdAllocationSuppressed(); +} + +viz::ScopedSurfaceIdAllocator Window::GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) { + return port_->GetSurfaceIdAllocator(std::move(allocation_task)); +} + const viz::LocalSurfaceId& Window::GetLocalSurfaceId() const { return port_->GetLocalSurfaceId(); } @@ -1153,7 +1170,9 @@ void Window::OnLayerOpacityChanged(ui::PropertyChangeReason reason) { observer.OnWindowOpacitySet(this, reason); } -void Window::OnLayerTransformed(ui::PropertyChangeReason reason) { +void Window::OnLayerTransformed(const gfx::Transform& old_transform, + ui::PropertyChangeReason reason) { + port_->OnDidChangeTransform(old_transform, layer()->transform()); WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; for (WindowObserver& observer : observers_) observer.OnWindowTransformed(this, reason); diff --git a/chromium/ui/aura/window.h b/chromium/ui/aura/window.h index b6e2dcfb871..8fb95e8e14f 100644 --- a/chromium/ui/aura/window.h +++ b/chromium/ui/aura/window.h @@ -19,9 +19,11 @@ #include "base/observer_list.h" #include "base/optional.h" #include "base/strings/string16.h" +#include "components/viz/common/surfaces/scoped_surface_id_allocator.h" #include "ui/aura/aura_export.h" #include "ui/aura/client/window_types.h" #include "ui/aura/window_observer.h" +#include "ui/aura/window_port.h" #include "ui/base/class_property.h" #include "ui/compositor/layer_animator.h" #include "ui/compositor/layer_delegate.h" @@ -58,7 +60,6 @@ class LayoutManager; class ScopedKeyboardHook; class WindowDelegate; class WindowObserver; -class WindowPort; class WindowPortForShutdown; class WindowTreeHost; @@ -216,6 +217,11 @@ class AURA_EXPORT Window : public ui::LayerDelegate, // LayoutManager may adjust the bounds. void SetBounds(const gfx::Rect& new_bounds); + // Explicitly set a device scale factor for the window. Note that the + // window's device scale factor is also set when adding its ui::Layer to the + // layer tree of a ui::Compositor. + void SetDeviceScaleFactor(float device_scale_factor); + // Changes the bounds of the window in the screen coordinates. // If present, the window's parent's LayoutManager may adjust the bounds. void SetBoundsInScreen(const gfx::Rect& new_bounds_in_screen_coords, @@ -386,6 +392,13 @@ class AURA_EXPORT Window : public ui::LayerDelegate, // that does not involve a resize or a device scale factor change. void AllocateLocalSurfaceId(); + // When a child-allocated viz::LocalSurfaceId is being processed, this returns + // true. + bool IsLocalSurfaceIdAllocationSuppressed() const; + + viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task); + // Gets the current viz::LocalSurfaceId. const viz::LocalSurfaceId& GetLocalSurfaceId() const; @@ -518,7 +531,8 @@ class AURA_EXPORT Window : public ui::LayerDelegate, void OnPaintLayer(const ui::PaintContext& context) override; void OnLayerBoundsChanged(const gfx::Rect& old_bounds, ui::PropertyChangeReason reason) override; - void OnLayerTransformed(ui::PropertyChangeReason reason) override; + void OnLayerTransformed(const gfx::Transform& old_transform, + ui::PropertyChangeReason reason) override; void OnLayerOpacityChanged(ui::PropertyChangeReason reason) override; // Overridden from ui::EventTarget: diff --git a/chromium/ui/aura/window_event_dispatcher.cc b/chromium/ui/aura/window_event_dispatcher.cc index 8bfba9421b2..31f80f2e704 100644 --- a/chromium/ui/aura/window_event_dispatcher.cc +++ b/chromium/ui/aura/window_event_dispatcher.cc @@ -131,7 +131,8 @@ void WindowEventDispatcher::RepostEvent(const ui::LocatedEvent* event) { if (held_repostable_event_) { base::ThreadTaskRunnerHandle::Get()->PostNonNestableTask( - FROM_HERE, base::Bind( + FROM_HERE, + base::BindOnce( base::IgnoreResult(&WindowEventDispatcher::DispatchHeldEvents), repost_event_factory_.GetWeakPtr())); } @@ -823,8 +824,8 @@ void WindowEventDispatcher::PostSynthesizeMouseMove() { synthesize_mouse_move_ = true; base::ThreadTaskRunnerHandle::Get()->PostNonNestableTask( FROM_HERE, - base::Bind(base::IgnoreResult( - &WindowEventDispatcher::SynthesizeMouseMoveEvent), + base::BindOnce( + base::IgnoreResult(&WindowEventDispatcher::SynthesizeMouseMoveEvent), held_event_factory_.GetWeakPtr())); } diff --git a/chromium/ui/aura/window_event_dispatcher_unittest.cc b/chromium/ui/aura/window_event_dispatcher_unittest.cc index dc9571ef34e..fb63240663b 100644 --- a/chromium/ui/aura/window_event_dispatcher_unittest.cc +++ b/chromium/ui/aura/window_event_dispatcher_unittest.cc @@ -2158,8 +2158,9 @@ class WindowEventDispatcherTestWithMessageLoop ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE)); message_loop()->task_runner()->PostTask( FROM_HERE, - base::Bind(&WindowEventDispatcherTestWithMessageLoop::RepostEventHelper, - host()->dispatcher(), base::Passed(&mouse))); + base::BindOnce( + &WindowEventDispatcherTestWithMessageLoop::RepostEventHelper, + host()->dispatcher(), std::move(mouse))); message_loop()->task_runner()->PostTask( FROM_HERE, message_loop()->QuitWhenIdleClosure()); @@ -2205,8 +2206,9 @@ TEST_P(WindowEventDispatcherTestWithMessageLoop, EventRepostedInNonNestedLoop) { // Perform the test in a callback, so that it runs after the message-loop // starts. message_loop()->task_runner()->PostTask( - FROM_HERE, base::Bind(&WindowEventDispatcherTestWithMessageLoop::RunTest, - base::Unretained(this))); + FROM_HERE, + base::BindOnce(&WindowEventDispatcherTestWithMessageLoop::RunTest, + base::Unretained(this))); base::RunLoop().Run(); } @@ -3205,8 +3207,8 @@ class NestedLocationDelegate : public test::TestWindowDelegate { // is considered the first nested loop. base::RunLoop run_loop; base::MessageLoop::current()->task_runner()->PostTask( - FROM_HERE, base::Bind(&NestedLocationDelegate::InInitialMessageLoop, - base::Unretained(this), &run_loop)); + FROM_HERE, base::BindOnce(&NestedLocationDelegate::InInitialMessageLoop, + base::Unretained(this), &run_loop)); run_loop.Run(); } @@ -3216,8 +3218,8 @@ class NestedLocationDelegate : public test::TestWindowDelegate { // RunLoop. base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); base::MessageLoop::current()->task_runner()->PostTask( - FROM_HERE, base::Bind(&NestedLocationDelegate::InRunMessageLoop, - base::Unretained(this), &run_loop)); + FROM_HERE, base::BindOnce(&NestedLocationDelegate::InRunMessageLoop, + base::Unretained(this), &run_loop)); run_loop.Run(); initial_run_loop->Quit(); } diff --git a/chromium/ui/aura/window_occlusion_tracker.cc b/chromium/ui/aura/window_occlusion_tracker.cc index dff18bd85e7..f573b75c057 100644 --- a/chromium/ui/aura/window_occlusion_tracker.cc +++ b/chromium/ui/aura/window_occlusion_tracker.cc @@ -27,7 +27,11 @@ constexpr ui::LayerAnimationElement::AnimatableProperties // Maximum number of times that MaybeComputeOcclusion() should have to recompute // occlusion states before they become stable. -constexpr int kMaxRecomputeOcclusion = 2; +// +// TODO(fdoray): This can be changed to 2 once showing/hiding a WebContents +// doesn't cause a call to Show()/Hide() on the aura::Window of a +// RenderWidgetHostViewAura. https://crbug.com/827268 +constexpr int kMaxRecomputeOcclusion = 3; WindowOcclusionTracker* g_tracker = nullptr; @@ -132,6 +136,11 @@ WindowOcclusionTracker::WindowOcclusionTracker() = default; WindowOcclusionTracker::~WindowOcclusionTracker() = default; +WindowOcclusionTracker* WindowOcclusionTracker::GetInstance() { + DCHECK(g_tracker); + return g_tracker; +} + void WindowOcclusionTracker::MaybeComputeOcclusion() { if (g_num_pause_occlusion_tracking || num_times_occlusion_recomputed_ != 0) return; @@ -352,8 +361,10 @@ void WindowOcclusionTracker::MarkRootWindowAsDirty( // TODO(fdoray): Remove this once we are confident that occlusion states are // stable after |kMaxRecomputeOcclusion| iterations in production. // https://crbug.com/813076 - if (num_times_occlusion_recomputed_ == kMaxRecomputeOcclusion) + if (num_times_occlusion_recomputed_ == kMaxRecomputeOcclusion) { + was_occlusion_recomputed_too_many_times_ = true; base::debug::DumpWithoutCrashing(); + } } bool WindowOcclusionTracker::WindowOrParentIsAnimated(Window* window) const { @@ -490,8 +501,13 @@ void WindowOcclusionTracker::OnWillRemoveWindow(Window* window) { void WindowOcclusionTracker::OnWindowVisibilityChanged(Window* window, bool visible) { - MarkRootWindowAsDirtyAndMaybeComputeOcclusionIf( - window, [=]() { return !WindowOrParentIsAnimated(window); }); + MarkRootWindowAsDirtyAndMaybeComputeOcclusionIf(window, [=]() { + // A child isn't visible when its parent isn't IsVisible(). Therefore, there + // is no need to compute occlusion when Show() or Hide() is called on a + // window with a hidden parent. + return (!window->parent() || window->parent()->IsVisible()) && + !WindowOrParentIsAnimated(window); + }); } void WindowOcclusionTracker::OnWindowBoundsChanged( diff --git a/chromium/ui/aura/window_occlusion_tracker.h b/chromium/ui/aura/window_occlusion_tracker.h index c76f4f4eee0..f60948420bd 100644 --- a/chromium/ui/aura/window_occlusion_tracker.h +++ b/chromium/ui/aura/window_occlusion_tracker.h @@ -22,6 +22,10 @@ class Transform; namespace aura { +namespace test { +class WindowOcclusionTrackerTestApi; +} + // Notifies tracked Windows when their occlusion state change. // // To start tracking the occlusion state of a Window, call @@ -54,6 +58,8 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, static void Track(Window* window); private: + friend class test::WindowOcclusionTrackerTestApi; + struct RootWindowState { // Number of Windows whose occlusion state is tracked under this root // Window. @@ -66,6 +72,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. @@ -194,6 +202,11 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, // recomputed occlusion states. Always 0 when not in MaybeComputeOcclusion(). int num_times_occlusion_recomputed_ = 0; + // Set to true when occlusion is recomputed too many times before it becomes + // stable. Reset in + // WindowOcclusionTrackerTestApi::WasOcclusionRecomputedTooManyTimes(). + bool was_occlusion_recomputed_too_many_times_ = false; + 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 6394bd7e42f..a89b3ecd7aa 100644 --- a/chromium/ui/aura/window_occlusion_tracker_unittest.cc +++ b/chromium/ui/aura/window_occlusion_tracker_unittest.cc @@ -12,6 +12,7 @@ #include "ui/aura/test/env_test_helper.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/test/test_windows.h" +#include "ui/aura/test/window_occlusion_tracker_test_api.h" #include "ui/aura/window_observer.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_sequence.h" @@ -1425,30 +1426,33 @@ class WindowDelegateChangingWindowVisibility : public MockWindowDelegate { if (!window_to_update_) return; + ++num_occlusion_change_; + if (window_to_update_->IsVisible()) { window_to_update_->Hide(); - EXPECT_FALSE(did_set_expectation_from_occlusion_changed_); - set_expectation(Window::OcclusionState::HIDDEN); - did_set_expectation_from_occlusion_changed_ = true; + if (num_occlusion_change_ <= 3) + set_expectation(Window::OcclusionState::HIDDEN); } else { window_to_update_->Show(); - if (!did_set_expectation_from_occlusion_changed_) set_expectation(Window::OcclusionState::VISIBLE); } } private: Window* window_to_update_ = nullptr; - bool did_set_expectation_from_occlusion_changed_ = false; + int num_occlusion_change_ = 0; DISALLOW_COPY_AND_ASSIGN(WindowDelegateChangingWindowVisibility); }; + } // namespace // Verify that if a window changes its visibility every time it is notified that // its occlusion state changed, the occlusion state of all IsVisible() windows -// is set to NOT_OCCLUDED and no infinite loop is entered. +// is set to VISIBLE and no infinite loop is entered. TEST_F(WindowOcclusionTrackerTest, OcclusionStatesDontBecomeStable) { + test::WindowOcclusionTrackerTestApi test_api; + // Create 2 superposed tracked windows. MockWindowDelegate* delegate_a = new MockWindowDelegate(); delegate_a->set_expectation(Window::OcclusionState::VISIBLE); @@ -1484,15 +1488,17 @@ TEST_F(WindowOcclusionTrackerTest, OcclusionStatesDontBecomeStable) { // Hide |window_d|. This will cause occlusion to be recomputed multiple times. // Once the maximum number of times that occlusion can be recomputed is // reached, the occlusion state of all IsVisible() windows should be set to - // NOT_OCCLUDED. + // VISIBLE. delegate_a->set_expectation(Window::OcclusionState::VISIBLE); delegate_d->set_expectation(Window::OcclusionState::HIDDEN); + EXPECT_FALSE(test_api.WasOcclusionRecomputedTooManyTimes()); window_d->Hide(); + EXPECT_TRUE(test_api.WasOcclusionRecomputedTooManyTimes()); EXPECT_FALSE(delegate_a->is_expecting_call()); EXPECT_FALSE(delegate_d->is_expecting_call()); } -// Verify that the occlusion states are correctly update when a branch of the +// Verify that the occlusion states are correctly updated when a branch of the // tree is hidden. TEST_F(WindowOcclusionTrackerTest, HideTreeBranch) { // Create a branch of 3 tracked windows. Expect them to be visible. @@ -1521,4 +1527,92 @@ TEST_F(WindowOcclusionTrackerTest, HideTreeBranch) { EXPECT_FALSE(delegate_c->is_expecting_call()); } +namespace { + +class WindowDelegateHidingWindow : public MockWindowDelegate { + public: + WindowDelegateHidingWindow() = default; + + void set_window_to_update(Window* window) { window_to_update_ = window; } + + // MockWindowDelegate: + void OnWindowOcclusionChanged( + Window::OcclusionState occlusion_state) override { + MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state); + if (!window_to_update_) + return; + + window_to_update_->Hide(); + } + + private: + Window* window_to_update_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(WindowDelegateHidingWindow); +}; + +class WindowDelegateAddingAndHidingChild : public MockWindowDelegate { + public: + WindowDelegateAddingAndHidingChild(WindowOcclusionTrackerTest* test) + : test_(test) {} + + void set_window_to_update(Window* window) { window_to_update_ = window; } + + // MockWindowDelegate: + void OnWindowOcclusionChanged( + Window::OcclusionState occlusion_state) override { + MockWindowDelegate::OnWindowOcclusionChanged(occlusion_state); + + if (!window_to_update_) + return; + + // Create a child window and hide it. Since this code runs when occlusion + // has already been recomputed twice, if one of the operations below causes + // occlusion to be recomputed, the test will fail with a DCHECK. + Window* window = + test_->CreateUntrackedWindow(gfx::Rect(0, 0, 5, 5), window_to_update_); + window->Hide(); + } + + private: + WindowOcclusionTrackerTest* test_; + Window* window_to_update_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(WindowDelegateAddingAndHidingChild); +}; + +} // namespace + +// Verify that hiding a window that has a hidden parent doesn't cause occlusion +// to be recomputed. +TEST_F(WindowOcclusionTrackerTest, + HideWindowWithHiddenParentOnOcclusionChange) { + test::WindowOcclusionTrackerTestApi test_api; + + auto* delegate_a = new WindowDelegateAddingAndHidingChild(this); + delegate_a->set_expectation(Window::OcclusionState::VISIBLE); + Window* window_a = CreateTrackedWindow(delegate_a, gfx::Rect(0, 0, 10, 10)); + EXPECT_FALSE(delegate_a->is_expecting_call()); + + auto* delegate_b = new WindowDelegateHidingWindow(); + delegate_b->set_expectation(Window::OcclusionState::VISIBLE); + Window* window_b = CreateTrackedWindow(delegate_b, gfx::Rect(0, 10, 10, 10)); + EXPECT_FALSE(delegate_b->is_expecting_call()); + + // When |window_b| is hidden, it will hide |window_a|. |window_a| will in turn + // add a child to itself and hide it. + delegate_a->set_window_to_update(window_a); + delegate_b->set_window_to_update(window_a); + + delegate_a->set_expectation(Window::OcclusionState::HIDDEN); + delegate_b->set_expectation(Window::OcclusionState::HIDDEN); + EXPECT_FALSE(test_api.WasOcclusionRecomputedTooManyTimes()); + window_b->Hide(); + // Hiding a child to |window_a| and hiding it shouldn't cause occlusion to be + // recomputed too many times. + EXPECT_FALSE(test_api.WasOcclusionRecomputedTooManyTimes()); + EXPECT_FALSE(delegate_a->is_expecting_call()); + EXPECT_FALSE(delegate_b->is_expecting_call()); +} + } // namespace aura diff --git a/chromium/ui/aura/window_port.h b/chromium/ui/aura/window_port.h index a57c5134920..91e6445fcca 100644 --- a/chromium/ui/aura/window_port.h +++ b/chromium/ui/aura/window_port.h @@ -13,6 +13,7 @@ #include "base/callback.h" #include "base/observer_list.h" #include "base/strings/string16.h" +#include "components/viz/common/surfaces/scoped_surface_id_allocator.h" #include "components/viz/common/surfaces/surface_id.h" #include "ui/aura/aura_export.h" #include "ui/base/class_property.h" @@ -90,6 +91,18 @@ class AURA_EXPORT WindowPort { // that does not involve a resize or a device scale factor change. virtual void AllocateLocalSurfaceId() = 0; + // When a child-allocated viz::LocalSurfaceId is being processed, this returns + // true. + virtual bool IsLocalSurfaceIdAllocationSuppressed() const = 0; + + // When a ScopedSurfaceIdAllocator is alive, it prevents the + // allocator from actually allocating. Instead, it triggers its + // |allocation_task| upon destruction. This allows us to issue only one + // allocation during the lifetime. This is used to continue routing and + // processing when a child allocates its own LocalSurfaceId. + virtual viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) = 0; + // Gets the current viz::LocalSurfaceId. The viz::LocalSurfaceId is allocated // lazily on call, and will be updated on changes to size or device scale // factor. diff --git a/chromium/ui/aura/window_port_for_shutdown.cc b/chromium/ui/aura/window_port_for_shutdown.cc index a11a2eed846..1b47442ed6c 100644 --- a/chromium/ui/aura/window_port_for_shutdown.cc +++ b/chromium/ui/aura/window_port_for_shutdown.cc @@ -4,7 +4,6 @@ #include "ui/aura/window_port_for_shutdown.h" -#include "base/memory/ptr_util.h" #include "cc/trees/layer_tree_frame_sink.h" #include "ui/aura/window.h" @@ -59,6 +58,15 @@ WindowPortForShutdown::CreateLayerTreeFrameSink() { void WindowPortForShutdown::AllocateLocalSurfaceId() {} +bool WindowPortForShutdown::IsLocalSurfaceIdAllocationSuppressed() const { + return false; +} + +viz::ScopedSurfaceIdAllocator WindowPortForShutdown::GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) { + return viz::ScopedSurfaceIdAllocator(std::move(allocation_task)); +} + const viz::LocalSurfaceId& WindowPortForShutdown::GetLocalSurfaceId() { return local_surface_id_; } diff --git a/chromium/ui/aura/window_port_for_shutdown.h b/chromium/ui/aura/window_port_for_shutdown.h index e3d97a5b64d..2027fccbecc 100644 --- a/chromium/ui/aura/window_port_for_shutdown.h +++ b/chromium/ui/aura/window_port_for_shutdown.h @@ -40,6 +40,9 @@ class WindowPortForShutdown : public WindowPort { std::unique_ptr<ui::PropertyData> data) override; std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink() override; void AllocateLocalSurfaceId() override; + bool IsLocalSurfaceIdAllocationSuppressed() const override; + viz::ScopedSurfaceIdAllocator GetSurfaceIdAllocator( + base::OnceCallback<void()> allocation_task) override; const viz::LocalSurfaceId& GetLocalSurfaceId() override; void OnEventTargetingPolicyChanged() override; bool ShouldRestackTransientChildren() override; diff --git a/chromium/ui/aura/window_targeter.cc b/chromium/ui/aura/window_targeter.cc index f8dbba61c78..c40ef77ed70 100644 --- a/chromium/ui/aura/window_targeter.cc +++ b/chromium/ui/aura/window_targeter.cc @@ -47,6 +47,22 @@ WindowTargeter::GetExtraHitTestShapeRects(Window* target) const { return nullptr; } +void WindowTargeter::SetInsets(const gfx::Insets& mouse_and_touch_extend) { + SetInsets(mouse_and_touch_extend, mouse_and_touch_extend); +} + +void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend, + const gfx::Insets& touch_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_); +} + Window* WindowTargeter::GetPriorityTargetInRootWindow( Window* root_window, const ui::LocatedEvent& event) { @@ -256,18 +272,6 @@ bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* window) const { void WindowTargeter::OnSetInsets(const gfx::Insets& last_mouse_extend, const gfx::Insets& last_touch_extend) {} -void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend, - const gfx::Insets& touch_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_); -} - Window* WindowTargeter::FindTargetForKeyEvent(Window* window, const ui::KeyEvent& key) { Window* root_window = window->GetRootWindow(); diff --git a/chromium/ui/aura/window_targeter.h b/chromium/ui/aura/window_targeter.h index f03c5b7c54e..ead1801cf50 100644 --- a/chromium/ui/aura/window_targeter.h +++ b/chromium/ui/aura/window_targeter.h @@ -60,6 +60,12 @@ class AURA_EXPORT WindowTargeter : public ui::EventTargeter { virtual std::unique_ptr<HitTestRects> GetExtraHitTestShapeRects( Window* target) const; + // Sets additional mouse and touch insets that are factored into the hit-test + // regions returned by GetHitTestRects. + void SetInsets(const gfx::Insets& mouse_and_touch_extend); + void SetInsets(const gfx::Insets& mouse_extend, + const gfx::Insets& touch_extend); + // If there is a target that takes priority over normal WindowTargeter (such // as a capture window) this returns it. Window* GetPriorityTargetInRootWindow(Window* root_window, @@ -115,11 +121,6 @@ class AURA_EXPORT WindowTargeter : public ui::EventTargeter { virtual void OnSetInsets(const gfx::Insets& last_mouse_extend, const gfx::Insets& last_touch_extend); - // Sets additional mouse and touch insets that are factored into the hit-test - // regions returned by GetHitTestRects. - void SetInsets(const gfx::Insets& mouse_extend, - const gfx::Insets& touch_extend); - const gfx::Insets& mouse_extend() const { return mouse_extend_; } const gfx::Insets& touch_extend() const { return touch_extend_; } diff --git a/chromium/ui/aura/window_tree_host.cc b/chromium/ui/aura/window_tree_host.cc index cbade2495bb..64e336c6ca5 100644 --- a/chromium/ui/aura/window_tree_host.cc +++ b/chromium/ui/aura/window_tree_host.cc @@ -61,8 +61,12 @@ WindowTreeHost* WindowTreeHost::GetForAcceleratedWidget( } void WindowTreeHost::InitHost() { + display::Display display = + display::Screen::GetScreen()->GetDisplayNearestWindow(window()); + device_scale_factor_ = display.device_scale_factor(); + InitCompositor(); - UpdateRootWindowSizeInPixels(GetBoundsInPixels().size()); + UpdateRootWindowSizeInPixels(); Env::GetInstance()->NotifyHostInitialized(this); } @@ -79,16 +83,15 @@ ui::EventSink* WindowTreeHost::event_sink() { } gfx::Transform WindowTreeHost::GetRootTransform() const { - float scale = ui::GetDeviceScaleFactor(window()->layer()); gfx::Transform transform; - transform.Scale(scale, scale); + transform.Scale(device_scale_factor_, device_scale_factor_); transform *= window()->layer()->transform(); return transform; } void WindowTreeHost::SetRootTransform(const gfx::Transform& transform) { window()->SetTransform(transform); - UpdateRootWindowSizeInPixels(GetBoundsInPixels().size()); + UpdateRootWindowSizeInPixels(); } gfx::Transform WindowTreeHost::GetInverseRootTransform() const { @@ -113,25 +116,11 @@ gfx::Transform WindowTreeHost::GetInverseRootTransformForLocalEventCoordinates() return invert; } -void WindowTreeHost::SetOutputSurfacePaddingInPixels( - const gfx::Insets& padding_in_pixels) { - if (output_surface_padding_in_pixels_ == padding_in_pixels) - return; - - output_surface_padding_in_pixels_ = padding_in_pixels; - OnHostResizedInPixels(GetBoundsInPixels().size()); -} - -void WindowTreeHost::UpdateRootWindowSizeInPixels( - const gfx::Size& host_size_in_pixels) { - gfx::Rect bounds(output_surface_padding_in_pixels_.left(), - output_surface_padding_in_pixels_.top(), - host_size_in_pixels.width(), host_size_in_pixels.height()); - float scale_factor = ui::GetDeviceScaleFactor(window()->layer()); - gfx::RectF new_bounds = - gfx::ScaleRect(gfx::RectF(bounds), 1.0f / scale_factor); - window()->layer()->transform().TransformRect(&new_bounds); - window()->SetBounds(gfx::ToEnclosingRect(new_bounds)); +void WindowTreeHost::UpdateRootWindowSizeInPixels() { + gfx::Rect transformed_bounds_in_pixels = + GetTransformedRootWindowBoundsInPixels(GetBoundsInPixels().size()); + window()->SetBounds(transformed_bounds_in_pixels); + window()->SetDeviceScaleFactor(device_scale_factor_); } void WindowTreeHost::ConvertDIPToScreenInPixels(gfx::Point* point) const { @@ -319,12 +308,12 @@ void WindowTreeHost::CreateCompositor(const viz::FrameSinkId& frame_sink_id, void WindowTreeHost::InitCompositor() { DCHECK(!compositor_->root_layer()); - display::Display display = - display::Screen::GetScreen()->GetDisplayNearestWindow(window()); - compositor_->SetScaleAndSize(display.device_scale_factor(), - GetBoundsInPixels().size(), + compositor_->SetScaleAndSize(device_scale_factor_, GetBoundsInPixels().size(), window()->GetLocalSurfaceId()); compositor_->SetRootLayer(window()->layer()); + + display::Display display = + display::Screen::GetScreen()->GetDisplayNearestWindow(window()); compositor_->SetDisplayColorSpace(display.color_space()); } @@ -345,19 +334,19 @@ void WindowTreeHost::OnHostMovedInPixels( void WindowTreeHost::OnHostResizedInPixels( const gfx::Size& new_size_in_pixels) { - gfx::Size adjusted_size(new_size_in_pixels); - adjusted_size.Enlarge(output_surface_padding_in_pixels_.width(), - output_surface_padding_in_pixels_.height()); + display::Display display = + display::Screen::GetScreen()->GetDisplayNearestWindow(window()); + device_scale_factor_ = display.device_scale_factor(); + + // The layer, and the observers should be notified of the + // transformed size of the root window. + UpdateRootWindowSizeInPixels(); // The compositor should have the same size as the native root window host. // Get the latest scale from display because it might have been changed. - compositor_->SetScaleAndSize(ui::GetScaleFactorForNativeView(window()), - adjusted_size, window()->GetLocalSurfaceId()); + compositor_->SetScaleAndSize(device_scale_factor_, new_size_in_pixels, + window()->GetLocalSurfaceId()); - gfx::Size layer_size = GetBoundsInPixels().size(); - // The layer, and the observers should be notified of the - // transformed size of the root window. - UpdateRootWindowSizeInPixels(layer_size); for (WindowTreeHostObserver& observer : observers_) observer.OnHostResized(this); } @@ -415,6 +404,15 @@ void WindowTreeHost::OnDisplayMetricsChanged(const display::Display& display, } } +gfx::Rect WindowTreeHost::GetTransformedRootWindowBoundsInPixels( + const gfx::Size& size_in_pixels) const { + gfx::Rect bounds(size_in_pixels); + gfx::RectF new_bounds = + gfx::ScaleRect(gfx::RectF(bounds), 1.0f / device_scale_factor_); + window()->layer()->transform().TransformRect(&new_bounds); + return gfx::ToEnclosingRect(new_bounds); +} + //////////////////////////////////////////////////////////////////////////////// // WindowTreeHost, private: diff --git a/chromium/ui/aura/window_tree_host.h b/chromium/ui/aura/window_tree_host.h index 4a3e90b7b55..83c144432b3 100644 --- a/chromium/ui/aura/window_tree_host.h +++ b/chromium/ui/aura/window_tree_host.h @@ -10,7 +10,6 @@ #include <memory> #include "base/containers/flat_set.h" -#include "base/event_types.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" @@ -22,11 +21,10 @@ #include "ui/compositor/compositor_observer.h" #include "ui/display/display_observer.h" #include "ui/events/event_source.h" -#include "ui/gfx/geometry/insets.h" +#include "ui/events/platform_event.h" #include "ui/gfx/native_widget_types.h" namespace gfx { -class Insets; class Point; class Rect; class Size; @@ -98,21 +96,12 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, virtual gfx::Transform GetInverseRootTransformForLocalEventCoordinates() const; - // Sets padding applied to the output surface. The output surface is sized to - // to the size of the host plus output surface padding. |window()| is offset - // by |padding_in_pixels|, that is, |window|'s origin is set to - // padding_in_pixels.left(), padding_in_pixels.top(). - // This does not impact the bounds as returned from GetBounds(), only the - // output surface size and location of window(). Additionally window() is - // sized to the size set by bounds (more specifically the size passed to - // OnHostResizedInPixels()), but the location of window() is set to that of - // |padding_in_pixels|. - void SetOutputSurfacePaddingInPixels(const gfx::Insets& padding_in_pixels); - // Updates the root window's size using |host_size_in_pixels|, current // transform and outsets. - virtual void UpdateRootWindowSizeInPixels( - const gfx::Size& host_size_in_pixels); + // TODO(ccameron): Make this function no longer public. The interaction + // between this call, GetBounds, and OnHostResizedInPixels is ambiguous and + // allows for inconsistencies. + void UpdateRootWindowSizeInPixels(); // Converts |point| from the root window's coordinate system to native // screen's. @@ -192,6 +181,11 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, void Hide(); // Gets/Sets the size of the WindowTreeHost (in pixels). + // TODO(ccameron): The existence of OnHostMoved/ResizedInPixels and this + // function create confusion as to the source of the true bounds. Should it + // be expected that this will always return the values most recently + // specified by OnHostMoved/ResizedInPixels? If so, why do we ask the + // sub-classes to return the value when this class already knows the value? virtual gfx::Rect GetBoundsInPixels() const = 0; virtual void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels) = 0; @@ -201,6 +195,10 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, // Releases OS capture of the root window. virtual void ReleaseCapture() = 0; + // Returns the device scale assumed by the WindowTreeHost (set during the + // most recent call to OnHostResizedInPixels). + float device_scale_factor() const { return device_scale_factor_; } + // Requests that |keys| be intercepted at the platform level and routed // directly to the web content. If |keys| is empty, all keys will be // intercepted. Returns a ScopedKeyboardHook instance which stops capturing @@ -232,6 +230,9 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, virtual gfx::Point GetLocationOnScreenInPixels() const = 0; void OnHostMovedInPixels(const gfx::Point& new_location_in_pixels); + // TODO(ccameron): This needs to specify a device scale factor. It should + // arguably be merged with OnHostMovedInPixels (since all callers are pulling + // the size or position from a rect which also feeds OnHostMovedInPixels). void OnHostResizedInPixels(const gfx::Size& new_size_in_pixels); void OnHostWorkspaceChanged(); void OnHostDisplayChanged(); @@ -271,7 +272,12 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, // Stops capturing system keyboard events. virtual void ReleaseSystemKeyEventCapture() = 0; - protected: + // True if |native_key_code| is reserved for an active KeyboardLock request. + virtual bool IsKeyLocked(int native_key_code) = 0; + + virtual gfx::Rect GetTransformedRootWindowBoundsInPixels( + const gfx::Size& size_in_pixels) const; + const base::ObserverList<WindowTreeHostObserver>& observers() const { return observers_; } @@ -305,6 +311,11 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, std::unique_ptr<ui::Compositor> compositor_; + // The device scale factor is snapshotted in OnHostResizedInPixels. + // TODO(ccameron): The size and location from OnHostResizedInPixels and + // OnHostMovedInPixels should be snapshotted here as well. + float device_scale_factor_ = 1.f; + // Last cursor set. Used for testing. gfx::NativeCursor last_cursor_; gfx::Point last_cursor_request_position_in_host_; @@ -319,8 +330,6 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, // Whether the InputMethod instance is owned by this WindowTreeHost. bool owned_input_method_; - gfx::Insets output_surface_padding_in_pixels_; - // Set to the time the synchronization event began. base::TimeTicks synchronization_start_time_; diff --git a/chromium/ui/aura/window_tree_host_platform.cc b/chromium/ui/aura/window_tree_host_platform.cc index 43294f6f232..892fabd5115 100644 --- a/chromium/ui/aura/window_tree_host_platform.cc +++ b/chromium/ui/aura/window_tree_host_platform.cc @@ -44,18 +44,7 @@ WindowTreeHostPlatform::WindowTreeHostPlatform(const gfx::Rect& bounds) : WindowTreeHostPlatform() { bounds_ = bounds; CreateCompositor(); -#if defined(USE_OZONE) - platform_window_ = - ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds); -#elif defined(OS_WIN) - platform_window_.reset(new ui::WinWindow(this, bounds)); -#elif defined(OS_ANDROID) - platform_window_.reset(new ui::PlatformWindowAndroid(this)); -#elif defined(USE_X11) - platform_window_.reset(new ui::X11Window(this, bounds)); -#else - NOTIMPLEMENTED(); -#endif + CreateAndSetDefaultPlatformWindow(); } WindowTreeHostPlatform::WindowTreeHostPlatform() @@ -67,6 +56,21 @@ WindowTreeHostPlatform::WindowTreeHostPlatform( widget_(gfx::kNullAcceleratedWidget), current_cursor_(ui::CursorType::kNull) {} +void WindowTreeHostPlatform::CreateAndSetDefaultPlatformWindow() { +#if defined(USE_OZONE) + platform_window_ = + ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds_); +#elif defined(OS_WIN) + platform_window_.reset(new ui::WinWindow(this, bounds_)); +#elif defined(OS_ANDROID) + platform_window_.reset(new ui::PlatformWindowAndroid(this)); +#elif defined(USE_X11) + platform_window_.reset(new ui::X11Window(this, bounds_)); +#else + NOTIMPLEMENTED(); +#endif +} + void WindowTreeHostPlatform::SetPlatformWindow( std::unique_ptr<ui::PlatformWindow> window) { platform_window_ = std::move(window); @@ -116,12 +120,19 @@ void WindowTreeHostPlatform::ReleaseCapture() { bool WindowTreeHostPlatform::CaptureSystemKeyEventsImpl( base::Optional<base::flat_set<int>> native_key_codes) { + // TODO(680809): Implement as part of the KeyboardLock feature work. NOTIMPLEMENTED(); return false; } void WindowTreeHostPlatform::ReleaseSystemKeyEventCapture() {} +bool WindowTreeHostPlatform::IsKeyLocked(int native_key_code) { + // TODO(680809): Implement as part of the KeyboardLock feature work. + NOTIMPLEMENTED(); + return false; +} + void WindowTreeHostPlatform::SetCursorNative(gfx::NativeCursor cursor) { if (cursor == current_cursor_) return; @@ -149,12 +160,10 @@ void WindowTreeHostPlatform::OnBoundsChanged(const gfx::Rect& new_bounds) { float new_scale = ui::GetScaleFactorForNativeView(window()); gfx::Rect old_bounds = bounds_; bounds_ = new_bounds; - if (bounds_.origin() != old_bounds.origin()) { + if (bounds_.origin() != old_bounds.origin()) OnHostMovedInPixels(bounds_.origin()); - } - if (bounds_.size() != old_bounds.size() || current_scale != new_scale) { + if (bounds_.size() != old_bounds.size() || current_scale != new_scale) OnHostResizedInPixels(bounds_.size()); - } } void WindowTreeHostPlatform::OnDamageRect(const gfx::Rect& damage_rect) { @@ -190,7 +199,9 @@ void WindowTreeHostPlatform::OnAcceleratedWidgetAvailable( gfx::AcceleratedWidget widget, float device_pixel_ratio) { widget_ = widget; - WindowTreeHost::OnAcceleratedWidgetAvailable(); + // This may be called before the Compositor has been created. + if (compositor()) + WindowTreeHost::OnAcceleratedWidgetAvailable(); } void WindowTreeHostPlatform::OnAcceleratedWidgetDestroyed() { diff --git a/chromium/ui/aura/window_tree_host_platform.h b/chromium/ui/aura/window_tree_host_platform.h index 5935d1634b6..893a8da1035 100644 --- a/chromium/ui/aura/window_tree_host_platform.h +++ b/chromium/ui/aura/window_tree_host_platform.h @@ -48,8 +48,15 @@ class AURA_EXPORT WindowTreeHostPlatform : public WindowTreeHost, WindowTreeHostPlatform(); explicit WindowTreeHostPlatform(std::unique_ptr<WindowPort> window_port); + // Creates a ui::PlatformWindow appropriate for the current platform and + // installs it at as the PlatformWindow for this WindowTreeHostPlatform. + void CreateAndSetDefaultPlatformWindow(); + void SetPlatformWindow(std::unique_ptr<ui::PlatformWindow> window); ui::PlatformWindow* platform_window() { return platform_window_.get(); } + const ui::PlatformWindow* platform_window() const { + return platform_window_.get(); + } // ui::PlatformWindowDelegate: void OnBoundsChanged(const gfx::Rect& new_bounds) override; @@ -66,6 +73,7 @@ class AURA_EXPORT WindowTreeHostPlatform : public WindowTreeHost, bool CaptureSystemKeyEventsImpl( base::Optional<base::flat_set<int>> native_key_codes) override; void ReleaseSystemKeyEventCapture() override; + bool IsKeyLocked(int native_key_code) override; private: gfx::AcceleratedWidget widget_; diff --git a/chromium/ui/aura/window_tree_host_unittest.cc b/chromium/ui/aura/window_tree_host_unittest.cc index 6dbddb67770..a2442b8950b 100644 --- a/chromium/ui/aura/window_tree_host_unittest.cc +++ b/chromium/ui/aura/window_tree_host_unittest.cc @@ -65,14 +65,8 @@ TEST_F(WindowTreeHostTest, DPIWindowSize) { host()->SetRootTransform(transform); EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds()); - gfx::Insets padding(1, 2, 3, 4); - // Padding is in physical pixels. - host()->SetOutputSurfacePaddingInPixels(padding); - gfx::Rect padded_rect = starting_bounds; - padded_rect.Inset(-padding); - EXPECT_EQ(padded_rect.size(), host()->compositor()->size()); EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels()); - EXPECT_EQ(gfx::Rect(1, 1, 534, 401), root_window()->bounds()); + EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds()); EXPECT_EQ(gfx::Vector2dF(0, 0), host()->compositor()->root_layer()->subpixel_position_offset()); } diff --git a/chromium/ui/base/BUILD.gn b/chromium/ui/base/BUILD.gn index b5904fe8e63..b209b397877 100644 --- a/chromium/ui/base/BUILD.gn +++ b/chromium/ui/base/BUILD.gn @@ -125,8 +125,6 @@ component("base") { "cocoa/focus_tracker.mm", "cocoa/focus_window_set.h", "cocoa/focus_window_set.mm", - "cocoa/fullscreen_window_manager.h", - "cocoa/fullscreen_window_manager.mm", "cocoa/hover_button.h", "cocoa/hover_button.mm", "cocoa/hover_image_button.h", @@ -143,6 +141,8 @@ component("base") { "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", @@ -312,9 +312,6 @@ component("base") { "win/mouse_wheel_util.h", "win/open_file_name_win.cc", "win/open_file_name_win.h", - "win/osk_display_manager.cc", - "win/osk_display_manager.h", - "win/osk_display_observer.h", "win/scoped_ole_initializer.cc", "win/scoped_ole_initializer.h", "win/shell.cc", @@ -731,6 +728,8 @@ static_library("test_support") { "ime/dummy_input_method.h", "ime/dummy_text_input_client.cc", "ime/dummy_text_input_client.h", + "ime/win/mock_tsf_bridge.cc", + "ime/win/mock_tsf_bridge.h", ] deps += [ @@ -849,7 +848,6 @@ test("ui_base_unittests") { "cocoa/controls/hyperlink_button_cell_unittest.mm", "cocoa/controls/hyperlink_text_view_unittest.mm", "cocoa/focus_tracker_unittest.mm", - "cocoa/fullscreen_window_manager_unittest.mm", "cocoa/hover_button_unittest.mm", "cocoa/hover_image_button_unittest.mm", "cocoa/menu_controller_unittest.mm", @@ -878,7 +876,9 @@ test("ui_base_unittests") { "ime/input_method_base_unittest.cc", "ime/input_method_chromeos_unittest.cc", "ime/win/imm32_manager_unittest.cc", + "ime/win/on_screen_keyboard_display_manager_unittest.cc", "ime/win/tsf_input_scope_unittest.cc", + "ime/win/tsf_text_store_unittest.cc", ] if (is_linux && use_aura && !is_chromeos) { sources += [ "ime/input_method_auralinux_unittest.cc" ] @@ -895,7 +895,7 @@ test("ui_base_unittests") { ":ui_base_unittests_bundle_data", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//net", "//net:test_support", "//skia", @@ -928,9 +928,9 @@ test("ui_base_unittests") { if (is_win) { sources += [ "dragdrop/os_exchange_data_win_unittest.cc", + "win/direct_manipulation_unittest.cc", "win/hwnd_subclass_unittest.cc", "win/open_file_name_win_unittest.cc", - "win/osk_display_manager_unittest.cc", ] ldflags = [ @@ -1034,6 +1034,8 @@ test("ui_base_unittests") { if (is_mac) { mac_framework_bundle("ui_unittests_framework") { testonly = true + framework_version = "U" + framework_contents = [ "Resources" ] deps = [ "//ui/resources:ui_test_pak_bundle_data", ] diff --git a/chromium/ui/base/accelerators/accelerator.cc b/chromium/ui/base/accelerators/accelerator.cc index 0694f7433df..a6756c20f22 100644 --- a/chromium/ui/base/accelerators/accelerator.cc +++ b/chromium/ui/base/accelerators/accelerator.cc @@ -5,9 +5,11 @@ #include "ui/base/accelerators/accelerator.h" #include <stdint.h> +#include <tuple> #include "base/i18n/rtl.h" #include "base/logging.h" +#include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -39,10 +41,12 @@ Accelerator::Accelerator() : Accelerator(VKEY_UNKNOWN, EF_NONE) {} Accelerator::Accelerator(KeyboardCode key_code, int modifiers, - KeyState key_state) + KeyState key_state, + base::TimeTicks time_stamp) : key_code_(key_code), key_state_(key_state), modifiers_(modifiers & kInterestingFlagsMask), + time_stamp_(time_stamp), interrupted_by_mouse_event_(false) {} Accelerator::Accelerator(const KeyEvent& key_event) @@ -51,12 +55,14 @@ Accelerator::Accelerator(const KeyEvent& key_event) : KeyState::RELEASED), // |modifiers_| may include the repeat flag. modifiers_(key_event.flags() & kInterestingFlagsMask), + time_stamp_(key_event.time_stamp()), interrupted_by_mouse_event_(false) {} Accelerator::Accelerator(const Accelerator& accelerator) { key_code_ = accelerator.key_code_; key_state_ = accelerator.key_state_; modifiers_ = accelerator.modifiers_; + time_stamp_ = accelerator.time_stamp_; interrupted_by_mouse_event_ = accelerator.interrupted_by_mouse_event_; if (accelerator.platform_accelerator_) platform_accelerator_ = accelerator.platform_accelerator_->CreateCopy(); @@ -74,7 +80,7 @@ KeyEvent Accelerator::ToKeyEvent() const { return KeyEvent(key_state() == Accelerator::KeyState::PRESSED ? ET_KEY_PRESSED : ET_KEY_RELEASED, - key_code(), modifiers()); + key_code(), modifiers(), time_stamp()); } Accelerator& Accelerator::operator=(const Accelerator& accelerator) { @@ -82,6 +88,7 @@ Accelerator& Accelerator::operator=(const Accelerator& accelerator) { key_code_ = accelerator.key_code_; key_state_ = accelerator.key_state_; modifiers_ = accelerator.modifiers_; + time_stamp_ = accelerator.time_stamp_; interrupted_by_mouse_event_ = accelerator.interrupted_by_mouse_event_; if (accelerator.platform_accelerator_) platform_accelerator_ = accelerator.platform_accelerator_->CreateCopy(); @@ -92,14 +99,10 @@ Accelerator& Accelerator::operator=(const Accelerator& accelerator) { } bool Accelerator::operator <(const Accelerator& rhs) const { - if (key_code_ != rhs.key_code_) - return key_code_ < rhs.key_code_; - if (key_state_ != rhs.key_state_) { - return static_cast<int32_t>(key_state_) < - static_cast<int32_t>(rhs.key_state_); - } - return MaskOutKeyEventFlags(modifiers_) < - MaskOutKeyEventFlags(rhs.modifiers_); + const int modifiers_with_mask = MaskOutKeyEventFlags(modifiers_); + const int rhs_modifiers_with_mask = MaskOutKeyEventFlags(rhs.modifiers_); + return std::tie(key_code_, key_state_, modifiers_with_mask) < + std::tie(rhs.key_code_, rhs.key_state_, rhs_modifiers_with_mask); } bool Accelerator::operator ==(const Accelerator& rhs) const { @@ -134,6 +137,170 @@ bool Accelerator::IsRepeat() const { } base::string16 Accelerator::GetShortcutText() const { + base::string16 shortcut; + +#if defined(OS_MACOSX) + shortcut = KeyCodeToMacSymbol(key_code_); +#else + shortcut = KeyCodeToName(key_code_); +#endif + + if (shortcut.empty()) { +#if defined(OS_WIN) + // Our fallback is to try translate the key code to a regular character + // unless it is one of digits (VK_0 to VK_9). Some keyboard + // layouts have characters other than digits assigned in + // an unshifted mode (e.g. French AZERY layout has 'a with grave + // accent' for '0'). For display in the menu (e.g. Ctrl-0 for the + // default zoom level), we leave VK_[0-9] alone without translation. + wchar_t key; + if (base::IsAsciiDigit(key_code_)) + key = static_cast<wchar_t>(key_code_); + else + key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR)); + shortcut += key; +#elif defined(USE_AURA) || defined(OS_MACOSX) + const uint16_t c = DomCodeToUsLayoutCharacter( + UsLayoutKeyboardCodeToDomCode(key_code_), false); + if (c != 0) + shortcut += + static_cast<base::string16::value_type>(base::ToUpperASCII(c)); +#endif + } + + // Checking whether the character used for the accelerator is alphanumeric. + // If it is not, then we need to adjust the string later on if the locale is + // right-to-left. See below for more information of why such adjustment is + // required. + base::string16 shortcut_rtl; + bool adjust_shortcut_for_rtl = false; + if (base::i18n::IsRTL() && shortcut.length() == 1 && + !base::IsAsciiAlpha(shortcut[0]) && !base::IsAsciiDigit(shortcut[0])) { + adjust_shortcut_for_rtl = true; + shortcut_rtl.assign(shortcut); + } + +#if defined(OS_MACOSX) + shortcut = ApplyShortFormModifiers(shortcut); +#else + shortcut = ApplyLongFormModifiers(shortcut); +#endif + + // For some reason, menus in Windows ignore standard Unicode directionality + // marks (such as LRE, PDF, etc.). On RTL locales, we use RTL menus and + // therefore any text we draw for the menu items is drawn in an RTL context. + // Thus, the text "Ctrl++" (which we currently use for the Zoom In option) + // appears as "++Ctrl" in RTL because the Unicode BiDi algorithm puts + // punctuations on the left when the context is right-to-left. Shortcuts that + // do not end with a punctuation mark (such as "Ctrl+H" do not have this + // problem). + // + // The only way to solve this problem is to adjust the string if the locale + // is RTL so that it is drawn correctly in an RTL context. Instead of + // returning "Ctrl++" in the above example, we return "++Ctrl". This will + // cause the text to appear as "Ctrl++" when Windows draws the string in an + // RTL context because the punctuation no longer appears at the end of the + // string. + // + // TODO(idana) bug# 1232732: this hack can be avoided if instead of using + // views::Menu we use views::MenuItemView because the latter is a View + // subclass and therefore it supports marking text as RTL or LTR using + // standard Unicode directionality marks. + if (adjust_shortcut_for_rtl) { + int key_length = static_cast<int>(shortcut_rtl.length()); + DCHECK_GT(key_length, 0); + shortcut_rtl.append(base::ASCIIToUTF16("+")); + + // Subtracting the size of the shortcut key and 1 for the '+' sign. + shortcut_rtl.append(shortcut, 0, shortcut.length() - key_length - 1); + shortcut.swap(shortcut_rtl); + } + + return shortcut; +} + +base::string16 Accelerator::ApplyLongFormModifiers( + base::string16 shortcut) const { + if (IsShiftDown()) + shortcut = l10n_util::GetStringFUTF16(IDS_APP_SHIFT_MODIFIER, shortcut); + + // Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut. + // See http://blogs.msdn.com/oldnewthing/archive/2004/03/29/101121.aspx for + // more information. + if (IsCtrlDown()) + shortcut = l10n_util::GetStringFUTF16(IDS_APP_CONTROL_MODIFIER, shortcut); + else if (IsAltDown()) + shortcut = l10n_util::GetStringFUTF16(IDS_APP_ALT_MODIFIER, shortcut); + + if (IsCmdDown()) { +#if defined(OS_MACOSX) + shortcut = l10n_util::GetStringFUTF16(IDS_APP_COMMAND_MODIFIER, shortcut); +#elif defined(OS_CHROMEOS) + shortcut = l10n_util::GetStringFUTF16(IDS_APP_SEARCH_MODIFIER, shortcut); +#else + NOTREACHED(); +#endif + } + + return shortcut; +} + +base::string16 Accelerator::ApplyShortFormModifiers( + base::string16 shortcut) const { + const base::char16 kCommandSymbol[] = {0x2318, 0}; + const base::char16 kCtrlSymbol[] = {0x2303, 0}; + const base::char16 kShiftSymbol[] = {0x21e7, 0}; + const base::char16 kOptionSymbol[] = {0x2325, 0}; + const base::char16 kNoSymbol[] = {0}; + + std::vector<base::string16> parts; + parts.push_back(base::string16(IsCtrlDown() ? kCtrlSymbol : kNoSymbol)); + parts.push_back(base::string16(IsAltDown() ? kOptionSymbol : kNoSymbol)); + parts.push_back(base::string16(IsShiftDown() ? kShiftSymbol : kNoSymbol)); + parts.push_back(base::string16(IsCmdDown() ? kCommandSymbol : kNoSymbol)); + parts.push_back(shortcut); + return base::StrCat(parts); +} + +#if defined(OS_MACOSX) +base::string16 Accelerator::KeyCodeToMacSymbol(KeyboardCode key_code) const { + switch (key_code) { + case VKEY_CAPITAL: + return base::string16({0x21ea, 0}); + case VKEY_RETURN: + return base::string16({0x2324, 0}); + case VKEY_BACK: + return base::string16({0x232b, 0}); + case VKEY_ESCAPE: + return base::string16({0x238b, 0}); + case VKEY_RIGHT: + return base::string16({0x2192, 0}); + case VKEY_LEFT: + return base::string16({0x2190, 0}); + case VKEY_UP: + return base::string16({0x2191, 0}); + case VKEY_DOWN: + return base::string16({0x2193, 0}); + case VKEY_PRIOR: + return base::string16({0x21de, 0}); + case VKEY_NEXT: + return base::string16({0x21df, 0}); + case VKEY_HOME: + return base::string16({0x2196, 0}); + case VKEY_END: + return base::string16({0x2198, 0}); + case VKEY_TAB: + return base::string16({0x21e5, 0}); + // Mac has a shift-tab icon (0x21e4) but we don't use it. + // "Space" and some other keys are written out; fall back to KeyCodeToName() + // for those (and any other unhandled keys). + default: + return KeyCodeToName(key_code); + } +} +#endif // OS_MACOSX + +base::string16 Accelerator::KeyCodeToName(KeyboardCode key_code) const { int string_id = 0; switch (key_code_) { case VKEY_TAB: @@ -142,9 +309,6 @@ base::string16 Accelerator::GetShortcutText() const { case VKEY_RETURN: string_id = IDS_APP_ENTER_KEY; break; - case VKEY_ESCAPE: - string_id = IDS_APP_ESC_KEY; - break; case VKEY_SPACE: string_id = IDS_APP_SPACE_KEY; break; @@ -178,6 +342,9 @@ base::string16 Accelerator::GetShortcutText() const { case VKEY_DOWN: string_id = IDS_APP_DOWN_ARROW_KEY; break; + case VKEY_ESCAPE: + string_id = IDS_APP_ESC_KEY; + break; case VKEY_BACK: string_id = IDS_APP_BACKSPACE_KEY; break; @@ -208,97 +375,7 @@ base::string16 Accelerator::GetShortcutText() const { default: break; } - - base::string16 shortcut; - if (!string_id) { -#if defined(OS_WIN) - // Our fallback is to try translate the key code to a regular character - // unless it is one of digits (VK_0 to VK_9). Some keyboard - // layouts have characters other than digits assigned in - // an unshifted mode (e.g. French AZERY layout has 'a with grave - // accent' for '0'). For display in the menu (e.g. Ctrl-0 for the - // default zoom level), we leave VK_[0-9] alone without translation. - wchar_t key; - if (base::IsAsciiDigit(key_code_)) - key = static_cast<wchar_t>(key_code_); - else - key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR)); - shortcut += key; -#elif defined(USE_AURA) || defined(OS_MACOSX) - const uint16_t c = DomCodeToUsLayoutCharacter( - UsLayoutKeyboardCodeToDomCode(key_code_), false); - if (c != 0) - shortcut += - static_cast<base::string16::value_type>(base::ToUpperASCII(c)); -#endif - } else { - shortcut = l10n_util::GetStringUTF16(string_id); - } - - // Checking whether the character used for the accelerator is alphanumeric. - // If it is not, then we need to adjust the string later on if the locale is - // right-to-left. See below for more information of why such adjustment is - // required. - base::string16 shortcut_rtl; - bool adjust_shortcut_for_rtl = false; - if (base::i18n::IsRTL() && shortcut.length() == 1 && - !base::IsAsciiAlpha(shortcut[0]) && !base::IsAsciiDigit(shortcut[0])) { - adjust_shortcut_for_rtl = true; - shortcut_rtl.assign(shortcut); - } - - if (IsShiftDown()) - shortcut = l10n_util::GetStringFUTF16(IDS_APP_SHIFT_MODIFIER, shortcut); - - // Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut. - // See http://blogs.msdn.com/oldnewthing/archive/2004/03/29/101121.aspx for - // more information. - if (IsCtrlDown()) - shortcut = l10n_util::GetStringFUTF16(IDS_APP_CONTROL_MODIFIER, shortcut); - else if (IsAltDown()) - shortcut = l10n_util::GetStringFUTF16(IDS_APP_ALT_MODIFIER, shortcut); - - if (IsCmdDown()) { -#if defined(OS_MACOSX) - shortcut = l10n_util::GetStringFUTF16(IDS_APP_COMMAND_MODIFIER, shortcut); -#elif defined(OS_CHROMEOS) - shortcut = l10n_util::GetStringFUTF16(IDS_APP_SEARCH_MODIFIER, shortcut); -#else - NOTREACHED(); -#endif - } - - // For some reason, menus in Windows ignore standard Unicode directionality - // marks (such as LRE, PDF, etc.). On RTL locales, we use RTL menus and - // therefore any text we draw for the menu items is drawn in an RTL context. - // Thus, the text "Ctrl++" (which we currently use for the Zoom In option) - // appears as "++Ctrl" in RTL because the Unicode BiDi algorithm puts - // punctuations on the left when the context is right-to-left. Shortcuts that - // do not end with a punctuation mark (such as "Ctrl+H" do not have this - // problem). - // - // The only way to solve this problem is to adjust the string if the locale - // is RTL so that it is drawn correctly in an RTL context. Instead of - // returning "Ctrl++" in the above example, we return "++Ctrl". This will - // cause the text to appear as "Ctrl++" when Windows draws the string in an - // RTL context because the punctuation no longer appears at the end of the - // string. - // - // TODO(idana) bug# 1232732: this hack can be avoided if instead of using - // views::Menu we use views::MenuItemView because the latter is a View - // subclass and therefore it supports marking text as RTL or LTR using - // standard Unicode directionality marks. - if (adjust_shortcut_for_rtl) { - int key_length = static_cast<int>(shortcut_rtl.length()); - DCHECK_GT(key_length, 0); - shortcut_rtl.append(base::ASCIIToUTF16("+")); - - // Subtracting the size of the shortcut key and 1 for the '+' sign. - shortcut_rtl.append(shortcut, 0, shortcut.length() - key_length - 1); - shortcut.swap(shortcut_rtl); - } - - return shortcut; + return string_id ? l10n_util::GetStringUTF16(string_id) : base::string16(); } } // namespace ui diff --git a/chromium/ui/base/accelerators/accelerator.h b/chromium/ui/base/accelerators/accelerator.h index e6033bd0830..652aa82d6e4 100644 --- a/chromium/ui/base/accelerators/accelerator.h +++ b/chromium/ui/base/accelerators/accelerator.h @@ -15,6 +15,8 @@ #include <utility> #include "base/strings/string16.h" +#include "base/time/time.h" +#include "build/build_config.h" #include "ui/base/accelerators/platform_accelerator.h" #include "ui/base/ui_base_export.h" #include "ui/events/event_constants.h" @@ -44,7 +46,8 @@ class UI_BASE_EXPORT Accelerator { // NOTE: this constructor strips out non key related flags. Accelerator(KeyboardCode key_code, int modifiers, - KeyState key_state = KeyState::PRESSED); + KeyState key_state = KeyState::PRESSED, + base::TimeTicks time_stamp = base::TimeTicks()); explicit Accelerator(const KeyEvent& key_event); Accelerator(const Accelerator& accelerator); ~Accelerator(); @@ -73,6 +76,8 @@ class UI_BASE_EXPORT Accelerator { int modifiers() const { return modifiers_; } + base::TimeTicks time_stamp() const { return time_stamp_; } + bool IsShiftDown() const; bool IsCtrlDown() const; bool IsAltDown() const; @@ -100,6 +105,14 @@ class UI_BASE_EXPORT Accelerator { } private: + base::string16 ApplyLongFormModifiers(base::string16 shortcut) const; + base::string16 ApplyShortFormModifiers(base::string16 shortcut) const; + +#if defined(OS_MACOSX) + base::string16 KeyCodeToMacSymbol(KeyboardCode key_code) const; +#endif + base::string16 KeyCodeToName(KeyboardCode key_code) const; + // The keycode (VK_...). KeyboardCode key_code_; @@ -108,6 +121,9 @@ class UI_BASE_EXPORT Accelerator { // The state of the Shift/Ctrl/Alt keys. This corresponds to Event::flags(). int modifiers_; + // The |time_stamp_| of the KeyEvent. + base::TimeTicks time_stamp_; + // Stores platform specific data. May be NULL. // TODO: this is only used in Mac code and should be removed from here. // http://crbug.com/702823. diff --git a/chromium/ui/base/accelerators/accelerator_unittest.cc b/chromium/ui/base/accelerators/accelerator_unittest.cc index c6ec7b26c53..ad7134c90de 100644 --- a/chromium/ui/base/accelerators/accelerator_unittest.cc +++ b/chromium/ui/base/accelerators/accelerator_unittest.cc @@ -4,7 +4,11 @@ #include "ui/base/accelerators/accelerator.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" namespace ui { @@ -19,4 +23,47 @@ TEST(AcceleratorTest, Repeat) { EXPECT_TRUE(accelerator_b_copy.IsRepeat()); } +TEST(AcceleratorTest, TimeStamp) { + const Accelerator accelerator_a(VKEY_A, EF_NONE); + EXPECT_EQ(base::TimeTicks(), accelerator_a.time_stamp()); + + const base::TimeTicks event_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(1); + KeyEvent keyevent(ET_KEY_PRESSED, VKEY_SPACE, EF_NONE, event_time); + + const Accelerator accelerator_b(keyevent); + EXPECT_EQ(event_time, accelerator_b.time_stamp()); +} + +#if defined(OS_ANDROID) +// Keyboard shortcuts don't have meaningful text on Android. +#define MAYBE_GetShortcutText DISABLED_GetShortcutText +#else +#define MAYBE_GetShortcutText GetShortcutText +#endif +TEST(AcceleratorTest, MAYBE_GetShortcutText) { + struct { + KeyboardCode code; + int modifiers; + const char* expected_long; + const char* expected_short; + } keys[] = { + {VKEY_Q, EF_CONTROL_DOWN | EF_SHIFT_DOWN, "Ctrl+Shift+Q", "\u2303\u21e7Q"}, + {VKEY_A, EF_ALT_DOWN | EF_SHIFT_DOWN, "Alt+Shift+A", "\u2325\u21e7A"}, +#if defined(OS_MACOSX) + {VKEY_T, EF_COMMAND_DOWN | EF_CONTROL_DOWN, nullptr, "\u2303\u2318T"}, +#endif + }; + + for (const auto& key : keys) { + base::string16 text = + Accelerator(key.code, key.modifiers).GetShortcutText(); +#if defined(OS_MACOSX) + EXPECT_EQ(text, base::UTF8ToUTF16(key.expected_short)); +#else + EXPECT_EQ(text, base::UTF8ToUTF16(key.expected_long)); +#endif + } +} + } // namespace ui diff --git a/chromium/ui/base/accelerators/mojo/BUILD.gn b/chromium/ui/base/accelerators/mojo/BUILD.gn index 3dd72e7416a..4e028283fc7 100644 --- a/chromium/ui/base/accelerators/mojo/BUILD.gn +++ b/chromium/ui/base/accelerators/mojo/BUILD.gn @@ -9,6 +9,7 @@ mojom("interfaces") { "accelerator.mojom", ] public_deps = [ + "//mojo/public/mojom/base", "//ui/events/mojo:interfaces", ] } @@ -19,6 +20,7 @@ source_set("struct_traits") { ] public_deps = [ ":interfaces", + "//mojo/public/mojom/base", "//ui/base", "//ui/events", ] diff --git a/chromium/ui/base/accelerators/mojo/DEPS b/chromium/ui/base/accelerators/mojo/DEPS new file mode 100644 index 00000000000..6cbc2015d05 --- /dev/null +++ b/chromium/ui/base/accelerators/mojo/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+mojo/common", + "+mojo/public/cpp/base", +] diff --git a/chromium/ui/base/accelerators/mojo/accelerator.mojom b/chromium/ui/base/accelerators/mojo/accelerator.mojom index 9bb0329d2f3..77a4029bbe3 100644 --- a/chromium/ui/base/accelerators/mojo/accelerator.mojom +++ b/chromium/ui/base/accelerators/mojo/accelerator.mojom @@ -4,6 +4,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"; @@ -20,4 +21,5 @@ struct Accelerator { int32 key_code; AcceleratorKeyState key_state; int32 modifiers; + mojo_base.mojom.TimeTicks time_stamp; }; diff --git a/chromium/ui/base/accelerators/mojo/accelerator_struct_traits.h b/chromium/ui/base/accelerators/mojo/accelerator_struct_traits.h index f42de0d797c..c0dc969d7fc 100644 --- a/chromium/ui/base/accelerators/mojo/accelerator_struct_traits.h +++ b/chromium/ui/base/accelerators/mojo/accelerator_struct_traits.h @@ -5,6 +5,7 @@ #ifndef UI_BASE_ACCELERATORS_MOJO_ACCELERATOR_STRUCT_TRAITS_H_ #define UI_BASE_ACCELERATORS_MOJO_ACCELERATOR_STRUCT_TRAITS_H_ +#include "mojo/public/cpp/base/time_mojom_traits.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/accelerators/mojo/accelerator.mojom.h" #include "ui/events/keycodes/keyboard_codes.h" @@ -49,13 +50,18 @@ struct StructTraits<ui::mojom::AcceleratorDataView, ui::Accelerator> { return p.key_state(); } static int32_t modifiers(const ui::Accelerator& p) { return p.modifiers(); } + static base::TimeTicks time_stamp(const ui::Accelerator& p) { + return p.time_stamp(); + } static bool Read(ui::mojom::AcceleratorDataView data, ui::Accelerator* out) { ui::Accelerator::KeyState key_state; if (!data.ReadKeyState(&key_state)) return false; + base::TimeTicks time_stamp; + if (!data.ReadTimeStamp(&time_stamp)) + return false; *out = ui::Accelerator(static_cast<ui::KeyboardCode>(data.key_code()), - data.modifiers()); - out->set_key_state(key_state); + data.modifiers(), key_state, time_stamp); return true; } }; diff --git a/chromium/ui/base/accelerators/mojo/accelerator_struct_traits_unittest.cc b/chromium/ui/base/accelerators/mojo/accelerator_struct_traits_unittest.cc index 24256753a52..08c894fad55 100644 --- a/chromium/ui/base/accelerators/mojo/accelerator_struct_traits_unittest.cc +++ b/chromium/ui/base/accelerators/mojo/accelerator_struct_traits_unittest.cc @@ -12,8 +12,10 @@ namespace ui { TEST(AcceleratorStructTraitsTest, SerializeAndDeserialize1) { - Accelerator accelerator(KeyboardCode::VKEY_TAB, EF_NUM_LOCK_ON); - accelerator.set_key_state(ui::Accelerator::KeyState::RELEASED); + Accelerator accelerator( + KeyboardCode::VKEY_TAB, EF_NUM_LOCK_ON, + ui::Accelerator::KeyState::RELEASED, + base::TimeTicks() + base::TimeDelta::FromMilliseconds(1)); Accelerator deserialized; ASSERT_TRUE(mojom::Accelerator::Deserialize( mojom::Accelerator::Serialize(&accelerator), &deserialized)); diff --git a/chromium/ui/base/base_window.h b/chromium/ui/base/base_window.h index f6af762c130..8d4dd05dfae 100644 --- a/chromium/ui/base/base_window.h +++ b/chromium/ui/base/base_window.h @@ -66,6 +66,9 @@ class UI_BASE_EXPORT BaseWindow { // Hides the window. virtual void Hide() = 0; + // Returns whether the window is visible. + virtual bool IsVisible() const = 0; + // Show the window, but do not activate it. Does nothing if window // is already visible. virtual void ShowInactive() = 0; diff --git a/chromium/ui/base/class_property_unittest.cc b/chromium/ui/base/class_property_unittest.cc index 6651c0e79f5..ae9e9019896 100644 --- a/chromium/ui/base/class_property_unittest.cc +++ b/chromium/ui/base/class_property_unittest.cc @@ -12,7 +12,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ui/base/clipboard/clipboard_aurax11.cc b/chromium/ui/base/clipboard/clipboard_aurax11.cc index 3745f43f580..d071d155a65 100644 --- a/chromium/ui/base/clipboard/clipboard_aurax11.cc +++ b/chromium/ui/base/clipboard/clipboard_aurax11.cc @@ -46,7 +46,7 @@ const char kMimeTypeFilename[] = "chromium/filename"; /////////////////////////////////////////////////////////////////////////////// // Uses the XFixes API to provide sequence numbers for GetSequenceNumber(). -class SelectionChangeObserver : public ui::PlatformEventObserver { +class SelectionChangeObserver : public PlatformEventObserver { public: static SelectionChangeObserver* GetInstance(); @@ -61,9 +61,9 @@ class SelectionChangeObserver : public ui::PlatformEventObserver { SelectionChangeObserver(); ~SelectionChangeObserver() override; - // ui::PlatformEventObserver: - void WillProcessEvent(const ui::PlatformEvent& event) override; - void DidProcessEvent(const ui::PlatformEvent& event) override {} + // PlatformEventObserver: + void WillProcessEvent(const PlatformEvent& event) override; + void DidProcessEvent(const PlatformEvent& event) override {} int event_base_; Atom clipboard_atom_; @@ -95,7 +95,7 @@ SelectionChangeObserver::SelectionChangeObserver() XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask); - ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); + PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); } } @@ -107,7 +107,7 @@ SelectionChangeObserver* SelectionChangeObserver::GetInstance() { return base::Singleton<SelectionChangeObserver>::get(); } -void SelectionChangeObserver::WillProcessEvent(const ui::PlatformEvent& event) { +void SelectionChangeObserver::WillProcessEvent(const PlatformEvent& event) { if (event->type == event_base_ + XFixesSelectionNotify) { XFixesSelectionNotifyEvent* ev = reinterpret_cast<XFixesSelectionNotifyEvent*>(event); diff --git a/chromium/ui/base/clipboard/clipboard_mac_unittest.mm b/chromium/ui/base/clipboard/clipboard_mac_unittest.mm index 475db134892..b196332a166 100644 --- a/chromium/ui/base/clipboard/clipboard_mac_unittest.mm +++ b/chromium/ui/base/clipboard/clipboard_mac_unittest.mm @@ -58,7 +58,12 @@ class ClipboardMacTest : public PlatformTest { } }; -TEST_F(ClipboardMacTest, ReadImageRetina) { +#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) +#define MAYBE_ReadImageRetina DISABLED_ReadImageRetina +#else +#define MAYBE_ReadImageRetina ReadImageRetina +#endif +TEST_F(ClipboardMacTest, MAYBE_ReadImageRetina) { int32_t width = 99; int32_t height = 101; scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard; @@ -74,7 +79,12 @@ TEST_F(ClipboardMacTest, ReadImageRetina) { EXPECT_EQ(2 * height, bitmap.height()); } -TEST_F(ClipboardMacTest, ReadImageNonRetina) { +#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) +#define MAYBE_ReadImageNonRetina DISABLED_ReadImageNonRetina +#else +#define MAYBE_ReadImageNonRetina ReadImageNonRetina +#endif +TEST_F(ClipboardMacTest, MAYBE_ReadImageNonRetina) { int32_t width = 99; int32_t height = 101; scoped_refptr<ui::UniquePasteboard> pasteboard = new ui::UniquePasteboard; diff --git a/chromium/ui/base/cocoa/fullscreen_window_manager.h b/chromium/ui/base/cocoa/fullscreen_window_manager.h deleted file mode 100644 index be59cad49c3..00000000000 --- a/chromium/ui/base/cocoa/fullscreen_window_manager.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_COCOA_FULLSCREEN_WINDOW_MANAGER_H_ -#define UI_BASE_COCOA_FULLSCREEN_WINDOW_MANAGER_H_ - -#import <Cocoa/Cocoa.h> - -#include "base/mac/mac_util.h" -#include "base/mac/scoped_nsobject.h" -#include "ui/base/ui_base_export.h" - -// A utility class to manage the fullscreen mode for a given window. This class -// also updates the window frame if the screen changes. -UI_BASE_EXPORT -@interface FullscreenWindowManager : NSObject { - @private - base::scoped_nsobject<NSWindow> window_; - // Explicitly keep track of the screen we want to position the window in. - // This is better than using -[NSWindow screen] because that might change if - // the screen changes to a low resolution. - base::scoped_nsobject<NSScreen> desiredScreen_; - base::mac::FullScreenMode fullscreenMode_; - BOOL fullscreenActive_; -} - -- (id)initWithWindow:(NSWindow*)window - desiredScreen:(NSScreen*)desiredScreen; - -// Enables fullscreen mode which causes the menubar and dock to be hidden as -// needed. -- (void)enterFullscreenMode; - -// Exists fullscreen mode which stops hiding the menubar and dock. -- (void)exitFullscreenMode; - -@end - -#endif // UI_BASE_COCOA_FULLSCREEN_WINDOW_MANAGER_H_ diff --git a/chromium/ui/base/cocoa/fullscreen_window_manager.mm b/chromium/ui/base/cocoa/fullscreen_window_manager.mm deleted file mode 100644 index 15a5948a7b0..00000000000 --- a/chromium/ui/base/cocoa/fullscreen_window_manager.mm +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "ui/base/cocoa/fullscreen_window_manager.h" - -namespace { - -// Get the screen with the menu bar. -NSScreen* GetMenuBarScreen() { - // Documentation in NSScreen says that the first object in - // +[NSScreen screens] is the menu bar screen. - NSArray *screens = [NSScreen screens]; - if ([screens count]) - return [screens objectAtIndex:0]; - return nil; -} - -// Get the screen with the dock. -NSScreen* GetDockScreen() { - NSArray *screens = [NSScreen screens]; - NSUInteger count = [screens count]; - if (count == 0) - return NULL; - if (count == 1) - return [screens objectAtIndex:0]; - - for (NSUInteger i = 1; i < count; ++i) { - NSScreen* screen = [screens objectAtIndex:i]; - // This screen is not the menu bar screen since it's not index 0. Therefore, - // the only reason that the frame would not match the visible frame is if - // the dock is on the screen. - if (!NSEqualRects([screen frame], [screen visibleFrame])) - return screen; - } - return [screens objectAtIndex:0]; -} - -} // namespace - -@interface FullscreenWindowManager() -- (void)onScreenChanged:(NSNotification*)note; -- (void)update; -@end - -@implementation FullscreenWindowManager - -- (id)initWithWindow:(NSWindow*)window - desiredScreen:(NSScreen*)desiredScreen { - if ((self = [super init])) { - window_.reset([window retain]); - desiredScreen_.reset([desiredScreen retain]); - fullscreenMode_ = base::mac::kFullScreenModeNormal; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(onScreenChanged:) - name:NSApplicationDidChangeScreenParametersNotification - object:NSApp]; - } - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self exitFullscreenMode]; - [super dealloc]; -} - -- (void)enterFullscreenMode { - if (fullscreenActive_) - return; - fullscreenActive_ = true; - [self update]; -} - -- (void)exitFullscreenMode { - if (!fullscreenActive_) - return; - fullscreenActive_ = false; - [self update]; -} - -- (void)onScreenChanged:(NSNotification*)note { - [self update]; -} - -- (void)update { - // From OS X 10.10, NSApplicationDidChangeScreenParametersNotification is sent - // when displaying a fullscreen window, which should normally only be sent if - // the monitor resolution has changed or new display is detected. - if (![[NSScreen screens] containsObject:desiredScreen_]) - desiredScreen_.reset([[window_ screen] retain]); - - base::mac::FullScreenMode newMode; - if (!fullscreenActive_) - newMode = base::mac::kFullScreenModeNormal; - else if ([desiredScreen_ isEqual:GetMenuBarScreen()]) - newMode = base::mac::kFullScreenModeHideAll; - else if ([desiredScreen_ isEqual:GetDockScreen()]) - newMode = base::mac::kFullScreenModeHideDock; - else - newMode = base::mac::kFullScreenModeNormal; - - if (fullscreenMode_ != newMode) { - if (fullscreenMode_ != base::mac::kFullScreenModeNormal) - base::mac::ReleaseFullScreen(fullscreenMode_); - if (newMode != base::mac::kFullScreenModeNormal) - base::mac::RequestFullScreen(newMode); - fullscreenMode_ = newMode; - } - - if (fullscreenActive_) - [window_ setFrame:[desiredScreen_ frame] display:YES]; -} - -@end diff --git a/chromium/ui/base/cocoa/fullscreen_window_manager_unittest.mm b/chromium/ui/base/cocoa/fullscreen_window_manager_unittest.mm deleted file mode 100644 index 25f3dc33280..00000000000 --- a/chromium/ui/base/cocoa/fullscreen_window_manager_unittest.mm +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "ui/base/cocoa/fullscreen_window_manager.h" - -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#import "ui/base/test/cocoa_helper.h" - -typedef ui::CocoaTest FullscreenWindowManagerTest; - -TEST_F(FullscreenWindowManagerTest, EnterExit) { - base::scoped_nsobject<FullscreenWindowManager> manager( - [[FullscreenWindowManager alloc] initWithWindow:test_window() - desiredScreen:[NSScreen mainScreen]]); - - NSApplicationPresentationOptions current_options = - [NSApp presentationOptions]; - EXPECT_EQ(NSApplicationPresentationDefault, current_options); - - [manager enterFullscreenMode]; - current_options = [NSApp presentationOptions]; - EXPECT_EQ(static_cast<NSApplicationPresentationOptions>( - NSApplicationPresentationHideDock | - NSApplicationPresentationHideMenuBar), - current_options); - - [manager exitFullscreenMode]; - current_options = [NSApp presentationOptions]; - EXPECT_EQ(NSApplicationPresentationDefault, current_options); -} diff --git a/chromium/ui/base/cocoa/menu_controller.mm b/chromium/ui/base/cocoa/menu_controller.mm index 384804ab4c9..bf27efe91c6 100644 --- a/chromium/ui/base/cocoa/menu_controller.mm +++ b/chromium/ui/base/cocoa/menu_controller.mm @@ -7,7 +7,6 @@ #include "base/cancelable_callback.h" #include "base/logging.h" #include "base/mac/bind_objc_block.h" -#include "base/memory/ptr_util.h" #include "base/strings/sys_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/base/accelerators/accelerator.h" diff --git a/chromium/ui/base/cocoa/secure_password_input.h b/chromium/ui/base/cocoa/secure_password_input.h new file mode 100644 index 00000000000..ca90d3964a5 --- /dev/null +++ b/chromium/ui/base/cocoa/secure_password_input.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_BASE_COCOA_SECURE_PASSWORD_INPUT_H_ +#define UI_BASE_COCOA_SECURE_PASSWORD_INPUT_H_ + +#include "base/macros.h" +#include "ui/base/ui_base_export.h" + +namespace ui { + +// Enables the secure password input mode while in scope. +class UI_BASE_EXPORT ScopedPasswordInputEnabler { + public: + ScopedPasswordInputEnabler(); + ~ScopedPasswordInputEnabler(); + + // Returns true if the password input mode is currently enabled. Useful for + // unit tests. + static bool IsPasswordInputEnabled(); + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedPasswordInputEnabler); +}; + +} // namespace ui + +#endif // UI_BASE_COCOA_SECURE_PASSWORD_INPUT_H_ diff --git a/chromium/ui/base/cocoa/secure_password_input.mm b/chromium/ui/base/cocoa/secure_password_input.mm new file mode 100644 index 00000000000..d0345336d4c --- /dev/null +++ b/chromium/ui/base/cocoa/secure_password_input.mm @@ -0,0 +1,63 @@ +// 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 "ui/base/cocoa/secure_password_input.h" + +#import <Carbon/Carbon.h> + +#include "base/logging.h" + +namespace { + +// Used to protect from out-of-order calls to enabling/disabling functions. +int g_password_input_counter = 0; + +// SetPasswordInputEnabled() is copied from +// enableSecureTextInput() and disableSecureTextInput() functions in +// third_party/WebKit/WebCore/platform/SecureTextInput.cpp +// +// The following technote describes proper EnableSecureEventInput() usage: +// https://developer.apple.com/library/content/technotes/tn2150/_index.html +void SetPasswordInputEnabled(bool enabled) { + if (enabled) { + DCHECK(!IsSecureEventInputEnabled()); + EnableSecureEventInput(); + + CFArrayRef inputSources = TISCreateASCIICapableInputSourceList(); + TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag, + sizeof(CFArrayRef), &inputSources); + CFRelease(inputSources); + } else { + DCHECK(IsSecureEventInputEnabled()); + TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag); + + DisableSecureEventInput(); + } +} + +} // namespace + +namespace ui { + +ScopedPasswordInputEnabler::ScopedPasswordInputEnabler() { + if (!g_password_input_counter) { + SetPasswordInputEnabled(true); + } + ++g_password_input_counter; +} + +ScopedPasswordInputEnabler::~ScopedPasswordInputEnabler() { + --g_password_input_counter; + DCHECK_LE(0, g_password_input_counter); + if (!g_password_input_counter) { + SetPasswordInputEnabled(false); + } +} + +// static +bool ScopedPasswordInputEnabler::IsPasswordInputEnabled() { + return g_password_input_counter > 0; +} + +} // namespace ui diff --git a/chromium/ui/base/cocoa/text_services_context_menu.cc b/chromium/ui/base/cocoa/text_services_context_menu.cc index f7cb1dc3c24..e4de9323daf 100644 --- a/chromium/ui/base/cocoa/text_services_context_menu.cc +++ b/chromium/ui/base/cocoa/text_services_context_menu.cc @@ -93,11 +93,29 @@ void TextServicesContextMenu::AppendToContextMenu(SimpleMenuModel* model) { } void TextServicesContextMenu::AppendEditableItems(SimpleMenuModel* model) { + // MacOS provides a contextual menu to set writing direction for BiDi + // languages. This functionality is exposed as a keyboard shortcut on + // Windows and Linux. model->AddSubMenuWithStringId(IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU, IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU, &bidi_submenu_model_); } +bool TextServicesContextMenu::SupportsCommand(int command_id) const { + switch (command_id) { + case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU: + case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT: + case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR: + case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL: + case IDS_SPEECH_MAC: + case IDS_SPEECH_START_SPEAKING_MAC: + case IDS_SPEECH_STOP_SPEAKING_MAC: + return true; + } + + return false; +} + bool TextServicesContextMenu::IsCommandIdChecked(int command_id) const { switch (command_id) { case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT: @@ -116,6 +134,9 @@ bool TextServicesContextMenu::IsCommandIdChecked(int command_id) const { bool TextServicesContextMenu::IsCommandIdEnabled(int command_id) const { switch (command_id) { + case IDS_SPEECH_MAC: + case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_MENU: + return true; case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_DEFAULT: case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_LTR: case IDS_CONTENT_CONTEXT_WRITING_DIRECTION_RTL: diff --git a/chromium/ui/base/cocoa/text_services_context_menu.h b/chromium/ui/base/cocoa/text_services_context_menu.h index 2c163d7bee7..e9d48efa68c 100644 --- a/chromium/ui/base/cocoa/text_services_context_menu.h +++ b/chromium/ui/base/cocoa/text_services_context_menu.h @@ -15,8 +15,6 @@ namespace ui { // This class is used to append and handle the Speech and BiDi submenu for the // context menu. -// TODO (spqchan): Replace the Speech and BiDi logic in RenderViewContextMenu -// with TextServicesContextMenu. class UI_BASE_EXPORT TextServicesContextMenu : public SimpleMenuModel::Delegate { public: @@ -52,6 +50,9 @@ class UI_BASE_EXPORT TextServicesContextMenu // submenu is added for bidirection, which |this| serves as a delegate for. void AppendEditableItems(SimpleMenuModel* model); + // Returns true if |command_id| is handled by this class. + bool SupportsCommand(int command_id) const; + // SimpleMenuModel::Delegate: bool IsCommandIdChecked(int command_id) const override; bool IsCommandIdEnabled(int command_id) const override; diff --git a/chromium/ui/base/cursor/cursor_loader_ozone.cc b/chromium/ui/base/cursor/cursor_loader_ozone.cc index ef23518d1e4..b5770539a54 100644 --- a/chromium/ui/base/cursor/cursor_loader_ozone.cc +++ b/chromium/ui/base/cursor/cursor_loader_ozone.cc @@ -28,7 +28,7 @@ void CursorLoaderOzone::LoadImageCursor(CursorType id, GetImageCursorBitmap(resource_id, scale(), rotation(), &hotspot, &bitmap); - cursors_[id] = factory_->CreateImageCursor(bitmap, hotspot, scale()); + image_cursors_[id] = factory_->CreateImageCursor(bitmap, hotspot, scale()); } void CursorLoaderOzone::LoadAnimatedCursor(CursorType id, @@ -41,25 +41,23 @@ void CursorLoaderOzone::LoadAnimatedCursor(CursorType id, GetAnimatedCursorBitmaps( resource_id, scale(), rotation(), &hotspot, &bitmaps); - cursors_[id] = + image_cursors_[id] = factory_->CreateAnimatedCursor(bitmaps, hotspot, frame_delay_ms, scale()); } void CursorLoaderOzone::UnloadAll() { - for (ImageCursorMap::const_iterator it = cursors_.begin(); - it != cursors_.end(); ++it) { - factory_->UnrefImageCursor(it->second); - } - cursors_.clear(); + for (const auto& image_cursor : image_cursors_) + factory_->UnrefImageCursor(image_cursor.second); + image_cursors_.clear(); } void CursorLoaderOzone::SetPlatformCursor(gfx::NativeCursor* cursor) { CursorType native_type = cursor->native_type(); PlatformCursor platform; - if (cursors_.count(native_type)) { + if (image_cursors_.count(native_type)) { // An image cursor is loaded for this type. - platform = cursors_[native_type]; + platform = image_cursors_[native_type]; } else if (native_type == CursorType::kCustom) { // The platform cursor was already set via WebCursor::GetPlatformCursor. platform = cursor->platform(); diff --git a/chromium/ui/base/cursor/cursor_loader_ozone.h b/chromium/ui/base/cursor/cursor_loader_ozone.h index acb95717bd5..ea43f0b62fa 100644 --- a/chromium/ui/base/cursor/cursor_loader_ozone.h +++ b/chromium/ui/base/cursor/cursor_loader_ozone.h @@ -35,8 +35,7 @@ class UI_BASE_EXPORT CursorLoaderOzone : public CursorLoader { private: // Pointers are owned by ResourceBundle and must not be freed here. - typedef std::map<CursorType, PlatformCursor> ImageCursorMap; - ImageCursorMap cursors_; + std::map<CursorType, PlatformCursor> image_cursors_; CursorFactoryOzone* factory_ = nullptr; DISALLOW_COPY_AND_ASSIGN(CursorLoaderOzone); diff --git a/chromium/ui/base/cursor/cursor_loader_x11.h b/chromium/ui/base/cursor/cursor_loader_x11.h index e8098fbd104..9fa7beaa272 100644 --- a/chromium/ui/base/cursor/cursor_loader_x11.h +++ b/chromium/ui/base/cursor/cursor_loader_x11.h @@ -60,15 +60,12 @@ class UI_BASE_EXPORT CursorLoaderX11 : public CursorLoader { // A map to hold all image cursors. It maps the cursor ID to the X Cursor, the // display's scale factor, and the display's rotation. - typedef std::map<CursorType, std::unique_ptr<ImageCursor>> ImageCursorMap; - ImageCursorMap image_cursors_; + std::map<CursorType, std::unique_ptr<ImageCursor>> image_cursors_; // A map to hold all animated cursors. It maps the cursor ID to the pair of // the X Cursor and the corresponding XcursorImages. We need a pointer to the // images so that we can free them on destruction. - typedef std::map<CursorType, std::pair<::Cursor, XcursorImages*>> - AnimatedCursorMap; - AnimatedCursorMap animated_cursors_; + std::map<CursorType, std::pair<::Cursor, XcursorImages*>> animated_cursors_; const XScopedCursor invisible_cursor_; diff --git a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h index 7a8a2d6fe27..28de403943f 100644 --- a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h +++ b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h @@ -77,9 +77,7 @@ class UI_BASE_EXPORT BitmapCursorFactoryOzone : public CursorFactoryOzone { scoped_refptr<BitmapCursorOzone> GetDefaultCursorInternal(CursorType type); // Default cursors are cached & owned by the factory. - typedef std::map<CursorType, scoped_refptr<BitmapCursorOzone>> - DefaultCursorMap; - DefaultCursorMap default_cursors_; + std::map<CursorType, scoped_refptr<BitmapCursorOzone>> default_cursors_; DISALLOW_COPY_AND_ASSIGN(BitmapCursorFactoryOzone); }; diff --git a/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.h b/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.h index c9859f23306..b5cba38833a 100644 --- a/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.h +++ b/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.h @@ -80,8 +80,7 @@ class UI_BASE_EXPORT CursorDataFactoryOzone : public CursorFactoryOzone { scoped_refptr<CursorDataOzone> GetDefaultCursorInternal(CursorType type); // Default cursors are cached & owned by the factory. - typedef std::map<CursorType, scoped_refptr<CursorDataOzone>> DefaultCursorMap; - DefaultCursorMap default_cursors_; + std::map<CursorType, scoped_refptr<CursorDataOzone>> default_cursors_; DISALLOW_COPY_AND_ASSIGN(CursorDataFactoryOzone); }; diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.h index 6457584efd9..f8e60b31ac6 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.h +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.h @@ -29,7 +29,7 @@ class OSExchangeDataProviderAuraX11Test; // OSExchangeData::Provider implementation for aura on linux. class UI_BASE_EXPORT OSExchangeDataProviderAuraX11 : public OSExchangeData::Provider, - public ui::PlatformEventDispatcher { + public PlatformEventDispatcher { public: // |x_window| is the window the cursor is over, and |selection| is the set of // data being offered. @@ -91,7 +91,7 @@ class UI_BASE_EXPORT OSExchangeDataProviderAuraX11 gfx::ImageSkia GetDragImage() const override; gfx::Vector2d GetDragImageOffset() const override; - // ui::PlatformEventDispatcher: + // PlatformEventDispatcher: bool CanDispatchEvent(const PlatformEvent& event) override; uint32_t DispatchEvent(const PlatformEvent& event) override; diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm b/chromium/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm index 8eb42de5776..e8d108aa3f0 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm @@ -4,7 +4,6 @@ #include "ui/base/dragdrop/os_exchange_data_provider_builder_mac.h" -#include "base/memory/ptr_util.h" #include "ui/base/dragdrop/os_exchange_data_provider_mac.h" namespace ui { diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc index 2837e7984fa..9c3a9ab2cbe 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc @@ -4,7 +4,6 @@ #include "ui/base/dragdrop/os_exchange_data_provider_factory.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #if defined(USE_X11) 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 85762b290b1..2aa2920a069 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc @@ -18,7 +18,6 @@ #include "base/i18n/file_util_icu.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/pickle.h" #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_hdc.h" diff --git a/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc index 73454128eb9..62d98d1b77a 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc @@ -23,7 +23,7 @@ class OSExchangeDataTest : public PlatformTest { OSExchangeDataTest() : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI), - event_source_(ui::PlatformEventSource::CreateDefault()) {} + event_source_(PlatformEventSource::CreateDefault()) {} private: base::test::ScopedTaskEnvironment scoped_task_environment_; diff --git a/chromium/ui/base/ime/BUILD.gn b/chromium/ui/base/ime/BUILD.gn index a8a36ead5f3..9e0c6190234 100644 --- a/chromium/ui/base/ime/BUILD.gn +++ b/chromium/ui/base/ime/BUILD.gn @@ -61,8 +61,6 @@ jumbo_component("ime") { "infolist_entry.cc", "infolist_entry.h", "input_method.h", - "input_method_auralinux.cc", - "input_method_auralinux.h", "input_method_base.cc", "input_method_base.h", "input_method_chromeos.cc", @@ -77,8 +75,6 @@ jumbo_component("ime") { "input_method_minimal.cc", "input_method_minimal.h", "input_method_observer.h", - "input_method_win.cc", - "input_method_win.h", "linux/fake_input_method_context.cc", "linux/fake_input_method_context.h", "linux/fake_input_method_context_factory.cc", @@ -96,8 +92,21 @@ jumbo_component("ime") { "ui_base_ime_export.h", "win/imm32_manager.cc", "win/imm32_manager.h", + "win/on_screen_keyboard_display_manager_stub.cc", + "win/on_screen_keyboard_display_manager_stub.h", + "win/on_screen_keyboard_display_manager_tab_tip.cc", + "win/on_screen_keyboard_display_manager_tab_tip.h", + "win/osk_display_manager.cc", + "win/osk_display_manager.h", + "win/osk_display_observer.h", + "win/tsf_bridge.cc", + "win/tsf_bridge.h", + "win/tsf_event_router.cc", + "win/tsf_event_router.h", "win/tsf_input_scope.cc", "win/tsf_input_scope.h", + "win/tsf_text_store.cc", + "win/tsf_text_store.h", ] # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. @@ -112,6 +121,7 @@ jumbo_component("ime") { "//net", "//third_party/icu", "//ui/base", + "//ui/base/ime/chromeos/public/interfaces", "//ui/display", "//ui/events", "//ui/gfx", @@ -125,8 +135,8 @@ jumbo_component("ime") { "//skia", ] - if (!use_aura || is_chromeos || (!is_linux && !use_ozone)) { - sources -= [ + if (is_desktop_linux) { + sources += [ "input_method_auralinux.cc", "input_method_auralinux.h", ] @@ -179,17 +189,25 @@ jumbo_component("ime") { cflags = [ "/wd4324" ] # Structure was padded due to __declspec(align()), which is # uninteresting. + sources += [ + "input_method_win.cc", + "input_method_win.h", + "input_method_win_base.cc", + "input_method_win_base.h", + "input_method_win_tsf.cc", + "input_method_win_tsf.h", + ] libs = [ "imm32.lib" ] + + jumbo_excluded_sources = [ + # tsf_text_store.cc needs INITGUID to be defined before + # including any header to properly generate GUID objects. That + # is not guaranteed when included in a jumbo build. + "win/tsf_text_store.cc", + ] } if (is_mac) { libs = [ "AppKit.framework" ] } - - if (is_fuchsia) { - sources -= [ - "input_method_auralinux.cc", - "input_method_auralinux.h", - ] - } } diff --git a/chromium/ui/base/ime/chromeos/public/interfaces/BUILD.gn b/chromium/ui/base/ime/chromeos/public/interfaces/BUILD.gn new file mode 100644 index 00000000000..625cdd4cf3f --- /dev/null +++ b/chromium/ui/base/ime/chromeos/public/interfaces/BUILD.gn @@ -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. + +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("interfaces") { + sources = [ + "ime_keyset.mojom", + ] +} diff --git a/chromium/ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom b/chromium/ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom new file mode 100644 index 00000000000..7a9d6297a67 --- /dev/null +++ b/chromium/ui/base/ime/chromeos/public/interfaces/ime_keyset.mojom @@ -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. + +module chromeos.input_method.mojom; + +// Used by the virtual keyboard to represent different key layouts for +// different purposes. 'kNone' represents the default key layout. +// Used in UMA, so this enum should not be reordered. +enum ImeKeyset { + kNone = 0, + kEmoji = 1, + kHandwriting = 2, + kVoice = 3, +}; diff --git a/chromium/ui/base/ime/composition_text_unittest.cc b/chromium/ui/base/ime/composition_text_unittest.cc index 8431e2eb6d5..9c57c233388 100644 --- a/chromium/ui/base/ime/composition_text_unittest.cc +++ b/chromium/ui/base/ime/composition_text_unittest.cc @@ -14,14 +14,17 @@ namespace ui { TEST(CompositionTextTest, CopyTest) { const base::string16 kSampleText = base::UTF8ToUTF16("Sample Text"); const ImeTextSpan kSampleUnderline1(ImeTextSpan::Type::kComposition, 10, 20, - SK_ColorBLACK, false, + ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT); const ImeTextSpan kSampleUnderline2(ImeTextSpan::Type::kComposition, 11, 21, - SK_ColorBLACK, true, SK_ColorTRANSPARENT); + ImeTextSpan::Thickness::kThick, + SK_ColorTRANSPARENT); - const ImeTextSpan kSampleUnderline3(ImeTextSpan::Type::kComposition, 12, 22, - SK_ColorRED, false, SK_ColorTRANSPARENT); + ImeTextSpan kSampleUnderline3(ImeTextSpan::Type::kComposition, 12, 22, + ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT); + kSampleUnderline3.underline_color = SK_ColorRED; // Make CompositionText CompositionText text; @@ -43,7 +46,8 @@ TEST(CompositionTextTest, CopyTest) { text2.ime_text_spans[i].end_offset); EXPECT_EQ(text.ime_text_spans[i].underline_color, text2.ime_text_spans[i].underline_color); - EXPECT_EQ(text.ime_text_spans[i].thick, text2.ime_text_spans[i].thick); + EXPECT_EQ(text.ime_text_spans[i].thickness, + text2.ime_text_spans[i].thickness); EXPECT_EQ(text.ime_text_spans[i].background_color, text2.ime_text_spans[i].background_color); } diff --git a/chromium/ui/base/ime/composition_text_util_pango.cc b/chromium/ui/base/ime/composition_text_util_pango.cc index 9ef58037f7b..e5980f367a0 100644 --- a/chromium/ui/base/ime/composition_text_util_pango.cc +++ b/chromium/ui/base/ime/composition_text_util_pango.cc @@ -74,15 +74,16 @@ void ExtractCompositionTextFromGtkPreedit(const gchar* utf8_text, pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE); if (background_attr || underline_attr) { - // Use a black thin underline by default. + // Use a thin underline with text color by default. ImeTextSpan ime_text_span(ImeTextSpan::Type::kComposition, char16_offsets[start], char16_offsets[end], - SK_ColorBLACK, false, SK_ColorTRANSPARENT); + ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT); // Always use thick underline for a range with background color, which // is usually the selection range. if (background_attr) { - ime_text_span.thick = true; + ime_text_span.thickness = ImeTextSpan::Thickness::kThick; // If the cursor is at start or end of this underline, then we treat // it as the selection range as well, but make sure to set the cursor // position to the selection end. @@ -97,7 +98,7 @@ void ExtractCompositionTextFromGtkPreedit(const gchar* utf8_text, if (underline_attr) { int type = reinterpret_cast<PangoAttrInt*>(underline_attr)->value; if (type == PANGO_UNDERLINE_DOUBLE) - ime_text_span.thick = true; + ime_text_span.thickness = ImeTextSpan::Thickness::kThick; else if (type == PANGO_UNDERLINE_ERROR) ime_text_span.underline_color = SK_ColorRED; } @@ -107,11 +108,11 @@ void ExtractCompositionTextFromGtkPreedit(const gchar* utf8_text, pango_attr_iterator_destroy(iter); } - // Use a black thin underline by default. + // Use a thin underline with text color by default. if (composition->ime_text_spans.empty()) { composition->ime_text_spans.push_back( - ImeTextSpan(ImeTextSpan::Type::kComposition, 0, length, SK_ColorBLACK, - false, SK_ColorTRANSPARENT)); + ImeTextSpan(ImeTextSpan::Type::kComposition, 0, length, + ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT)); } } diff --git a/chromium/ui/base/ime/composition_text_util_pango_unittest.cc b/chromium/ui/base/ime/composition_text_util_pango_unittest.cc index be674433b65..26e34395208 100644 --- a/chromium/ui/base/ime/composition_text_util_pango_unittest.cc +++ b/chromium/ui/base/ime/composition_text_util_pango_unittest.cc @@ -29,7 +29,7 @@ struct ImeTextSpan { unsigned start_offset; unsigned end_offset; uint32_t underline_color; - bool thick; + ui::ImeTextSpan::Thickness thickness; uint32_t background_color; }; @@ -47,10 +47,13 @@ const TestData kTestData[] = { {PANGO_ATTR_BACKGROUND, 0, 4, 7}, {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 13}, {0, 0, 0, 0}}, - {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {4, 7, SK_ColorBLACK, true, SK_ColorTRANSPARENT}, - {8, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {0, 0, 0, false, SK_ColorTRANSPARENT}}}, + {{0, 3, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {4, 7, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThick, + SK_ColorTRANSPARENT}, + {8, 13, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {0, 0, 0, ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT}}}, // Offset overflow. {"One Two Three", @@ -58,10 +61,13 @@ const TestData kTestData[] = { {PANGO_ATTR_BACKGROUND, 0, 4, 7}, {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 20}, {0, 0, 0, 0}}, - {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {4, 7, SK_ColorBLACK, true, SK_ColorTRANSPARENT}, - {8, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {0, 0, 0, false, SK_ColorTRANSPARENT}}}, + {{0, 3, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {4, 7, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThick, + SK_ColorTRANSPARENT}, + {8, 13, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {0, 0, 0, ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT}}}, // Error underline. {"One Two Three", @@ -69,16 +75,20 @@ const TestData kTestData[] = { {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_ERROR, 4, 7}, {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 8, 13}, {0, 0, 0, 0}}, - {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {4, 7, SK_ColorRED, false, SK_ColorTRANSPARENT}, - {8, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {0, 0, 0, false, SK_ColorTRANSPARENT}}}, + {{0, 3, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {4, 7, SK_ColorRED, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {8, 13, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {0, 0, 0, ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT}}}, // Default underline. {"One Two Three", {{0, 0, 0, 0}}, - {{0, 13, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {0, 0, 0, false, SK_ColorTRANSPARENT}}}, + {{0, 13, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {0, 0, 0, ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT}}}, // Unicode, including non-BMP characters: "123你好𠀀𠀁一丁 456" {"123\xE4\xBD\xA0\xE5\xA5\xBD\xF0\xA0\x80\x80\xF0\xA0\x80\x81\xE4\xB8\x80" @@ -88,18 +98,22 @@ const TestData kTestData[] = { {PANGO_ATTR_BACKGROUND, 0, 5, 7}, {PANGO_ATTR_UNDERLINE, PANGO_UNDERLINE_SINGLE, 7, 13}, {0, 0, 0, 0}}, - {{0, 3, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {3, 5, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {5, 9, SK_ColorBLACK, true, SK_ColorTRANSPARENT}, - {9, 15, SK_ColorBLACK, false, SK_ColorTRANSPARENT}, - {0, 0, 0, false, SK_ColorTRANSPARENT}}}, + {{0, 3, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {3, 5, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {5, 9, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThick, + SK_ColorTRANSPARENT}, + {9, 15, SK_ColorTRANSPARENT, ui::ImeTextSpan::Thickness::kThin, + SK_ColorTRANSPARENT}, + {0, 0, 0, ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT}}}, }; void CompareImeTextSpan(const ImeTextSpan& a, const ui::ImeTextSpan& b) { EXPECT_EQ(a.start_offset, b.start_offset); EXPECT_EQ(a.end_offset, b.end_offset); EXPECT_EQ(a.underline_color, b.underline_color); - EXPECT_EQ(a.thick, b.thick); + EXPECT_EQ(a.thickness, b.thickness); EXPECT_EQ(a.background_color, b.background_color); } diff --git a/chromium/ui/base/ime/dummy_input_method.cc b/chromium/ui/base/ime/dummy_input_method.cc index 9b6b2cdf89b..cda3ff2221e 100644 --- a/chromium/ui/base/ime/dummy_input_method.cc +++ b/chromium/ui/base/ime/dummy_input_method.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ui/base/ime/dummy_input_method.h" +#include "build/build_config.h" #include "ui/events/event.h" namespace ui { @@ -22,10 +23,12 @@ void DummyInputMethod::OnFocus() { void DummyInputMethod::OnBlur() { } -bool DummyInputMethod::OnUntranslatedIMEMessage(const base::NativeEvent& event, +#if defined(OS_WIN) +bool DummyInputMethod::OnUntranslatedIMEMessage(const MSG event, NativeEventResult* result) { return false; } +#endif void DummyInputMethod::SetFocusedTextInputClient(TextInputClient* client) { } diff --git a/chromium/ui/base/ime/dummy_input_method.h b/chromium/ui/base/ime/dummy_input_method.h index 6a9201f484f..8b41021b831 100644 --- a/chromium/ui/base/ime/dummy_input_method.h +++ b/chromium/ui/base/ime/dummy_input_method.h @@ -6,6 +6,7 @@ #define UI_BASE_IME_DUMMY_INPUT_METHOD_H_ #include "base/macros.h" +#include "build/build_config.h" #include "ui/base/ime/input_method.h" namespace ui { @@ -21,8 +22,12 @@ class DummyInputMethod : public InputMethod { void SetDelegate(internal::InputMethodDelegate* delegate) override; void OnFocus() override; void OnBlur() override; - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, + +#if defined(OS_WIN) + bool OnUntranslatedIMEMessage(const MSG event, NativeEventResult* result) override; +#endif + void SetFocusedTextInputClient(TextInputClient* client) override; void DetachTextInputClient(TextInputClient* client) override; TextInputClient* GetTextInputClient() const override; diff --git a/chromium/ui/base/ime/ime_engine_handler_interface.h b/chromium/ui/base/ime/ime_engine_handler_interface.h index 3987085386a..a226fa38b6e 100644 --- a/chromium/ui/base/ime/ime_engine_handler_interface.h +++ b/chromium/ui/base/ime/ime_engine_handler_interface.h @@ -28,7 +28,7 @@ class KeyEvent; // A interface to handle the engine handler method call. class UI_BASE_IME_EXPORT IMEEngineHandlerInterface { public: - typedef base::Callback<void(bool consumed)> KeyEventDoneCallback; + typedef base::OnceCallback<void(bool consumed)> KeyEventDoneCallback; // A information about a focused text input field. // A type of each member is based on the html spec, but InputContext can be @@ -74,7 +74,7 @@ class UI_BASE_IME_EXPORT IMEEngineHandlerInterface { // Called when the key event is received. // Actual implementation must call |callback| after key event handling. virtual void ProcessKeyEvent(const KeyEvent& key_event, - KeyEventDoneCallback& callback) = 0; + KeyEventDoneCallback callback) = 0; // Called when a new surrounding text is set. The |text| is surrounding text // and |cursor_pos| is 0 based index of cursor position in |text|. If there is diff --git a/chromium/ui/base/ime/ime_text_span.cc b/chromium/ui/base/ime/ime_text_span.cc index 1ee2876be35..14b482ed591 100644 --- a/chromium/ui/base/ime/ime_text_span.cc +++ b/chromium/ui/base/ime/ime_text_span.cc @@ -9,32 +9,28 @@ namespace ui { -ImeTextSpan::ImeTextSpan() : ImeTextSpan(0, 0, SK_ColorTRANSPARENT, false) {} +ImeTextSpan::ImeTextSpan() : ImeTextSpan(0, 0, Thickness::kThin) {} ImeTextSpan::ImeTextSpan(uint32_t start_offset, uint32_t end_offset, - SkColor underline_color, - bool thick) + Thickness thickness) : ImeTextSpan(Type::kComposition, start_offset, end_offset, - underline_color, - thick, + thickness, SK_ColorTRANSPARENT) {} ImeTextSpan::ImeTextSpan(Type type, uint32_t start_offset, uint32_t end_offset, - SkColor underline_color, - bool thick, + Thickness thickness, SkColor background_color, SkColor suggestion_highlight_color, const std::vector<std::string>& suggestions) : type(type), start_offset(start_offset), end_offset(end_offset), - underline_color(underline_color), - thick(thick), + thickness(thickness), background_color(background_color), suggestion_highlight_color(suggestion_highlight_color), suggestions(suggestions) {} diff --git a/chromium/ui/base/ime/ime_text_span.h b/chromium/ui/base/ime/ime_text_span.h index eee554f4fe2..e47064a1a63 100644 --- a/chromium/ui/base/ime/ime_text_span.h +++ b/chromium/ui/base/ime/ime_text_span.h @@ -31,19 +31,23 @@ struct UI_BASE_IME_EXPORT ImeTextSpan { kMisspellingSuggestion, }; + enum class Thickness { + kNone, + kThin, + kThick, + }; + // The default constructor is used by generated Mojo code. ImeTextSpan(); // TODO(huangs): remove this constructor. ImeTextSpan(uint32_t start_offset, uint32_t end_offset, - SkColor underline_color, - bool thick); + Thickness thickness); ImeTextSpan( Type type, uint32_t start_offset, uint32_t end_offset, - SkColor underline_color, - bool thick, + Thickness thickness, SkColor background_color, SkColor suggestion_highlight_color = SK_ColorTRANSPARENT, const std::vector<std::string>& suggestions = std::vector<std::string>()); @@ -57,7 +61,7 @@ struct UI_BASE_IME_EXPORT ImeTextSpan { (this->start_offset == rhs.start_offset) && (this->end_offset == rhs.end_offset) && (this->underline_color == rhs.underline_color) && - (this->thick == rhs.thick) && + (this->thickness == rhs.thickness) && (this->background_color == rhs.background_color) && (this->suggestion_highlight_color == rhs.suggestion_highlight_color) && @@ -69,8 +73,8 @@ struct UI_BASE_IME_EXPORT ImeTextSpan { Type type; uint32_t start_offset; uint32_t end_offset; - SkColor underline_color; - bool thick; + SkColor underline_color = SK_ColorTRANSPARENT; + Thickness thickness; SkColor background_color; SkColor suggestion_highlight_color; std::vector<std::string> suggestions; diff --git a/chromium/ui/base/ime/input_method.h b/chromium/ui/base/ime/input_method.h index fad2ecca3bb..1a5fb7adbd7 100644 --- a/chromium/ui/base/ime/input_method.h +++ b/chromium/ui/base/ime/input_method.h @@ -11,11 +11,11 @@ #include <string> #include <vector> -#include "base/event_types.h" #include "build/build_config.h" #include "ui/base/ime/text_input_mode.h" #include "ui/base/ime/text_input_type.h" #include "ui/events/event_dispatcher.h" +#include "ui/events/platform_event.h" #include "ui/gfx/geometry/rect.h" namespace extensions { @@ -76,12 +76,13 @@ class InputMethod { // Called when the top-level system window loses keyboard focus. virtual void OnBlur() = 0; +#if defined(OS_WIN) // Called when the focused window receives native IME messages that are not // translated into other predefined event callbacks. Currently this method is // used only for IME functionalities specific to Windows. - // TODO(ime): Break down these messages into platform-neutral methods. - virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event, + virtual bool OnUntranslatedIMEMessage(const MSG event, NativeEventResult* result) = 0; +#endif // Sets the text input client which receives text input events such as // SetCompositionText(). |client| can be NULL. A gfx::NativeWindow which diff --git a/chromium/ui/base/ime/input_method_auralinux.cc b/chromium/ui/base/ime/input_method_auralinux.cc index 9a6c7bee4fc..c02def8d3f7 100644 --- a/chromium/ui/base/ime/input_method_auralinux.cc +++ b/chromium/ui/base/ime/input_method_auralinux.cc @@ -46,12 +46,6 @@ LinuxInputMethodContext* InputMethodAuraLinux::GetContextForTesting( // Overriden from InputMethod. -bool InputMethodAuraLinux::OnUntranslatedIMEMessage( - const base::NativeEvent& event, - NativeEventResult* result) { - return false; -} - ui::EventDispatchDetails InputMethodAuraLinux::DispatchKeyEvent( ui::KeyEvent* event) { DCHECK(event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED); @@ -99,13 +93,14 @@ ui::EventDispatchDetails InputMethodAuraLinux::DispatchKeyEvent( if (text_input_type_ != TEXT_INPUT_TYPE_PASSWORD && GetEngine() && GetEngine()->IsInterestedInKeyEvent() && (!filtered || NeedInsertChar())) { - ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::Bind( - &InputMethodAuraLinux::ProcessKeyEventByEngineDone, - weak_ptr_factory_.GetWeakPtr(), base::Owned(new ui::KeyEvent(*event)), - filtered, composition_changed_, - base::Owned(new ui::CompositionText(composition_)), - base::Owned(new base::string16(result_text_))); - GetEngine()->ProcessKeyEvent(*event, callback); + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = + base::BindOnce(&InputMethodAuraLinux::ProcessKeyEventByEngineDone, + weak_ptr_factory_.GetWeakPtr(), + base::Owned(new ui::KeyEvent(*event)), filtered, + composition_changed_, + base::Owned(new ui::CompositionText(composition_)), + base::Owned(new base::string16(result_text_))); + GetEngine()->ProcessKeyEvent(*event, std::move(callback)); return ui::EventDispatchDetails(); } diff --git a/chromium/ui/base/ime/input_method_auralinux.h b/chromium/ui/base/ime/input_method_auralinux.h index 7a2adae8cf4..ef4985b95c6 100644 --- a/chromium/ui/base/ime/input_method_auralinux.h +++ b/chromium/ui/base/ime/input_method_auralinux.h @@ -27,8 +27,6 @@ class UI_BASE_IME_EXPORT InputMethodAuraLinux LinuxInputMethodContext* GetContextForTesting(bool is_simple); // Overriden from InputMethod. - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) override; ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; void OnTextInputTypeChanged(const TextInputClient* client) override; void OnCaretBoundsChanged(const TextInputClient* client) override; diff --git a/chromium/ui/base/ime/input_method_base.cc b/chromium/ui/base/ime/input_method_base.cc index 7d4747b5c05..ea0e64490e5 100644 --- a/chromium/ui/base/ime/input_method_base.cc +++ b/chromium/ui/base/ime/input_method_base.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "ui/base/ime/ime_bridge.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/input_method_observer.h" @@ -22,9 +23,9 @@ ui::IMEEngineHandlerInterface* InputMethodBase::GetEngine() { return nullptr; } -InputMethodBase::InputMethodBase() +InputMethodBase::InputMethodBase(internal::InputMethodDelegate* delegate) : sending_key_event_(false), - delegate_(nullptr), + delegate_(delegate), text_input_client_(nullptr) {} InputMethodBase::~InputMethodBase() { @@ -53,6 +54,14 @@ void InputMethodBase::OnBlur() { ui::IMEBridge::Get()->SetInputContextHandler(nullptr); } +#if defined(OS_WIN) +bool InputMethodBase::OnUntranslatedIMEMessage( + const MSG event, + InputMethod::NativeEventResult* result) { + return false; +} +#endif + void InputMethodBase::SetFocusedTextInputClient(TextInputClient* client) { SetFocusedTextInputClientInternal(client); } @@ -143,17 +152,17 @@ ui::EventDispatchDetails InputMethodBase::DispatchKeyEventPostIME( ui::EventDispatchDetails InputMethodBase::DispatchKeyEventPostIME( ui::KeyEvent* event, - std::unique_ptr<base::OnceCallback<void(bool)>> ack_callback) const { + base::OnceCallback<void(bool)> ack_callback) const { if (delegate_) { ui::EventDispatchDetails details = delegate_->DispatchKeyEventPostIME(event); - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(event->stopped_propagation()); + if (ack_callback) + std::move(ack_callback).Run(event->stopped_propagation()); return details; } - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(false); + if (ack_callback) + std::move(ack_callback).Run(false); return EventDispatchDetails(); } diff --git a/chromium/ui/base/ime/input_method_base.h b/chromium/ui/base/ime/input_method_base.h index 6a26900d342..f97938b4213 100644 --- a/chromium/ui/base/ime/input_method_base.h +++ b/chromium/ui/base/ime/input_method_base.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "build/build_config.h" #include "ui/base/ime/ime_input_context_handler_interface.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/ui_base_ime_export.h" @@ -34,13 +35,19 @@ class UI_BASE_IME_EXPORT InputMethodBase public base::SupportsWeakPtr<InputMethodBase>, public IMEInputContextHandlerInterface { public: - InputMethodBase(); + explicit InputMethodBase(internal::InputMethodDelegate* delegate = nullptr); ~InputMethodBase() override; // Overriden from InputMethod. void SetDelegate(internal::InputMethodDelegate* delegate) override; void OnFocus() override; void OnBlur() override; + +#if defined(OS_WIN) + bool OnUntranslatedIMEMessage(const MSG event, + NativeEventResult* result) override; +#endif + void SetFocusedTextInputClient(TextInputClient* client) override; void DetachTextInputClient(TextInputClient* client) override; TextInputClient* GetTextInputClient() const override; @@ -100,8 +107,7 @@ class UI_BASE_IME_EXPORT InputMethodBase virtual ui::EventDispatchDetails DispatchKeyEventPostIME( ui::KeyEvent* event, - std::unique_ptr<base::OnceCallback<void(bool)>> ack_callback) const - WARN_UNUSED_RESULT; + base::OnceCallback<void(bool)> ack_callback) const WARN_UNUSED_RESULT; // Convenience method to notify all observers of TextInputClient changes. void NotifyTextInputStateChanged(const TextInputClient* client); diff --git a/chromium/ui/base/ime/input_method_base_unittest.cc b/chromium/ui/base/ime/input_method_base_unittest.cc index cccce2d70f5..d5f7dd8936a 100644 --- a/chromium/ui/base/ime/input_method_base_unittest.cc +++ b/chromium/ui/base/ime/input_method_base_unittest.cc @@ -140,11 +140,6 @@ class MockInputMethodBase : public InputMethodBase { private: // Overriden from InputMethod. - bool OnUntranslatedIMEMessage( - const base::NativeEvent& event, - InputMethod::NativeEventResult* result) override { - return false; - } ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent*) override { return ui::EventDispatchDetails(); } diff --git a/chromium/ui/base/ime/input_method_chromeos.cc b/chromium/ui/base/ime/input_method_chromeos.cc index fba861cd4a9..b11f08eaad0 100644 --- a/chromium/ui/base/ime/input_method_chromeos.cc +++ b/chromium/ui/base/ime/input_method_chromeos.cc @@ -15,7 +15,6 @@ #include "base/bind.h" #include "base/i18n/char_iterator.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/third_party/icu/icu_utf.h" @@ -105,8 +104,7 @@ ui::EventDispatchDetails InputMethodChromeOS::DispatchKeyEvent( // TODO(shuchen): Eventually, the language input keys should be handed // over to the IME extension to process. And IMF can handle if the IME // extension didn't handle. - return DispatchKeyEventPostIME( - event, std::make_unique<AckCallback>(std::move(ack_callback))); + return DispatchKeyEventPostIME(event, std::move(ack_callback)); } } } @@ -121,36 +119,28 @@ ui::EventDispatchDetails InputMethodChromeOS::DispatchKeyEvent( if (ExecuteCharacterComposer(*event)) { // Treating as PostIME event if character composer handles key event and // generates some IME event, - return ProcessKeyEventPostIME( - event, std::make_unique<AckCallback>(std::move(ack_callback)), - false, true); + return ProcessKeyEventPostIME(event, std::move(ack_callback), false, + true); } - return ProcessUnfilteredKeyPressEvent( - event, std::make_unique<AckCallback>(std::move(ack_callback))); + return ProcessUnfilteredKeyPressEvent(event, std::move(ack_callback)); } - return DispatchKeyEventPostIME( - event, std::make_unique<AckCallback>(std::move(ack_callback))); + return DispatchKeyEventPostIME(event, std::move(ack_callback)); } handling_key_event_ = true; if (GetEngine()->IsInterestedInKeyEvent()) { - ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::Bind( - &InputMethodChromeOS::KeyEventDoneCallback, - weak_ptr_factory_.GetWeakPtr(), - // Pass the ownership of the new copied event. - base::Owned(new ui::KeyEvent(*event)), Passed(&ack_callback)); - GetEngine()->ProcessKeyEvent(*event, callback); + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = + base::BindOnce(&InputMethodChromeOS::KeyEventDoneCallback, + weak_ptr_factory_.GetWeakPtr(), + // Pass the ownership of the new copied event. + base::Owned(new ui::KeyEvent(*event)), + std::move(ack_callback)); + GetEngine()->ProcessKeyEvent(*event, std::move(callback)); return ui::EventDispatchDetails(); } return ProcessKeyEventDone(event, std::move(ack_callback), false); } -bool InputMethodChromeOS::OnUntranslatedIMEMessage( - const base::NativeEvent& event, - NativeEventResult* result) { - return false; -} - void InputMethodChromeOS::KeyEventDoneCallback(ui::KeyEvent* event, AckCallback ack_callback, bool is_handled) { @@ -176,9 +166,8 @@ ui::EventDispatchDetails InputMethodChromeOS::ProcessKeyEventDone( } ui::EventDispatchDetails details; if (event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED) { - details = ProcessKeyEventPostIME( - event, std::make_unique<AckCallback>(std::move(ack_callback)), false, - is_handled); + details = ProcessKeyEventPostIME(event, std::move(ack_callback), false, + is_handled); } handling_key_event_ = false; return details; @@ -361,7 +350,7 @@ void InputMethodChromeOS::UpdateContextFocusState() { ui::EventDispatchDetails InputMethodChromeOS::ProcessKeyEventPostIME( ui::KeyEvent* event, - std::unique_ptr<AckCallback> ack_callback, + AckCallback ack_callback, bool skip_process_filtered, bool handled) { TextInputClient* client = GetTextInputClient(); @@ -378,8 +367,8 @@ ui::EventDispatchDetails InputMethodChromeOS::ProcessKeyEventPostIME( // In case the focus was changed by the key event. The |context_| should have // been reset when the focused window changed. if (client != GetTextInputClient()) { - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(false); + if (ack_callback) + std::move(ack_callback).Run(false); return dispatch_details; } if (HasInputMethodResult()) @@ -388,14 +377,14 @@ ui::EventDispatchDetails InputMethodChromeOS::ProcessKeyEventPostIME( // In case the focus was changed when sending input method results to the // focused window. if (client != GetTextInputClient()) { - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(false); + if (ack_callback) + std::move(ack_callback).Run(false); return dispatch_details; } if (handled) { - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(true); + if (ack_callback) + std::move(ack_callback).Run(true); return dispatch_details; // IME handled the key event. do not forward. } @@ -409,11 +398,11 @@ ui::EventDispatchDetails InputMethodChromeOS::ProcessKeyEventPostIME( ui::EventDispatchDetails InputMethodChromeOS::ProcessFilteredKeyPressEvent( ui::KeyEvent* event, - std::unique_ptr<AckCallback> ack_callback) { - auto callback = std::make_unique<AckCallback>(base::Bind( + AckCallback ack_callback) { + auto callback = base::Bind( &InputMethodChromeOS::PostProcessFilteredKeyPressEvent, weak_ptr_factory_.GetWeakPtr(), base::Owned(new ui::KeyEvent(*event)), - GetTextInputClient(), Passed(&ack_callback))); + GetTextInputClient(), Passed(&ack_callback)); if (NeedInsertChar()) return DispatchKeyEventPostIME(event, std::move(callback)); @@ -434,7 +423,7 @@ ui::EventDispatchDetails InputMethodChromeOS::ProcessFilteredKeyPressEvent( void InputMethodChromeOS::PostProcessFilteredKeyPressEvent( ui::KeyEvent* event, TextInputClient* prev_client, - std::unique_ptr<AckCallback> ack_callback, + AckCallback ack_callback, bool stopped_propagation) { // In case the focus was changed by the key event. if (GetTextInputClient() != prev_client) @@ -442,8 +431,8 @@ void InputMethodChromeOS::PostProcessFilteredKeyPressEvent( if (stopped_propagation) { ResetContext(); - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(true); + if (ack_callback) + std::move(ack_callback).Run(true); return; } ignore_result( @@ -452,24 +441,24 @@ void InputMethodChromeOS::PostProcessFilteredKeyPressEvent( ui::EventDispatchDetails InputMethodChromeOS::ProcessUnfilteredKeyPressEvent( ui::KeyEvent* event, - std::unique_ptr<AckCallback> ack_callback) { + AckCallback ack_callback) { return DispatchKeyEventPostIME( event, - std::make_unique<AckCallback>(base::Bind( - &InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent, - weak_ptr_factory_.GetWeakPtr(), base::Owned(new ui::KeyEvent(*event)), - GetTextInputClient(), Passed(&ack_callback)))); + base::BindOnce(&InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent, + weak_ptr_factory_.GetWeakPtr(), + base::Owned(new ui::KeyEvent(*event)), + GetTextInputClient(), std::move(ack_callback))); } void InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent( ui::KeyEvent* event, TextInputClient* prev_client, - std::unique_ptr<AckCallback> ack_callback, + AckCallback ack_callback, bool stopped_propagation) { if (stopped_propagation) { ResetContext(); - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(false); + if (ack_callback) + std::move(ack_callback).Run(false); return; } @@ -482,8 +471,8 @@ void InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent( // We should return here not to send the Tab key event to RWHV. TextInputClient* client = GetTextInputClient(); if (!client || client != prev_client) { - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(false); + if (ack_callback) + std::move(ack_callback).Run(false); return; } @@ -494,8 +483,8 @@ void InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent( if (ch) client->InsertChar(*event); - if (ack_callback && !ack_callback->is_null()) - std::move(*ack_callback).Run(false); + if (ack_callback) + std::move(ack_callback).Run(false); } void InputMethodChromeOS::ProcessInputMethodResult(ui::KeyEvent* event, @@ -703,9 +692,9 @@ void InputMethodChromeOS::ExtractCompositionText( continue; ImeTextSpan ime_text_span(ui::ImeTextSpan::Type::kComposition, char16_offsets[start], char16_offsets[end], - text_ime_text_spans[i].underline_color, - text_ime_text_spans[i].thick, + text_ime_text_spans[i].thickness, text_ime_text_spans[i].background_color); + ime_text_span.underline_color = text_ime_text_spans[i].underline_color; out_composition->ime_text_spans.push_back(ime_text_span); } } @@ -716,7 +705,7 @@ void InputMethodChromeOS::ExtractCompositionText( const uint32_t end = text.selection.end(); ImeTextSpan ime_text_span(ui::ImeTextSpan::Type::kComposition, char16_offsets[start], char16_offsets[end], - SK_ColorBLACK, true /* thick */, + ui::ImeTextSpan::Thickness::kThick, SK_ColorTRANSPARENT); out_composition->ime_text_spans.push_back(ime_text_span); @@ -732,11 +721,11 @@ void InputMethodChromeOS::ExtractCompositionText( } } - // Use a black thin underline by default. + // Use a thin underline with text color by default. if (out_composition->ime_text_spans.empty()) { out_composition->ime_text_spans.push_back( ImeTextSpan(ui::ImeTextSpan::Type::kComposition, 0, length, - SK_ColorBLACK, false /* thick */, SK_ColorTRANSPARENT)); + ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT)); } } diff --git a/chromium/ui/base/ime/input_method_chromeos.h b/chromium/ui/base/ime/input_method_chromeos.h index 24859210c03..c86e65c66a4 100644 --- a/chromium/ui/base/ime/input_method_chromeos.h +++ b/chromium/ui/base/ime/input_method_chromeos.h @@ -33,8 +33,6 @@ class UI_BASE_IME_EXPORT InputMethodChromeOS : public InputMethodBase { AckCallback ack_callback); // Overridden from InputMethod: - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) override; ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; void OnTextInputTypeChanged(const TextInputClient* client) override; void OnCaretBoundsChanged(const TextInputClient* client) override; @@ -50,7 +48,7 @@ class UI_BASE_IME_EXPORT InputMethodChromeOS : public InputMethodBase { // Process a key returned from the input method. virtual ui::EventDispatchDetails ProcessKeyEventPostIME( ui::KeyEvent* event, - std::unique_ptr<AckCallback> ack_callback, + AckCallback ack_callback, bool skip_process_filtered, bool handled) WARN_UNUSED_RESULT; @@ -79,26 +77,24 @@ class UI_BASE_IME_EXPORT InputMethodChromeOS : public InputMethodBase { // when dispatching post IME. ui::EventDispatchDetails ProcessFilteredKeyPressEvent( ui::KeyEvent* event, - std::unique_ptr<AckCallback> ack_callback) WARN_UNUSED_RESULT; + AckCallback ack_callback) WARN_UNUSED_RESULT; // Post processes a key event that was already filtered by the input method. - void PostProcessFilteredKeyPressEvent( - ui::KeyEvent* event, - TextInputClient* prev_client, - std::unique_ptr<AckCallback> ack_callback, - bool stopped_propagation); + void PostProcessFilteredKeyPressEvent(ui::KeyEvent* event, + TextInputClient* prev_client, + AckCallback ack_callback, + bool stopped_propagation); // Processes a key event that was not filtered by the input method. ui::EventDispatchDetails ProcessUnfilteredKeyPressEvent( ui::KeyEvent* event, - std::unique_ptr<AckCallback> ack_callback) WARN_UNUSED_RESULT; + AckCallback ack_callback) WARN_UNUSED_RESULT; // Post processes a key event that was unfiltered by the input method. - void PostProcessUnfilteredKeyPressEvent( - ui::KeyEvent* event, - TextInputClient* prev_client, - std::unique_ptr<AckCallback> ack_callback, - bool stopped_propagation); + void PostProcessUnfilteredKeyPressEvent(ui::KeyEvent* event, + TextInputClient* prev_client, + AckCallback ack_callback, + bool stopped_propagation); // Sends input method result caused by the given key event to the focused text // input client. diff --git a/chromium/ui/base/ime/input_method_chromeos_unittest.cc b/chromium/ui/base/ime/input_method_chromeos_unittest.cc index fc127d28125..c826c1f150e 100644 --- a/chromium/ui/base/ime/input_method_chromeos_unittest.cc +++ b/chromium/ui/base/ime/input_method_chromeos_unittest.cc @@ -51,11 +51,6 @@ uint32_t GetOffsetInUTF16(const base::string16& utf16_string, return char_iterator.array_pos(); } -enum KeyEventHandlerBehavior { - KEYEVENT_CONSUME, - KEYEVENT_NOT_CONSUME, -}; - } // namespace @@ -67,22 +62,23 @@ class TestableInputMethodChromeOS : public InputMethodChromeOS { } struct ProcessKeyEventPostIMEArgs { - ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {} - const ui::KeyEvent* event; + ProcessKeyEventPostIMEArgs() + : event(ET_UNKNOWN, VKEY_UNKNOWN, DomCode::NONE, EF_NONE), + handled(false) {} + ui::KeyEvent event; bool handled; }; // Overridden from InputMethodChromeOS: - ui::EventDispatchDetails ProcessKeyEventPostIME( - ui::KeyEvent* key_event, - std::unique_ptr<AckCallback> ack_callback, - bool skip_process_filtered, - bool handled) override { + ui::EventDispatchDetails ProcessKeyEventPostIME(ui::KeyEvent* key_event, + AckCallback ack_callback, + bool skip_process_filtered, + bool handled) override { ui::EventDispatchDetails details = InputMethodChromeOS::ProcessKeyEventPostIME( key_event, std::move(ack_callback), skip_process_filtered, handled); if (!skip_process_filtered) { - process_key_event_post_ime_args_.event = key_event; + process_key_event_post_ime_args_.event = *key_event; process_key_event_post_ime_args_.handled = handled; ++process_key_event_post_ime_call_count_; } @@ -110,72 +106,6 @@ class TestableInputMethodChromeOS : public InputMethodChromeOS { int process_key_event_post_ime_call_count_; }; -class SynchronousKeyEventHandler { - public: - SynchronousKeyEventHandler(uint32_t expected_keyval, - uint32_t expected_keycode, - uint32_t expected_state, - KeyEventHandlerBehavior behavior) - : expected_keyval_(expected_keyval), - expected_keycode_(expected_keycode), - expected_state_(expected_state), - behavior_(behavior) {} - - virtual ~SynchronousKeyEventHandler() {} - - void Run(uint32_t keyval, - uint32_t keycode, - uint32_t state, - const KeyEventCallback& callback) { - EXPECT_EQ(expected_keyval_, keyval); - EXPECT_EQ(expected_keycode_, keycode); - EXPECT_EQ(expected_state_, state); - callback.Run(behavior_ == KEYEVENT_CONSUME); - } - - private: - const uint32_t expected_keyval_; - const uint32_t expected_keycode_; - const uint32_t expected_state_; - const KeyEventHandlerBehavior behavior_; - - DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler); -}; - -class AsynchronousKeyEventHandler { - public: - AsynchronousKeyEventHandler(uint32_t expected_keyval, - uint32_t expected_keycode, - uint32_t expected_state) - : expected_keyval_(expected_keyval), - expected_keycode_(expected_keycode), - expected_state_(expected_state) {} - - virtual ~AsynchronousKeyEventHandler() {} - - void Run(uint32_t keyval, - uint32_t keycode, - uint32_t state, - const KeyEventCallback& callback) { - EXPECT_EQ(expected_keyval_, keyval); - EXPECT_EQ(expected_keycode_, keycode); - EXPECT_EQ(expected_state_, state); - callback_ = callback; - } - - void RunCallback(KeyEventHandlerBehavior behavior) { - callback_.Run(behavior == KEYEVENT_CONSUME); - } - - private: - const uint32_t expected_keyval_; - const uint32_t expected_keycode_; - const uint32_t expected_state_; - KeyEventCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler); -}; - class SetSurroundingTextVerifier { public: SetSurroundingTextVerifier(const std::string& expected_surrounding_text, @@ -605,7 +535,8 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) { EXPECT_EQ(0UL, composition_text.ime_text_spans[0].start_offset); EXPECT_EQ(kSampleAsciiText.size(), composition_text.ime_text_spans[0].end_offset); - EXPECT_FALSE(composition_text.ime_text_spans[0].thick); + EXPECT_EQ(ui::ImeTextSpan::Thickness::kThin, + composition_text.ime_text_spans[0].thickness); } TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) { @@ -615,7 +546,7 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) { CompositionText composition_text; composition_text.text = kSampleText; ImeTextSpan underline(ImeTextSpan::Type::kComposition, 1UL, 4UL, - SK_ColorBLACK, false, SK_ColorTRANSPARENT); + ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT); composition_text.ime_text_spans.push_back(underline); CompositionText composition_text2; @@ -630,9 +561,11 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) { composition_text2.ime_text_spans[0].start_offset); EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_offset), composition_text2.ime_text_spans[0].end_offset); - // Single underline represents as black thin line. - EXPECT_EQ(SK_ColorBLACK, composition_text2.ime_text_spans[0].underline_color); - EXPECT_FALSE(composition_text2.ime_text_spans[0].thick); + // Single underline represents as thin line with text color. + EXPECT_EQ(SK_ColorTRANSPARENT, + composition_text2.ime_text_spans[0].underline_color); + EXPECT_EQ(ui::ImeTextSpan::Thickness::kThin, + composition_text2.ime_text_spans[0].thickness); EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), composition_text2.ime_text_spans[0].background_color); } @@ -644,7 +577,8 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) { CompositionText composition_text; composition_text.text = kSampleText; ImeTextSpan underline(ImeTextSpan::Type::kComposition, 1UL, 4UL, - SK_ColorBLACK, true, SK_ColorTRANSPARENT); + ui::ImeTextSpan::Thickness::kThick, + SK_ColorTRANSPARENT); composition_text.ime_text_spans.push_back(underline); CompositionText composition_text2; @@ -659,9 +593,11 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) { composition_text2.ime_text_spans[0].start_offset); EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_offset), composition_text2.ime_text_spans[0].end_offset); - // Double underline represents as black thick line. - EXPECT_EQ(SK_ColorBLACK, composition_text2.ime_text_spans[0].underline_color); - EXPECT_TRUE(composition_text2.ime_text_spans[0].thick); + // Double underline represents as thick line with text color. + EXPECT_EQ(SK_ColorTRANSPARENT, + composition_text2.ime_text_spans[0].underline_color); + EXPECT_EQ(ui::ImeTextSpan::Thickness::kThick, + composition_text2.ime_text_spans[0].thickness); EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), composition_text2.ime_text_spans[0].background_color); } @@ -672,8 +608,9 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) { // Set up chromeos composition text with one underline attribute. CompositionText composition_text; composition_text.text = kSampleText; - ImeTextSpan underline(ImeTextSpan::Type::kComposition, 1UL, 4UL, SK_ColorRED, - false, SK_ColorTRANSPARENT); + ImeTextSpan underline(ImeTextSpan::Type::kComposition, 1UL, 4UL, + ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT); + underline.underline_color = SK_ColorRED; composition_text.ime_text_spans.push_back(underline); CompositionText composition_text2; @@ -689,7 +626,8 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) { composition_text2.ime_text_spans[0].end_offset); // Error underline represents as red thin line. EXPECT_EQ(SK_ColorRED, composition_text2.ime_text_spans[0].underline_color); - EXPECT_FALSE(composition_text2.ime_text_spans[0].thick); + EXPECT_EQ(ui::ImeTextSpan::Thickness::kThin, + composition_text2.ime_text_spans[0].thickness); } TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) { @@ -712,8 +650,10 @@ TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) { composition_text2.ime_text_spans[0].start_offset); EXPECT_EQ(GetOffsetInUTF16(kSampleText, composition_text.selection.end()), composition_text2.ime_text_spans[0].end_offset); - EXPECT_EQ(SK_ColorBLACK, composition_text2.ime_text_spans[0].underline_color); - EXPECT_TRUE(composition_text2.ime_text_spans[0].thick); + EXPECT_EQ(SK_ColorTRANSPARENT, + composition_text2.ime_text_spans[0].underline_color); + EXPECT_EQ(ui::ImeTextSpan::Thickness::kThick, + composition_text2.ime_text_spans[0].thickness); EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), composition_text2.ime_text_spans[0].background_color); } @@ -743,8 +683,10 @@ TEST_F(InputMethodChromeOSTest, composition_text2.ime_text_spans[0].start_offset); EXPECT_EQ(GetOffsetInUTF16(kSampleText, composition_text.selection.end()), composition_text2.ime_text_spans[0].end_offset); - EXPECT_EQ(SK_ColorBLACK, composition_text2.ime_text_spans[0].underline_color); - EXPECT_TRUE(composition_text2.ime_text_spans[0].thick); + EXPECT_EQ(SK_ColorTRANSPARENT, + composition_text2.ime_text_spans[0].underline_color); + EXPECT_EQ(ui::ImeTextSpan::Thickness::kThick, + composition_text2.ime_text_spans[0].thickness); EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), composition_text2.ime_text_spans[0].background_color); } @@ -774,8 +716,10 @@ TEST_F(InputMethodChromeOSTest, composition_text2.ime_text_spans[0].start_offset); EXPECT_EQ(GetOffsetInUTF16(kSampleText, composition_text.selection.end()), composition_text2.ime_text_spans[0].end_offset); - EXPECT_EQ(SK_ColorBLACK, composition_text2.ime_text_spans[0].underline_color); - EXPECT_TRUE(composition_text2.ime_text_spans[0].thick); + EXPECT_EQ(SK_ColorTRANSPARENT, + composition_text2.ime_text_spans[0].underline_color); + EXPECT_EQ(ui::ImeTextSpan::Thickness::kThick, + composition_text2.ime_text_spans[0].thickness); EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), composition_text2.ime_text_spans[0].background_color); } @@ -913,14 +857,14 @@ TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) { EXPECT_EQ(0, inserted_char_); // Do callback. - mock_ime_engine_handler_->last_passed_callback().Run(true); + std::move(mock_ime_engine_handler_->last_passed_callback()).Run(true); // Check the results EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count()); - const ui::KeyEvent* stored_event = + const ui::KeyEvent stored_event = ime_->process_key_event_post_ime_args().event; - EXPECT_EQ(ui::VKEY_A, stored_event->key_code()); - EXPECT_EQ(kFlags, stored_event->flags()); + EXPECT_EQ(ui::VKEY_A, stored_event.key_code()); + EXPECT_EQ(kFlags, stored_event.flags()); EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled); EXPECT_EQ(L'A', inserted_char_); @@ -966,16 +910,15 @@ TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) { EXPECT_EQ(0, composition_text_.text[0]); // Do callback for first key event. - first_callback.Run(true); + std::move(first_callback).Run(true); EXPECT_EQ(comp.text, composition_text_.text); // Check the results for first key event. EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count()); - const ui::KeyEvent* stored_event = - ime_->process_key_event_post_ime_args().event; - EXPECT_EQ(ui::VKEY_B, stored_event->key_code()); - EXPECT_EQ(kFlags, stored_event->flags()); + ui::KeyEvent stored_event = ime_->process_key_event_post_ime_args().event; + EXPECT_EQ(ui::VKEY_B, stored_event.key_code()); + EXPECT_EQ(kFlags, stored_event.flags()); EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled); EXPECT_EQ(0, inserted_char_); @@ -985,8 +928,8 @@ TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) { // Check the results for second key event. EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count()); stored_event = ime_->process_key_event_post_ime_args().event; - EXPECT_EQ(ui::VKEY_C, stored_event->key_code()); - EXPECT_EQ(kFlags, stored_event->flags()); + EXPECT_EQ(ui::VKEY_C, stored_event.key_code()); + EXPECT_EQ(kFlags, stored_event.flags()); EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled); EXPECT_EQ(L'C', inserted_char_); @@ -1030,7 +973,8 @@ TEST_F(InputMethodChromeOSKeyEventTest, DeadKeyPressTest) { 0, DomKey::DeadKeyFromCombiningCharacter('^'), EventTimeForNow()); - ime_->ProcessKeyEventPostIME(&eventA, nullptr, false, true); + ime_->ProcessKeyEventPostIME(&eventA, InputMethodChromeOS::AckCallback(), + false, true); const ui::KeyEvent& key_event = dispatched_key_event_; diff --git a/chromium/ui/base/ime/input_method_factory.cc b/chromium/ui/base/ime/input_method_factory.cc index a1b1798fba6..d93dd27f82f 100644 --- a/chromium/ui/base/ime/input_method_factory.cc +++ b/chromium/ui/base/ime/input_method_factory.cc @@ -8,12 +8,14 @@ #include "base/memory/ptr_util.h" #include "build/build_config.h" #include "ui/base/ime/mock_input_method.h" +#include "ui/base/ui_base_features.h" #include "ui/gfx/switches.h" #if defined(OS_CHROMEOS) #include "ui/base/ime/input_method_chromeos.h" #elif defined(OS_WIN) #include "ui/base/ime/input_method_win.h" +#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) @@ -55,6 +57,8 @@ std::unique_ptr<InputMethod> CreateInputMethod( #if defined(OS_CHROMEOS) return std::make_unique<InputMethodChromeOS>(delegate); #elif defined(OS_WIN) + if (base::FeatureList::IsEnabled(features::kTSFImeSupport)) + return std::make_unique<InputMethodWinTSF>(delegate, widget); return std::make_unique<InputMethodWin>(delegate, widget); #elif defined(OS_MACOSX) return std::make_unique<InputMethodMac>(delegate); diff --git a/chromium/ui/base/ime/input_method_initializer.cc b/chromium/ui/base/ime/input_method_initializer.cc index 026b564b502..b64f362a5d8 100644 --- a/chromium/ui/base/ime/input_method_initializer.cc +++ b/chromium/ui/base/ime/input_method_initializer.cc @@ -11,6 +11,9 @@ #elif defined(USE_AURA) && defined(OS_LINUX) #include "base/logging.h" #include "ui/base/ime/linux/fake_input_method_context_factory.h" +#elif defined(OS_WIN) +#include "ui/base/ime/input_method_factory.h" +#include "ui/base/ime/win/tsf_bridge.h" #endif namespace { @@ -27,12 +30,16 @@ namespace ui { void InitializeInputMethod() { #if defined(OS_CHROMEOS) IMEBridge::Initialize(); +#elif defined(OS_WIN) + TSFBridge::Initialize(); #endif } void ShutdownInputMethod() { #if defined(OS_CHROMEOS) IMEBridge::Shutdown(); +#elif defined(OS_WIN) + TSFBridge::Shutdown(); #endif } @@ -50,6 +57,10 @@ void InitializeInputMethodForTesting() { << "else."; LinuxInputMethodContextFactory::SetInstance( g_linux_input_method_context_factory_for_testing); +#elif defined(OS_WIN) + // Make sure COM is initialized because TSF depends on COM. + CoInitialize(nullptr); + TSFBridge::Initialize(); #endif } @@ -64,6 +75,9 @@ void ShutdownInputMethodForTesting() { LinuxInputMethodContextFactory::SetInstance(NULL); delete g_linux_input_method_context_factory_for_testing; g_linux_input_method_context_factory_for_testing = NULL; +#elif defined(OS_WIN) + TSFBridge::Shutdown(); + CoUninitialize(); #endif } diff --git a/chromium/ui/base/ime/input_method_mac.h b/chromium/ui/base/ime/input_method_mac.h index 25d371881e0..7ca676d113a 100644 --- a/chromium/ui/base/ime/input_method_mac.h +++ b/chromium/ui/base/ime/input_method_mac.h @@ -20,8 +20,6 @@ class UI_BASE_IME_EXPORT InputMethodMac : public InputMethodBase { ~InputMethodMac() override; // Overriden from InputMethod. - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) override; ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; void OnCaretBoundsChanged(const TextInputClient* client) override; void CancelComposition(const TextInputClient* client) override; diff --git a/chromium/ui/base/ime/input_method_mac.mm b/chromium/ui/base/ime/input_method_mac.mm index d048773109c..1b4e953d7c0 100644 --- a/chromium/ui/base/ime/input_method_mac.mm +++ b/chromium/ui/base/ime/input_method_mac.mm @@ -15,11 +15,6 @@ InputMethodMac::InputMethodMac(internal::InputMethodDelegate* delegate) { InputMethodMac::~InputMethodMac() { } -bool InputMethodMac::OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) { - return false; -} - ui::EventDispatchDetails InputMethodMac::DispatchKeyEvent(ui::KeyEvent* event) { // This is used on Mac only to dispatch events post-IME. return DispatchKeyEventPostIME(event); diff --git a/chromium/ui/base/ime/input_method_minimal.cc b/chromium/ui/base/ime/input_method_minimal.cc index d040d9fd415..331fc8e9cf9 100644 --- a/chromium/ui/base/ime/input_method_minimal.cc +++ b/chromium/ui/base/ime/input_method_minimal.cc @@ -19,12 +19,6 @@ InputMethodMinimal::InputMethodMinimal( InputMethodMinimal::~InputMethodMinimal() {} -bool InputMethodMinimal::OnUntranslatedIMEMessage( - const base::NativeEvent& event, - NativeEventResult* result) { - return false; -} - ui::EventDispatchDetails InputMethodMinimal::DispatchKeyEvent( ui::KeyEvent* event) { DCHECK(event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED); diff --git a/chromium/ui/base/ime/input_method_minimal.h b/chromium/ui/base/ime/input_method_minimal.h index 96359226adc..259c9993529 100644 --- a/chromium/ui/base/ime/input_method_minimal.h +++ b/chromium/ui/base/ime/input_method_minimal.h @@ -18,8 +18,6 @@ class UI_BASE_IME_EXPORT InputMethodMinimal : public InputMethodBase { ~InputMethodMinimal() override; // Overriden from InputMethod. - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) override; ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; void OnCaretBoundsChanged(const TextInputClient* client) override; void CancelComposition(const TextInputClient* client) override; diff --git a/chromium/ui/base/ime/input_method_win.cc b/chromium/ui/base/ime/input_method_win.cc index 06f8831222d..4c3b458047e 100644 --- a/chromium/ui/base/ime/input_method_win.cc +++ b/chromium/ui/base/ime/input_method_win.cc @@ -25,10 +25,6 @@ namespace ui { namespace { -// Extra number of chars before and after selection (or composition) range which -// is returned to IME for improving conversion accuracy. -static const size_t kExtraNumberOfChars = 20; - ui::EventDispatchDetails DispatcherDestroyedDetails() { ui::EventDispatchDetails dispatcher_details; dispatcher_details.dispatcher_destroyed = true; @@ -39,14 +35,12 @@ ui::EventDispatchDetails DispatcherDestroyedDetails() { InputMethodWin::InputMethodWin(internal::InputMethodDelegate* delegate, HWND toplevel_window_handle) - : toplevel_window_handle_(toplevel_window_handle), + : InputMethodWinBase(delegate, toplevel_window_handle), pending_requested_direction_(base::i18n::UNKNOWN_DIRECTION), - accept_carriage_return_(false), enabled_(false), is_candidate_popup_open_(false), composing_window_handle_(NULL), weak_ptr_factory_(this) { - SetDelegate(delegate); imm32_manager_.SetInputLanguage(); } @@ -58,7 +52,7 @@ void InputMethodWin::OnFocus() { } bool InputMethodWin::OnUntranslatedIMEMessage( - const base::NativeEvent& event, + const MSG event, InputMethod::NativeEventResult* result) { LRESULT original_result = 0; BOOL handled = FALSE; @@ -106,7 +100,7 @@ ui::EventDispatchDetails InputMethodWin::DispatchKeyEvent(ui::KeyEvent* event) { if (!event->HasNativeEvent()) return DispatchFabricatedKeyEvent(event); - const base::NativeEvent& native_key_event = event->native_event(); + const PlatformEvent& native_key_event = event->native_event(); BOOL handled = FALSE; if (native_key_event.message == WM_CHAR) { auto ref = weak_ptr_factory_.GetWeakPtr(); @@ -178,11 +172,12 @@ ui::EventDispatchDetails InputMethodWin::DispatchKeyEvent(ui::KeyEvent* event) { // messages have been combined in the event processing flow. if (char_msgs.size() <= 1 && GetEngine() && GetEngine()->IsInterestedInKeyEvent()) { - ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::Bind( - &InputMethodWin::ProcessKeyEventDone, weak_ptr_factory_.GetWeakPtr(), - base::Owned(new ui::KeyEvent(*event)), - base::Owned(new std::vector<MSG>(char_msgs))); - GetEngine()->ProcessKeyEvent(*event, callback); + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = + base::BindOnce(&InputMethodWin::ProcessKeyEventDone, + weak_ptr_factory_.GetWeakPtr(), + base::Owned(new ui::KeyEvent(*event)), + base::Owned(new std::vector<MSG>(char_msgs))); + GetEngine()->ProcessKeyEvent(*event, std::move(callback)); return ui::EventDispatchDetails(); } @@ -313,44 +308,7 @@ void InputMethodWin::OnDidChangeFocusedClient( // bounds has not changed. OnCaretBoundsChanged(focused); } - if (focused_before != focused) - accept_carriage_return_ = false; -} - -LRESULT InputMethodWin::OnChar(HWND window_handle, - UINT message, - WPARAM wparam, - LPARAM lparam, - const base::NativeEvent& event, - BOOL* handled) { - *handled = TRUE; - - // We need to send character events to the focused text input client event if - // its text input type is ui::TEXT_INPUT_TYPE_NONE. - if (GetTextInputClient()) { - const base::char16 kCarriageReturn = L'\r'; - const base::char16 ch = static_cast<base::char16>(wparam); - // A mask to determine the previous key state from |lparam|. The value is 1 - // if the key is down before the message is sent, or it is 0 if the key is - // up. - const uint32_t kPrevKeyDownBit = 0x40000000; - if (ch == kCarriageReturn && !(lparam & kPrevKeyDownBit)) - accept_carriage_return_ = true; - // Conditionally ignore '\r' events to work around crbug.com/319100. - // TODO(yukawa, IME): Figure out long-term solution. - if (ch != kCarriageReturn || accept_carriage_return_) { - ui::KeyEvent char_event(event); - GetTextInputClient()->InsertChar(char_event); - } - } - - // Explicitly show the system menu at a good location on [Alt]+[Space]. - // Note: Setting |handled| to FALSE for DefWindowProc triggering of the system - // menu causes undesirable titlebar artifacts in the classic theme. - if (message == WM_SYSCHAR && wparam == VK_SPACE) - gfx::ShowSystemMenu(window_handle); - - return 0; + InputMethodWinBase::OnDidChangeFocusedClient(focused_before, focused); } LRESULT InputMethodWin::OnImeSetContext(HWND window_handle, @@ -481,188 +439,6 @@ LRESULT InputMethodWin::OnImeNotify(UINT message, return 0; } -LRESULT InputMethodWin::OnImeRequest(UINT message, - WPARAM wparam, - LPARAM lparam, - BOOL* handled) { - *handled = FALSE; - - // Should not receive WM_IME_REQUEST message, if IME is disabled. - const ui::TextInputType type = GetTextInputType(); - if (type == ui::TEXT_INPUT_TYPE_NONE || - type == ui::TEXT_INPUT_TYPE_PASSWORD) { - return 0; - } - - switch (wparam) { - case IMR_RECONVERTSTRING: - *handled = TRUE; - return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam)); - case IMR_DOCUMENTFEED: - *handled = TRUE; - return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam)); - case IMR_QUERYCHARPOSITION: - *handled = TRUE; - return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam)); - default: - return 0; - } -} - -LRESULT InputMethodWin::OnDocumentFeed(RECONVERTSTRING* reconv) { - ui::TextInputClient* client = GetTextInputClient(); - if (!client) - return 0; - - gfx::Range text_range; - if (!client->GetTextRange(&text_range) || text_range.is_empty()) - return 0; - - bool result = false; - gfx::Range target_range; - if (client->HasCompositionText()) - result = client->GetCompositionTextRange(&target_range); - - if (!result || target_range.is_empty()) { - if (!client->GetSelectionRange(&target_range) || - !target_range.IsValid()) { - return 0; - } - } - - if (!text_range.Contains(target_range)) - return 0; - - if (target_range.GetMin() - text_range.start() > kExtraNumberOfChars) - text_range.set_start(target_range.GetMin() - kExtraNumberOfChars); - - if (text_range.end() - target_range.GetMax() > kExtraNumberOfChars) - text_range.set_end(target_range.GetMax() + kExtraNumberOfChars); - - size_t len = text_range.length(); - size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); - - if (!reconv) - return need_size; - - if (reconv->dwSize < need_size) - return 0; - - base::string16 text; - if (!GetTextInputClient()->GetTextFromRange(text_range, &text)) - return 0; - DCHECK_EQ(text_range.length(), text.length()); - - reconv->dwVersion = 0; - reconv->dwStrLen = len; - reconv->dwStrOffset = sizeof(RECONVERTSTRING); - reconv->dwCompStrLen = - client->HasCompositionText() ? target_range.length() : 0; - reconv->dwCompStrOffset = - (target_range.GetMin() - text_range.start()) * sizeof(WCHAR); - reconv->dwTargetStrLen = target_range.length(); - reconv->dwTargetStrOffset = reconv->dwCompStrOffset; - - memcpy((char*)reconv + sizeof(RECONVERTSTRING), - text.c_str(), len * sizeof(WCHAR)); - - // According to Microsoft API document, IMR_RECONVERTSTRING and - // IMR_DOCUMENTFEED should return reconv, but some applications return - // need_size. - return reinterpret_cast<LRESULT>(reconv); -} - -LRESULT InputMethodWin::OnReconvertString(RECONVERTSTRING* reconv) { - ui::TextInputClient* client = GetTextInputClient(); - if (!client) - return 0; - - // If there is a composition string already, we don't allow reconversion. - if (client->HasCompositionText()) - return 0; - - gfx::Range text_range; - if (!client->GetTextRange(&text_range) || text_range.is_empty()) - return 0; - - gfx::Range selection_range; - if (!client->GetSelectionRange(&selection_range) || - selection_range.is_empty()) { - return 0; - } - - DCHECK(text_range.Contains(selection_range)); - - size_t len = selection_range.length(); - size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); - - if (!reconv) - return need_size; - - if (reconv->dwSize < need_size) - return 0; - - // TODO(penghuang): Return some extra context to help improve IME's - // reconversion accuracy. - base::string16 text; - if (!GetTextInputClient()->GetTextFromRange(selection_range, &text)) - return 0; - DCHECK_EQ(selection_range.length(), text.length()); - - reconv->dwVersion = 0; - reconv->dwStrLen = len; - reconv->dwStrOffset = sizeof(RECONVERTSTRING); - reconv->dwCompStrLen = len; - reconv->dwCompStrOffset = 0; - reconv->dwTargetStrLen = len; - reconv->dwTargetStrOffset = 0; - - memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING), - text.c_str(), len * sizeof(WCHAR)); - - // According to Microsoft API document, IMR_RECONVERTSTRING and - // IMR_DOCUMENTFEED should return reconv, but some applications return - // need_size. - return reinterpret_cast<LRESULT>(reconv); -} - -LRESULT InputMethodWin::OnQueryCharPosition(IMECHARPOSITION* char_positon) { - if (!char_positon) - return 0; - - if (char_positon->dwSize < sizeof(IMECHARPOSITION)) - return 0; - - ui::TextInputClient* client = GetTextInputClient(); - if (!client) - return 0; - - // Tentatively assume that the returned value from |client| is DIP (Density - // Independent Pixel). See the comment in text_input_client.h and - // http://crbug.com/360334. - gfx::Rect dip_rect; - if (client->HasCompositionText()) { - if (!client->GetCompositionCharacterBounds(char_positon->dwCharPos, - &dip_rect)) { - return 0; - } - } else { - // If there is no composition and the first character is queried, returns - // the caret bounds. This behavior is the same to that of RichEdit control. - if (char_positon->dwCharPos != 0) - return 0; - dip_rect = client->GetCaretBounds(); - } - const gfx::Rect rect = - display::win::ScreenWin::DIPToScreenRect(toplevel_window_handle_, - dip_rect); - - char_positon->pt.x = rect.x(); - char_positon->pt.y = rect.y(); - char_positon->cLineHeight = rect.height(); - return 1; // returns non-zero value when succeeded. -} - void InputMethodWin::RefreshInputLanguage() { TextInputType type_original = GetTextInputType(); imm32_manager_.SetInputLanguage(); @@ -677,18 +453,6 @@ void InputMethodWin::RefreshInputLanguage() { } } -bool InputMethodWin::IsWindowFocused(const TextInputClient* client) const { - if (!client) - return false; - // When Aura is enabled, |attached_window_handle| should always be a top-level - // window. So we can safely assume that |attached_window_handle| is ready for - // receiving keyboard input as long as it is an active window. This works well - // even when the |attached_window_handle| becomes active but has not received - // WM_FOCUS yet. - return toplevel_window_handle_ && - GetActiveWindow() == toplevel_window_handle_; -} - ui::EventDispatchDetails InputMethodWin::DispatchFabricatedKeyEvent( ui::KeyEvent* event) { // The key event if from calling input.ime.sendKeyEvent or test. diff --git a/chromium/ui/base/ime/input_method_win.h b/chromium/ui/base/ime/input_method_win.h index c41504e29ff..603c456fb16 100644 --- a/chromium/ui/base/ime/input_method_win.h +++ b/chromium/ui/base/ime/input_method_win.h @@ -11,13 +11,13 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "ui/base/ime/input_method_base.h" +#include "ui/base/ime/input_method_win_base.h" #include "ui/base/ime/win/imm32_manager.h" namespace ui { // A common InputMethod implementation based on IMM32. -class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { +class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodWinBase { public: InputMethodWin(internal::InputMethodDelegate* delegate, HWND toplevel_window_handle); @@ -27,7 +27,7 @@ class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { void OnFocus() override; // Overridden from InputMethod: - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, + bool OnUntranslatedIMEMessage(const MSG event, NativeEventResult* result) override; ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; void OnTextInputTypeChanged(const TextInputClient* client) override; @@ -47,14 +47,6 @@ class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { TextInputClient* focused) override; private: - // For both WM_CHAR and WM_SYSCHAR - LRESULT OnChar(HWND window_handle, - UINT message, - WPARAM wparam, - LPARAM lparam, - const base::NativeEvent& event, - BOOL* handled); - LRESULT OnImeSetContext(HWND window_handle, UINT message, WPARAM wparam, @@ -80,23 +72,8 @@ class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { LPARAM lparam, BOOL* handled); - // Some IMEs rely on WM_IME_REQUEST message even when TSF is enabled. So - // OnImeRequest (and its actual implementations as OnDocumentFeed, - // OnReconvertString, and OnQueryCharPosition) are placed in this base class. - LRESULT OnImeRequest(UINT message, - WPARAM wparam, - LPARAM lparam, - BOOL* handled); - LRESULT OnDocumentFeed(RECONVERTSTRING* reconv); - LRESULT OnReconvertString(RECONVERTSTRING* reconv); - LRESULT OnQueryCharPosition(IMECHARPOSITION* char_positon); - void RefreshInputLanguage(); - // Returns true if the Win32 native window bound to |client| is considered - // to be ready for receiving keyboard input. - bool IsWindowFocused(const TextInputClient* client) const; - ui::EventDispatchDetails DispatchFabricatedKeyEvent(ui::KeyEvent* event); // Asks the client to confirm current composition text. @@ -118,21 +95,11 @@ class UI_BASE_IME_EXPORT InputMethodWin : public InputMethodBase { // (See "ui/base/ime/win/ime_input.h" for its details.) ui::IMM32Manager imm32_manager_; - // The toplevel window handle. - // On non-Aura environment, this value is not used and always NULL. - const HWND toplevel_window_handle_; - // The new text direction and layout alignment requested by the user by // pressing ctrl-shift. It'll be sent to the text input client when the key // is released. base::i18n::TextDirection pending_requested_direction_; - // Represents if WM_CHAR[wparam=='\r'] should be dispatched to the focused - // text input client or ignored silently. This flag is introduced as a quick - // workaround against crbug.com/319100 - // TODO(yukawa, IME): Figure out long-term solution. - bool accept_carriage_return_; - // True when an IME should be allowed to process key events. bool enabled_; diff --git a/chromium/ui/base/ime/input_method_win_base.cc b/chromium/ui/base/ime/input_method_win_base.cc new file mode 100644 index 00000000000..0e89451afb3 --- /dev/null +++ b/chromium/ui/base/ime/input_method_win_base.cc @@ -0,0 +1,276 @@ +// 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_win_base.h" + +#include <stddef.h> +#include <stdint.h> +#include <cwctype> + +#include "base/auto_reset.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "ui/base/ime/ime_bridge.h" +#include "ui/base/ime/ime_engine_handler_interface.h" +#include "ui/base/ime/text_input_client.h" +#include "ui/base/ime/win/tsf_input_scope.h" +#include "ui/display/win/screen_win.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "ui/gfx/win/hwnd_util.h" + +namespace ui { +namespace { + +// Extra number of chars before and after selection (or composition) range which +// is returned to IME for improving conversion accuracy. +constexpr size_t kExtraNumberOfChars = 20; + +} // namespace + +InputMethodWinBase::InputMethodWinBase(internal::InputMethodDelegate* delegate, + HWND toplevel_window_handle) + : InputMethodBase(delegate), + toplevel_window_handle_(toplevel_window_handle) {} + +InputMethodWinBase::~InputMethodWinBase() {} + +void InputMethodWinBase::OnDidChangeFocusedClient( + TextInputClient* focused_before, + TextInputClient* focused) { + if (focused_before != focused) + accept_carriage_return_ = false; +} + +bool InputMethodWinBase::IsWindowFocused(const TextInputClient* client) const { + if (!client) + return false; + // When Aura is enabled, |attached_window_handle| should always be a top-level + // window. So we can safely assume that |attached_window_handle| is ready for + // receiving keyboard input as long as it is an active window. This works well + // even when the |attached_window_handle| becomes active but has not received + // WM_FOCUS yet. + return toplevel_window_handle_ && + GetActiveWindow() == toplevel_window_handle_; +} + +LRESULT InputMethodWinBase::OnChar(HWND window_handle, + UINT message, + WPARAM wparam, + LPARAM lparam, + const PlatformEvent& event, + BOOL* handled) { + *handled = TRUE; + + // We need to send character events to the focused text input client event if + // its text input type is ui::TEXT_INPUT_TYPE_NONE. + if (GetTextInputClient()) { + const base::char16 kCarriageReturn = L'\r'; + const base::char16 ch = static_cast<base::char16>(wparam); + // A mask to determine the previous key state from |lparam|. The value is 1 + // if the key is down before the message is sent, or it is 0 if the key is + // up. + const uint32_t kPrevKeyDownBit = 0x40000000; + if (ch == kCarriageReturn && !(lparam & kPrevKeyDownBit)) + accept_carriage_return_ = true; + // Conditionally ignore '\r' events to work around https://crbug.com/319100. + // TODO(yukawa, IME): Figure out long-term solution. + if (ch != kCarriageReturn || accept_carriage_return_) { + ui::KeyEvent char_event(event); + GetTextInputClient()->InsertChar(char_event); + } + } + + // Explicitly show the system menu at a good location on [Alt]+[Space]. + // Note: Setting |handled| to FALSE for DefWindowProc triggering of the system + // menu causes undesirable titlebar artifacts in the classic theme. + if (message == WM_SYSCHAR && wparam == VK_SPACE) + gfx::ShowSystemMenu(window_handle); + + return 0; +} + +LRESULT InputMethodWinBase::OnImeRequest(UINT message, + WPARAM wparam, + LPARAM lparam, + BOOL* handled) { + *handled = FALSE; + + // Should not receive WM_IME_REQUEST message, if IME is disabled. + const ui::TextInputType type = GetTextInputType(); + if (type == ui::TEXT_INPUT_TYPE_NONE || + type == ui::TEXT_INPUT_TYPE_PASSWORD) { + return 0; + } + + switch (wparam) { + case IMR_RECONVERTSTRING: + *handled = TRUE; + return OnReconvertString(reinterpret_cast<RECONVERTSTRING*>(lparam)); + case IMR_DOCUMENTFEED: + *handled = TRUE; + return OnDocumentFeed(reinterpret_cast<RECONVERTSTRING*>(lparam)); + case IMR_QUERYCHARPOSITION: + *handled = TRUE; + return OnQueryCharPosition(reinterpret_cast<IMECHARPOSITION*>(lparam)); + default: + return 0; + } +} + +LRESULT InputMethodWinBase::OnDocumentFeed(RECONVERTSTRING* reconv) { + ui::TextInputClient* client = GetTextInputClient(); + if (!client) + return 0; + + gfx::Range text_range; + if (!client->GetTextRange(&text_range) || text_range.is_empty()) + return 0; + + bool result = false; + gfx::Range target_range; + if (client->HasCompositionText()) + result = client->GetCompositionTextRange(&target_range); + + if (!result || target_range.is_empty()) { + if (!client->GetSelectionRange(&target_range) || !target_range.IsValid()) { + return 0; + } + } + + if (!text_range.Contains(target_range)) + return 0; + + if (target_range.GetMin() - text_range.start() > kExtraNumberOfChars) + text_range.set_start(target_range.GetMin() - kExtraNumberOfChars); + + if (text_range.end() - target_range.GetMax() > kExtraNumberOfChars) + text_range.set_end(target_range.GetMax() + kExtraNumberOfChars); + + size_t len = text_range.length(); + size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); + + if (!reconv) + return need_size; + + if (reconv->dwSize < need_size) + return 0; + + base::string16 text; + if (!GetTextInputClient()->GetTextFromRange(text_range, &text)) + return 0; + DCHECK_EQ(text_range.length(), text.length()); + + reconv->dwVersion = 0; + reconv->dwStrLen = len; + reconv->dwStrOffset = sizeof(RECONVERTSTRING); + reconv->dwCompStrLen = + client->HasCompositionText() ? target_range.length() : 0; + reconv->dwCompStrOffset = + (target_range.GetMin() - text_range.start()) * sizeof(WCHAR); + reconv->dwTargetStrLen = target_range.length(); + reconv->dwTargetStrOffset = reconv->dwCompStrOffset; + + memcpy((char*)reconv + sizeof(RECONVERTSTRING), text.c_str(), + len * sizeof(WCHAR)); + + // According to Microsoft API document, IMR_RECONVERTSTRING and + // IMR_DOCUMENTFEED should return reconv, but some applications return + // need_size. + return reinterpret_cast<LRESULT>(reconv); +} + +LRESULT InputMethodWinBase::OnReconvertString(RECONVERTSTRING* reconv) { + ui::TextInputClient* client = GetTextInputClient(); + if (!client) + return 0; + + // If there is a composition string already, we don't allow reconversion. + if (client->HasCompositionText()) + return 0; + + gfx::Range text_range; + if (!client->GetTextRange(&text_range) || text_range.is_empty()) + return 0; + + gfx::Range selection_range; + if (!client->GetSelectionRange(&selection_range) || + selection_range.is_empty()) { + return 0; + } + + DCHECK(text_range.Contains(selection_range)); + + size_t len = selection_range.length(); + size_t need_size = sizeof(RECONVERTSTRING) + len * sizeof(WCHAR); + + if (!reconv) + return need_size; + + if (reconv->dwSize < need_size) + return 0; + + // TODO(penghuang): Return some extra context to help improve IME's + // reconversion accuracy. + base::string16 text; + if (!GetTextInputClient()->GetTextFromRange(selection_range, &text)) + return 0; + DCHECK_EQ(selection_range.length(), text.length()); + + reconv->dwVersion = 0; + reconv->dwStrLen = len; + reconv->dwStrOffset = sizeof(RECONVERTSTRING); + reconv->dwCompStrLen = len; + reconv->dwCompStrOffset = 0; + reconv->dwTargetStrLen = len; + reconv->dwTargetStrOffset = 0; + + memcpy(reinterpret_cast<char*>(reconv) + sizeof(RECONVERTSTRING), + text.c_str(), len * sizeof(WCHAR)); + + // According to Microsoft API document, IMR_RECONVERTSTRING and + // IMR_DOCUMENTFEED should return reconv, but some applications return + // need_size. + return reinterpret_cast<LRESULT>(reconv); +} + +LRESULT InputMethodWinBase::OnQueryCharPosition(IMECHARPOSITION* char_positon) { + if (!char_positon) + return 0; + + if (char_positon->dwSize < sizeof(IMECHARPOSITION)) + return 0; + + ui::TextInputClient* client = GetTextInputClient(); + if (!client) + return 0; + + // Tentatively assume that the returned value from |client| is DIP (Density + // Independent Pixel). See the comment in text_input_client.h and + // http://crbug.com/360334. + gfx::Rect dip_rect; + if (client->HasCompositionText()) { + if (!client->GetCompositionCharacterBounds(char_positon->dwCharPos, + &dip_rect)) { + return 0; + } + } else { + // If there is no composition and the first character is queried, returns + // the caret bounds. This behavior is the same to that of RichEdit control. + if (char_positon->dwCharPos != 0) + return 0; + dip_rect = client->GetCaretBounds(); + } + const gfx::Rect rect = display::win::ScreenWin::DIPToScreenRect( + toplevel_window_handle_, dip_rect); + + char_positon->pt.x = rect.x(); + char_positon->pt.y = rect.y(); + char_positon->cLineHeight = rect.height(); + return 1; // returns non-zero value when succeeded. +} + +} // namespace ui diff --git a/chromium/ui/base/ime/input_method_win_base.h b/chromium/ui/base/ime/input_method_win_base.h new file mode 100644 index 00000000000..9e63213046e --- /dev/null +++ b/chromium/ui/base/ime/input_method_win_base.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_BASE_IME_INPUT_METHOD_WIN_BASE_H_ +#define UI_BASE_IME_INPUT_METHOD_WIN_BASE_H_ + +#include <windows.h> + +#include <string> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "ui/base/ime/input_method_base.h" +#include "ui/base/ime/win/imm32_manager.h" + +namespace ui { + +// A common InputMethod base implementation for Windows. +class UI_BASE_IME_EXPORT InputMethodWinBase : public InputMethodBase { + public: + InputMethodWinBase(internal::InputMethodDelegate* delegate, + HWND toplevel_window_handle); + ~InputMethodWinBase() override; + + protected: + void OnDidChangeFocusedClient(TextInputClient* focused_before, + TextInputClient* focused) override; + + // Returns true if the Win32 native window bound to |client| is considered + // to be ready for receiving keyboard input. + bool IsWindowFocused(const TextInputClient* client) const; + + // For both WM_CHAR and WM_SYSCHAR + LRESULT OnChar(HWND window_handle, + UINT message, + WPARAM wparam, + LPARAM lparam, + const PlatformEvent& event, + BOOL* handled); + + // Some IMEs rely on WM_IME_REQUEST message even when TSF is enabled. So + // OnImeRequest (and its actual implementations as OnDocumentFeed, + // OnReconvertString, and OnQueryCharPosition) are placed in this base class. + LRESULT OnImeRequest(UINT message, + WPARAM wparam, + LPARAM lparam, + BOOL* handled); + LRESULT OnDocumentFeed(RECONVERTSTRING* reconv); + LRESULT OnReconvertString(RECONVERTSTRING* reconv); + LRESULT OnQueryCharPosition(IMECHARPOSITION* char_positon); + + // The toplevel window handle. + const HWND toplevel_window_handle_; + + // Represents if WM_CHAR[wparam=='\r'] should be dispatched to the focused + // text input client or ignored silently. This flag is introduced as a quick + // workaround against https://crbug.com/319100 + // TODO(yukawa, IME): Figure out long-term solution. + bool accept_carriage_return_ = false; + + DISALLOW_COPY_AND_ASSIGN(InputMethodWinBase); +}; + +} // namespace ui + +#endif // UI_BASE_IME_INPUT_METHOD_WIN_BASE_H_ diff --git a/chromium/ui/base/ime/input_method_win_tsf.cc b/chromium/ui/base/ime/input_method_win_tsf.cc new file mode 100644 index 00000000000..0cf6f88e39f --- /dev/null +++ b/chromium/ui/base/ime/input_method_win_tsf.cc @@ -0,0 +1,147 @@ +// 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_win_tsf.h" + +#include "ui/base/ime/text_input_client.h" +#include "ui/base/ime/win/tsf_bridge.h" +#include "ui/base/ime/win/tsf_event_router.h" + +namespace ui { + +class InputMethodWinTSF::TSFEventObserver : public TSFEventRouterObserver { + public: + TSFEventObserver() = default; + + // Returns true if we know for sure that a candidate window (or IME suggest, + // etc.) is open. + bool IsCandidatePopupOpen() const { return is_candidate_popup_open_; } + + // Overridden from TSFEventRouterObserver: + void OnCandidateWindowCountChanged(size_t window_count) override { + is_candidate_popup_open_ = (window_count != 0); + } + + private: + // True if we know for sure that a candidate window is open. + bool is_candidate_popup_open_ = false; + + DISALLOW_COPY_AND_ASSIGN(TSFEventObserver); +}; + +InputMethodWinTSF::InputMethodWinTSF(internal::InputMethodDelegate* delegate, + HWND toplevel_window_handle) + : InputMethodWinBase(delegate, toplevel_window_handle), + tsf_event_observer_(new TSFEventObserver()), + tsf_event_router_(new TSFEventRouter(tsf_event_observer_.get())) {} + +InputMethodWinTSF::~InputMethodWinTSF() {} + +ui::EventDispatchDetails InputMethodWinTSF::DispatchKeyEvent( + ui::KeyEvent* event) { + // TODO(dtapuska): Handle WM_CHAR events. + return ui::EventDispatchDetails(); +} + +void InputMethodWinTSF::OnFocus() { + tsf_event_router_->SetManager( + ui::TSFBridge::GetInstance()->GetThreadManager().Get()); +} + +void InputMethodWinTSF::OnBlur() { + tsf_event_router_->SetManager(nullptr); +} + +bool InputMethodWinTSF::OnUntranslatedIMEMessage( + const MSG event, + InputMethod::NativeEventResult* result) { + LRESULT original_result = 0; + BOOL handled = FALSE; + // Even when TSF is enabled, following IMM32/Win32 messages must be handled. + switch (event.message) { + case WM_IME_REQUEST: + // Some TSF-native TIPs (Text Input Processors) such as ATOK and Mozc + // still rely on WM_IME_REQUEST message to implement reverse conversion. + original_result = + OnImeRequest(event.message, event.wParam, event.lParam, &handled); + break; + case WM_CHAR: + case WM_SYSCHAR: + // ui::InputMethod interface is responsible for handling Win32 character + // messages. For instance, we will be here in the following cases. + // - TIP is not activated. (e.g, the current language profile is English) + // - TIP does not handle and WM_KEYDOWN and WM_KEYDOWN is translated into + // WM_CHAR by TranslateMessage API. (e.g, TIP is turned off) + // - Another application sends WM_CHAR through SendMessage API. + original_result = OnChar(event.hwnd, event.message, event.wParam, + event.lParam, event, &handled); + break; + } + + if (result) + *result = original_result; + return !!handled; +} + +void InputMethodWinTSF::OnTextInputTypeChanged(const TextInputClient* client) { + if (!IsTextInputClientFocused(client) || !IsWindowFocused(client)) + return; + ui::TSFBridge::GetInstance()->CancelComposition(); + ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(client); +} + +void InputMethodWinTSF::OnCaretBoundsChanged(const TextInputClient* client) { + if (!IsTextInputClientFocused(client) || !IsWindowFocused(client)) + return; + ui::TSFBridge::GetInstance()->OnTextLayoutChanged(); +} + +void InputMethodWinTSF::CancelComposition(const TextInputClient* client) { + if (IsTextInputClientFocused(client) && IsWindowFocused(client)) + ui::TSFBridge::GetInstance()->CancelComposition(); +} + +void InputMethodWinTSF::DetachTextInputClient(TextInputClient* client) { + InputMethodWinBase::DetachTextInputClient(client); + ui::TSFBridge::GetInstance()->RemoveFocusedClient(client); +} + +bool InputMethodWinTSF::IsCandidatePopupOpen() const { + return tsf_event_observer_->IsCandidatePopupOpen(); +} + +void InputMethodWinTSF::OnWillChangeFocusedClient( + TextInputClient* focused_before, + TextInputClient* focused) { + if (IsWindowFocused(focused_before)) { + ConfirmCompositionText(); + ui::TSFBridge::GetInstance()->RemoveFocusedClient(focused_before); + } +} + +void InputMethodWinTSF::OnDidChangeFocusedClient( + TextInputClient* focused_before, + TextInputClient* focused) { + if (IsWindowFocused(focused) && IsTextInputClientFocused(focused)) { + ui::TSFBridge::GetInstance()->SetFocusedClient(toplevel_window_handle_, + focused); + + // Force to update the input type since client's TextInputStateChanged() + // function might not be called if text input types before the client loses + // focus and after it acquires focus again are the same. + OnTextInputTypeChanged(focused); + + // Force to update caret bounds, in case the client thinks that the caret + // bounds has not changed. + OnCaretBoundsChanged(focused); + } + InputMethodWinBase::OnDidChangeFocusedClient(focused_before, focused); +} + +void InputMethodWinTSF::ConfirmCompositionText() { + if (!IsTextInputTypeNone()) + ui::TSFBridge::GetInstance()->ConfirmComposition(); +} + +} // namespace ui diff --git a/chromium/ui/base/ime/input_method_win_tsf.h b/chromium/ui/base/ime/input_method_win_tsf.h new file mode 100644 index 00000000000..4d8a7c465bd --- /dev/null +++ b/chromium/ui/base/ime/input_method_win_tsf.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_BASE_IME_INPUT_METHOD_WIN_TSF_H_ +#define UI_BASE_IME_INPUT_METHOD_WIN_TSF_H_ + +#include <windows.h> + +#include <string> + +#include "ui/base/ime/input_method_win_base.h" + +namespace ui { + +class TSFEventRouter; + +// An InputMethod implementation based on Windows TSF API. +class UI_BASE_IME_EXPORT InputMethodWinTSF : public InputMethodWinBase { + public: + InputMethodWinTSF(internal::InputMethodDelegate* delegate, + HWND toplevel_window_handle); + ~InputMethodWinTSF() override; + + // Overridden from InputMethod: + ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; + void OnFocus() override; + void OnBlur() override; + bool OnUntranslatedIMEMessage(const MSG event, + NativeEventResult* result) override; + void OnTextInputTypeChanged(const TextInputClient* client) override; + void OnCaretBoundsChanged(const TextInputClient* client) override; + void CancelComposition(const TextInputClient* client) override; + void DetachTextInputClient(TextInputClient* client) override; + bool IsCandidatePopupOpen() const override; + + // Overridden from InputMethodBase: + void OnWillChangeFocusedClient(TextInputClient* focused_before, + TextInputClient* focused) override; + void OnDidChangeFocusedClient(TextInputClient* focused_before, + TextInputClient* focused) override; + + private: + class TSFEventObserver; + + // Asks the client to confirm current composition text. + void ConfirmCompositionText(); + + // TSF event router and observer. + std::unique_ptr<TSFEventObserver> tsf_event_observer_; + std::unique_ptr<TSFEventRouter> tsf_event_router_; + + DISALLOW_COPY_AND_ASSIGN(InputMethodWinTSF); +}; + +} // namespace ui + +#endif // UI_BASE_IME_INPUT_METHOD_WIN_TSF_H_ diff --git a/chromium/ui/base/ime/linux/fake_input_method_context_factory.cc b/chromium/ui/base/ime/linux/fake_input_method_context_factory.cc index ec651ce0c04..f8d3102ee48 100644 --- a/chromium/ui/base/ime/linux/fake_input_method_context_factory.cc +++ b/chromium/ui/base/ime/linux/fake_input_method_context_factory.cc @@ -4,7 +4,6 @@ #include "ui/base/ime/linux/fake_input_method_context_factory.h" -#include "base/memory/ptr_util.h" #include "ui/base/ime/linux/fake_input_method_context.h" namespace ui { diff --git a/chromium/ui/base/ime/mock_input_method.cc b/chromium/ui/base/ime/mock_input_method.cc index 43d6b07f516..79ae727e400 100644 --- a/chromium/ui/base/ime/mock_input_method.cc +++ b/chromium/ui/base/ime/mock_input_method.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ui/base/ime/mock_input_method.h" +#include "build/build_config.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/events/event.h" @@ -55,12 +56,14 @@ void MockInputMethod::OnBlur() { observer.OnBlur(); } -bool MockInputMethod::OnUntranslatedIMEMessage(const base::NativeEvent& event, +#if defined(OS_WIN) +bool MockInputMethod::OnUntranslatedIMEMessage(const MSG event, NativeEventResult* result) { if (result) *result = NativeEventResult(); return false; } +#endif void MockInputMethod::OnTextInputTypeChanged(const TextInputClient* client) { for (InputMethodObserver& observer : observer_list_) diff --git a/chromium/ui/base/ime/mock_input_method.h b/chromium/ui/base/ime/mock_input_method.h index b841a7a1d26..d3058a4608f 100644 --- a/chromium/ui/base/ime/mock_input_method.h +++ b/chromium/ui/base/ime/mock_input_method.h @@ -10,6 +10,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/observer_list.h" +#include "build/build_config.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/input_method_observer.h" #include "ui/base/ime/ui_base_ime_export.h" @@ -32,8 +33,12 @@ class UI_BASE_IME_EXPORT MockInputMethod : public InputMethod { void SetDelegate(internal::InputMethodDelegate* delegate) override; void OnFocus() override; void OnBlur() override; - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, + +#if defined(OS_WIN) + bool OnUntranslatedIMEMessage(const MSG event, NativeEventResult* result) override; +#endif + void SetFocusedTextInputClient(TextInputClient* client) override; void DetachTextInputClient(TextInputClient* client) override; TextInputClient* GetTextInputClient() const override; diff --git a/chromium/ui/base/ime/win/imm32_manager.cc b/chromium/ui/base/ime/win/imm32_manager.cc index ed9b186d6c6..9f9e6548840 100644 --- a/chromium/ui/base/ime/win/imm32_manager.cc +++ b/chromium/ui/base/ime/win/imm32_manager.cc @@ -78,13 +78,13 @@ void GetImeTextSpans(HIMC imm_context, ime_text_span.start_offset = clause_data[i]; ime_text_span.end_offset = clause_data[i + 1]; ime_text_span.underline_color = SK_ColorBLACK; - ime_text_span.thick = false; + ime_text_span.thickness = ui::ImeTextSpan::Thickness::kThin; ime_text_span.background_color = SK_ColorTRANSPARENT; // Use thick underline for the target clause. if (ime_text_span.start_offset >= static_cast<uint32_t>(target_start) && ime_text_span.end_offset <= static_cast<uint32_t>(target_end)) { - ime_text_span.thick = true; + ime_text_span.thickness = ui::ImeTextSpan::Thickness::kThick; } ime_text_spans->push_back(ime_text_span); } @@ -330,24 +330,24 @@ void IMM32Manager::GetCompositionInfo(HIMC imm_context, return; ImeTextSpan ime_text_span; - ime_text_span.underline_color = SK_ColorBLACK; + ime_text_span.underline_color = SK_ColorTRANSPARENT; ime_text_span.background_color = SK_ColorTRANSPARENT; if (target_start > 0) { ime_text_span.start_offset = 0U; ime_text_span.end_offset = static_cast<uint32_t>(target_start); - ime_text_span.thick = false; + ime_text_span.thickness = ui::ImeTextSpan::Thickness::kThin; composition->ime_text_spans.push_back(ime_text_span); } if (target_end > target_start) { ime_text_span.start_offset = static_cast<uint32_t>(target_start); ime_text_span.end_offset = static_cast<uint32_t>(target_end); - ime_text_span.thick = true; + ime_text_span.thickness = ui::ImeTextSpan::Thickness::kThick; composition->ime_text_spans.push_back(ime_text_span); } if (target_end < length) { ime_text_span.start_offset = static_cast<uint32_t>(target_end); ime_text_span.end_offset = static_cast<uint32_t>(length); - ime_text_span.thick = false; + ime_text_span.thickness = ui::ImeTextSpan::Thickness::kThin; composition->ime_text_spans.push_back(ime_text_span); } } diff --git a/chromium/ui/base/ime/win/mock_tsf_bridge.cc b/chromium/ui/base/ime/win/mock_tsf_bridge.cc new file mode 100644 index 00000000000..a69f37603e6 --- /dev/null +++ b/chromium/ui/base/ime/win/mock_tsf_bridge.cc @@ -0,0 +1,70 @@ +// 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/win/mock_tsf_bridge.h" + +#include "base/logging.h" +#include "ui/base/ime/text_input_client.h" + +namespace ui { + +MockTSFBridge::MockTSFBridge() = default; + +MockTSFBridge::~MockTSFBridge() = default; + +bool MockTSFBridge::CancelComposition() { + ++cancel_composition_call_count_; + return true; +} + +bool MockTSFBridge::ConfirmComposition() { + ++confirm_composition_call_count_; + return true; +} + +void MockTSFBridge::OnTextInputTypeChanged(const TextInputClient* client) { + latest_text_input_type_ = client->GetTextInputType(); +} + +void MockTSFBridge::OnTextLayoutChanged() { + ++on_text_layout_changed_; +} + +void MockTSFBridge::SetFocusedClient(HWND focused_window, + TextInputClient* client) { + ++set_focused_client_call_count_; + focused_window_ = focused_window; + text_input_client_ = client; +} + +void MockTSFBridge::RemoveFocusedClient(TextInputClient* client) { + ++remove_focused_client_call_count_; + DCHECK_EQ(client, text_input_client_); + text_input_client_ = nullptr; + focused_window_ = nullptr; +} + +Microsoft::WRL::ComPtr<ITfThreadMgr> MockTSFBridge::GetThreadManager() { + return thread_manager_; +} + +TextInputClient* MockTSFBridge::GetFocusedTextInputClient() const { + return text_input_client_; +} + +void MockTSFBridge::Reset() { + enable_ime_call_count_ = 0; + disable_ime_call_count_ = 0; + cancel_composition_call_count_ = 0; + confirm_composition_call_count_ = 0; + on_text_layout_changed_ = 0; + associate_focus_call_count_ = 0; + set_focused_client_call_count_ = 0; + remove_focused_client_call_count_ = 0; + text_input_client_ = nullptr; + focused_window_ = nullptr; + latest_text_input_type_ = TEXT_INPUT_TYPE_NONE; +} + +} // namespace ui diff --git a/chromium/ui/base/ime/win/mock_tsf_bridge.h b/chromium/ui/base/ime/win/mock_tsf_bridge.h new file mode 100644 index 00000000000..5baca1ade05 --- /dev/null +++ b/chromium/ui/base/ime/win/mock_tsf_bridge.h @@ -0,0 +1,99 @@ +// 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_WIN_MOCK_TSF_BRIDGE_H_ +#define UI_BASE_IME_WIN_MOCK_TSF_BRIDGE_H_ + +#include <msctf.h> +#include <wrl/client.h> + +#include "base/compiler_specific.h" +#include "ui/base/ime/text_input_type.h" +#include "ui/base/ime/win/tsf_bridge.h" + +namespace ui { + +class MockTSFBridge : public TSFBridge { + public: + MockTSFBridge(); + ~MockTSFBridge() override; + + // TSFBridge: + bool CancelComposition() override; + bool ConfirmComposition() override; + void OnTextInputTypeChanged(const TextInputClient* client) override; + void OnTextLayoutChanged() override; + void SetFocusedClient(HWND focused_window, TextInputClient* client) override; + void RemoveFocusedClient(TextInputClient* client) override; + Microsoft::WRL::ComPtr<ITfThreadMgr> GetThreadManager() override; + TextInputClient* GetFocusedTextInputClient() const override; + + // Resets MockTSFBridge state including function call counter. + void Reset(); + + // Call count of EnableIME(). + unsigned enable_ime_call_count() const { return enable_ime_call_count_; } + + // Call count of DisableIME(). + unsigned disable_ime_call_count() const { return disable_ime_call_count_; } + + // Call count of CancelComposition(). + unsigned cancel_composition_call_count() const { + return cancel_composition_call_count_; + } + + // Call count of ConfirmComposition(). + unsigned confirm_composition_call_count() const { + return confirm_composition_call_count_; + } + + // Call count of OnTextLayoutChanged(). + unsigned on_text_layout_changed() const { return on_text_layout_changed_; } + + // Call count of AssociateFocus(). + unsigned associate_focus_call_count() const { + return associate_focus_call_count_; + } + + // Call count of SetFocusClient(). + unsigned set_focused_client_call_count() const { + return set_focused_client_call_count_; + } + + // Call count of RemoveFocusedClient(). + unsigned remove_focused_client_call_count() const { + return remove_focused_client_call_count_; + } + + // Returns current TextInputClient. + TextInputClient* text_input_clinet() const { return text_input_client_; } + + // Returns currently focused window handle. + HWND focused_window() const { return focused_window_; } + + // Returns latest text input type. + TextInputType latest_text_iput_type() const { + return latest_text_input_type_; + } + + private: + unsigned enable_ime_call_count_ = 0; + unsigned disable_ime_call_count_ = 0; + unsigned cancel_composition_call_count_ = 0; + unsigned confirm_composition_call_count_ = 0; + unsigned on_text_layout_changed_ = 0; + unsigned associate_focus_call_count_ = 0; + unsigned set_focused_client_call_count_ = 0; + unsigned remove_focused_client_call_count_ = 0; + TextInputClient* text_input_client_ = nullptr; + HWND focused_window_ = nullptr; + TextInputType latest_text_input_type_ = TEXT_INPUT_TYPE_NONE; + Microsoft::WRL::ComPtr<ITfThreadMgr> thread_manager_; + + DISALLOW_COPY_AND_ASSIGN(MockTSFBridge); +}; + +} // namespace ui + +#endif // UI_BASE_IME_WIN_MOCK_TSF_BRIDGE_H_ diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_stub.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_stub.cc new file mode 100644 index 00000000000..ef3d5a82793 --- /dev/null +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_stub.cc @@ -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. + +#include "ui/base/ime/win/on_screen_keyboard_display_manager_stub.h" + +namespace ui { + +// OnScreenKeyboardDisplayManagerStub member definitions. +OnScreenKeyboardDisplayManagerStub::OnScreenKeyboardDisplayManagerStub() {} + +OnScreenKeyboardDisplayManagerStub::~OnScreenKeyboardDisplayManagerStub() {} + +bool OnScreenKeyboardDisplayManagerStub::DisplayVirtualKeyboard( + OnScreenKeyboardObserver* observer) { + return false; +} + +bool OnScreenKeyboardDisplayManagerStub::DismissVirtualKeyboard() { + return false; +} + +void OnScreenKeyboardDisplayManagerStub::RemoveObserver( + OnScreenKeyboardObserver* observer) {} + +bool OnScreenKeyboardDisplayManagerStub::IsKeyboardVisible() const { + return false; +} + +} // namespace ui diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_stub.h b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_stub.h new file mode 100644 index 00000000000..184f8067a1e --- /dev/null +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_stub.h @@ -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. + +#ifndef UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_ +#define UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_ + +#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/base/ime/win/osk_display_manager.h" + +namespace ui { + +// This class provides a stub OnScreenDisplayManager. +// Used for < Win8 OS versions. +class UI_BASE_IME_EXPORT OnScreenKeyboardDisplayManagerStub + : public OnScreenKeyboardDisplayManager { + public: + ~OnScreenKeyboardDisplayManagerStub() override; + + // OnScreenKeyboardDisplayManager overrides. + bool DisplayVirtualKeyboard(OnScreenKeyboardObserver* observer) final; + bool DismissVirtualKeyboard() final; + void RemoveObserver(OnScreenKeyboardObserver* observer) final; + bool IsKeyboardVisible() const final; + + private: + friend class OnScreenKeyboardDisplayManager; + OnScreenKeyboardDisplayManagerStub(); + + DISALLOW_COPY_AND_ASSIGN(OnScreenKeyboardDisplayManagerStub); +}; + +} // namespace ui + +#endif // UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_STUB_H_ diff --git a/chromium/ui/base/win/on_screen_keyboard_display_manager_tab_tip.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc index bbce7bda6eb..f1fcd63e3c1 100644 --- a/chromium/ui/base/win/on_screen_keyboard_display_manager_tab_tip.cc +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc @@ -2,15 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/base/win/osk_display_manager.h" +#include "ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h" #include <windows.h> + #include <shellapi.h> #include <shlobj.h> #include <shobjidl.h> // Must be before propkey. #include "base/bind.h" -#include "base/debug/leak_annotations.h" #include "base/location.h" #include "base/logging.h" #include "base/single_thread_task_runner.h" @@ -20,15 +20,17 @@ #include "base/win/scoped_co_mem.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" +#include "ui/base/ime/win/osk_display_observer.h" #include "ui/base/win/hidden_window.h" -#include "ui/base/win/osk_display_observer.h" -#include "ui/display/win/dpi.h" +#include "ui/display/win/screen_win.h" #include "ui/gfx/geometry/dip_util.h" namespace { -constexpr int kCheckOSKDelayMs = 1000; -constexpr int kDismissKeyboardRetryTimeoutMs = 100; +constexpr base::TimeDelta kCheckOSKDelay = + base::TimeDelta::FromMilliseconds(1000); +constexpr base::TimeDelta kDismissKeyboardRetryTimeout = + base::TimeDelta::FromMilliseconds(100); constexpr int kDismissKeyboardMaxRetries = 5; constexpr wchar_t kOSKClassName[] = L"IPTip_Main_Window"; @@ -64,10 +66,13 @@ class OnScreenKeyboardDetector { void AddObserver(OnScreenKeyboardObserver* observer); void RemoveObserver(OnScreenKeyboardObserver* observer); - // Returns true if the osk is visible. Sets osk bounding rect if non-null - static bool IsKeyboardVisible(gfx::Rect* osk_bounding_rect); + // Returns true if the osk is visible. + static bool IsKeyboardVisible(); private: + // Returns the occluded rect in dips. + gfx::Rect GetOccludedRect(); + // Executes as a task and detects if the on screen keyboard is displayed. // Once the keyboard is displayed it schedules the HideIfNecessary() task to // detect when the keyboard is or should be hidden. @@ -80,7 +85,7 @@ class OnScreenKeyboardDetector { // Notifies observers that the keyboard was displayed. // A recurring task HideIfNecessary() is started to detect when the OSK // disappears. - void HandleKeyboardVisible(); + void HandleKeyboardVisible(const gfx::Rect& occluded_rect); // Notifies observers that the keyboard was hidden. // The observer list is cleared out after this notification. @@ -95,9 +100,6 @@ class OnScreenKeyboardDetector { // Tracks if the keyboard was displayed. bool osk_visible_notification_received_ = false; - // The keyboard dimensions in pixels. - gfx::Rect osk_rect_pixels_; - // Set to true if a call to DetectKeyboard() was made. bool keyboard_detect_requested_ = false; @@ -131,14 +133,14 @@ void OnScreenKeyboardDetector::DetectKeyboard(HWND main_window) { // a delayed task to check if the keyboard is visible because of the possible // delay between the ShellExecute call and the keyboard becoming visible. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&OnScreenKeyboardDetector::CheckIfKeyboardVisible, - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); + FROM_HERE, + base::BindOnce(&OnScreenKeyboardDetector::CheckIfKeyboardVisible, + keyboard_detector_factory_.GetWeakPtr()), + kCheckOSKDelay); } bool OnScreenKeyboardDetector::DismissKeyboard() { - // We dismiss the virtual keyboard by generating the ESC keystroke - // programmatically. + // We dismiss the virtual keyboard by generating the SC_CLOSE. HWND osk = ::FindWindow(kOSKClassName, nullptr); if (::IsWindow(osk) && ::IsWindowEnabled(osk)) { keyboard_detect_requested_ = false; @@ -146,16 +148,19 @@ bool OnScreenKeyboardDetector::DismissKeyboard() { HandleKeyboardHidden(); PostMessage(osk, WM_SYSCOMMAND, SC_CLOSE, 0); return true; - } else if (keyboard_detect_requested_) { + } + + if (keyboard_detect_requested_) { if (keyboard_dismiss_retry_count_ < kDismissKeyboardMaxRetries) { keyboard_dismiss_retry_count_++; // Please refer to the comments in the DetectKeyboard() function for more // information as to why we need a delayed task here. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(base::IgnoreResult( - &OnScreenKeyboardDetector::DismissKeyboard), - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kDismissKeyboardRetryTimeoutMs)); + FROM_HERE, + base::BindOnce( + base::IgnoreResult(&OnScreenKeyboardDetector::DismissKeyboard), + keyboard_detector_factory_.GetWeakPtr()), + kDismissKeyboardRetryTimeout); } else { keyboard_dismiss_retry_count_ = 0; } @@ -173,24 +178,41 @@ void OnScreenKeyboardDetector::RemoveObserver( } // static -bool OnScreenKeyboardDetector::IsKeyboardVisible(gfx::Rect* osk_bounding_rect) { +bool OnScreenKeyboardDetector::IsKeyboardVisible() { HWND osk = ::FindWindow(kOSKClassName, nullptr); if (!::IsWindow(osk)) return false; - if (osk_bounding_rect) { - RECT osk_rect = {}; - ::GetWindowRect(osk, &osk_rect); - *osk_bounding_rect = gfx::Rect(osk_rect); - } return ::IsWindowVisible(osk) && ::IsWindowEnabled(osk); } +gfx::Rect OnScreenKeyboardDetector::GetOccludedRect() { + gfx::Rect occluded_rect; + HWND osk = ::FindWindow(kOSKClassName, nullptr); + if (!::IsWindow(osk) || !::IsWindowVisible(osk) || !::IsWindowEnabled(osk)) + return occluded_rect; + + RECT osk_rect = {}; + RECT main_window_rect = {}; + if (!::GetWindowRect(osk, &osk_rect) || + !::GetWindowRect(main_window_, &main_window_rect)) { + return occluded_rect; + } + + gfx::Rect gfx_osk_rect(osk_rect); + gfx::Rect gfx_main_window_rect(main_window_rect); + + gfx_osk_rect.Intersect(gfx_main_window_rect); + + return display::win::ScreenWin::ScreenToDIPRect(main_window_, gfx_osk_rect); +} + void OnScreenKeyboardDetector::CheckIfKeyboardVisible() { - if (IsKeyboardVisible(&osk_rect_pixels_)) { + gfx::Rect occluded_rect = GetOccludedRect(); + if (!occluded_rect.IsEmpty()) { if (!osk_visible_notification_received_) - HandleKeyboardVisible(); + HandleKeyboardVisible(occluded_rect); } else { - DVLOG(1) << "OSK did not come up in 1 second. Something wrong."; + DVLOG(1) << "OSK did not come up. Something wrong."; } } @@ -221,30 +243,33 @@ void OnScreenKeyboardDetector::HideIfNecessary() { } } else { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&OnScreenKeyboardDetector::HideIfNecessary, - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); + FROM_HERE, + base::BindOnce(&OnScreenKeyboardDetector::HideIfNecessary, + keyboard_detector_factory_.GetWeakPtr()), + kCheckOSKDelay); } } -void OnScreenKeyboardDetector::HandleKeyboardVisible() { +void OnScreenKeyboardDetector::HandleKeyboardVisible( + const gfx::Rect& occluded_rect) { DCHECK(!osk_visible_notification_received_); osk_visible_notification_received_ = true; for (OnScreenKeyboardObserver& observer : observers_) - observer.OnKeyboardVisible(osk_rect_pixels_); + observer.OnKeyboardVisible(occluded_rect); // Now that the keyboard is visible, run the task to detect if it was hidden. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&OnScreenKeyboardDetector::HideIfNecessary, - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); + FROM_HERE, + base::BindOnce(&OnScreenKeyboardDetector::HideIfNecessary, + keyboard_detector_factory_.GetWeakPtr()), + kCheckOSKDelay); } void OnScreenKeyboardDetector::HandleKeyboardHidden() { osk_visible_notification_received_ = false; for (OnScreenKeyboardObserver& observer : observers_) - observer.OnKeyboardHidden(osk_rect_pixels_); + observer.OnKeyboardHidden(); ClearObservers(); } @@ -253,25 +278,15 @@ void OnScreenKeyboardDetector::ClearObservers() { RemoveObserver(&observer); } -// OnScreenKeyboardDisplayManager member definitions. -OnScreenKeyboardDisplayManager::OnScreenKeyboardDisplayManager() {} - -OnScreenKeyboardDisplayManager::~OnScreenKeyboardDisplayManager() {} - -OnScreenKeyboardDisplayManager* OnScreenKeyboardDisplayManager::GetInstance() { - static OnScreenKeyboardDisplayManager* instance = nullptr; - if (!instance) { - instance = new OnScreenKeyboardDisplayManager; - ANNOTATE_LEAKING_OBJECT_PTR(instance); - } - return instance; +// OnScreenKeyboardDisplayManagerTabTip member definitions. +OnScreenKeyboardDisplayManagerTabTip::OnScreenKeyboardDisplayManagerTabTip() { + DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); } -bool OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard( - OnScreenKeyboardObserver* observer) { - if (base::win::GetVersion() < base::win::VERSION_WIN8) - return false; +OnScreenKeyboardDisplayManagerTabTip::~OnScreenKeyboardDisplayManagerTabTip() {} +bool OnScreenKeyboardDisplayManagerTabTip::DisplayVirtualKeyboard( + OnScreenKeyboardObserver* observer) { if (base::win::IsKeyboardPresentOnSlate(nullptr, ui::GetHiddenWindow())) return false; @@ -287,7 +302,7 @@ bool OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard( if (success) { // If multiple calls to DisplayVirtualKeyboard occur one after the other, // the last observer would be the one to get notifications. - keyboard_detector_.reset(new OnScreenKeyboardDetector); + keyboard_detector_ = std::make_unique<OnScreenKeyboardDetector>(); if (observer) keyboard_detector_->AddObserver(observer); keyboard_detector_->DetectKeyboard(::GetForegroundWindow()); @@ -295,20 +310,18 @@ bool OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard( return success; } -bool OnScreenKeyboardDisplayManager::DismissVirtualKeyboard() { - if (base::win::GetVersion() < base::win::VERSION_WIN8) - return false; - +bool OnScreenKeyboardDisplayManagerTabTip::DismissVirtualKeyboard() { return keyboard_detector_ ? keyboard_detector_->DismissKeyboard() : false; } -void OnScreenKeyboardDisplayManager::RemoveObserver( +void OnScreenKeyboardDisplayManagerTabTip::RemoveObserver( OnScreenKeyboardObserver* observer) { if (keyboard_detector_) keyboard_detector_->RemoveObserver(observer); } -bool OnScreenKeyboardDisplayManager::GetOSKPath(base::string16* osk_path) { +bool OnScreenKeyboardDisplayManagerTabTip::GetOSKPath( + base::string16* osk_path) { DCHECK(osk_path); // We need to launch TabTip.exe from the location specified under the @@ -372,8 +385,8 @@ bool OnScreenKeyboardDisplayManager::GetOSKPath(base::string16* osk_path) { return !osk_path->empty(); } -bool OnScreenKeyboardDisplayManager::IsKeyboardVisible() const { - return OnScreenKeyboardDetector::IsKeyboardVisible(nullptr); +bool OnScreenKeyboardDisplayManagerTabTip::IsKeyboardVisible() const { + return OnScreenKeyboardDetector::IsKeyboardVisible(); } } // namespace ui diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h new file mode 100644 index 00000000000..e63bf814456 --- /dev/null +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h @@ -0,0 +1,51 @@ +// 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_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAB_TIP_H_ +#define UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAB_TIP_H_ + +#include "base/gtest_prod_util.h" +#include "base/strings/string16.h" +#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/base/ime/win/osk_display_manager.h" + +namespace ui { + +class OnScreenKeyboardDetector; + +// This class provides an implementation of the OnScreenKeyboardDisplayManager +// that uses heuristics and the TabTip.exe to display the on screen keyboard. +// Used on Windows > 7 and Windows < 10.0.10240.0 +class UI_BASE_IME_EXPORT OnScreenKeyboardDisplayManagerTabTip + : public OnScreenKeyboardDisplayManager { + public: + ~OnScreenKeyboardDisplayManagerTabTip() override; + + // OnScreenKeyboardDisplayManager overrides. + bool DisplayVirtualKeyboard(OnScreenKeyboardObserver* observer) final; + bool DismissVirtualKeyboard() final; + void RemoveObserver(OnScreenKeyboardObserver* observer) final; + bool IsKeyboardVisible() const final; + + // Returns the path of the on screen keyboard exe (TabTip.exe) in the + // |osk_path| parameter. + // Returns true on success. + bool GetOSKPath(base::string16* osk_path); + + private: + friend class OnScreenKeyboardDisplayManager; + friend class OnScreenKeyboardTest; + OnScreenKeyboardDisplayManagerTabTip(); + + std::unique_ptr<OnScreenKeyboardDetector> keyboard_detector_; + + // The location of TabTip.exe. + base::string16 osk_path_; + + DISALLOW_COPY_AND_ASSIGN(OnScreenKeyboardDisplayManagerTabTip); +}; + +} // namespace ui + +#endif // UI_BASE_IME_WIN_ON_SCREEN_KEYBOARD_DISPLAY_MANAGER_TAB_TIP_H_ diff --git a/chromium/ui/base/win/osk_display_manager_unittest.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc index f9112c846ed..16d785f01a8 100644 --- a/chromium/ui/base/win/osk_display_manager_unittest.cc +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/base/win/osk_display_manager.h" +#include "ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -12,15 +12,26 @@ #include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" +namespace ui { + +class OnScreenKeyboardTest : public ::testing::Test { + protected: + std::unique_ptr<OnScreenKeyboardDisplayManagerTabTip> + CreateOSKDisplayManager() { + return std::unique_ptr<OnScreenKeyboardDisplayManagerTabTip>( + new OnScreenKeyboardDisplayManagerTabTip()); + } +}; + // This test validates the on screen keyboard path (tabtip.exe) which is read // from the registry. -TEST(OnScreenKeyboardTest, OSKPath) { +TEST_F(OnScreenKeyboardTest, OSKPath) { // The on screen keyboard is only available on Windows 8+. if (base::win::GetVersion() < base::win::VERSION_WIN8) return; - ui::OnScreenKeyboardDisplayManager* keyboard_display_manager = - ui::OnScreenKeyboardDisplayManager::GetInstance(); + std::unique_ptr<OnScreenKeyboardDisplayManagerTabTip> + keyboard_display_manager(CreateOSKDisplayManager()); EXPECT_NE(nullptr, keyboard_display_manager); base::string16 osk_path; @@ -38,3 +49,5 @@ TEST(OnScreenKeyboardTest, OSKPath) { EXPECT_TRUE(base::PathExists(base::FilePath(osk_path))); } + +} // namespace ui diff --git a/chromium/ui/base/ime/win/osk_display_manager.cc b/chromium/ui/base/ime/win/osk_display_manager.cc new file mode 100644 index 00000000000..e7b441c0eb1 --- /dev/null +++ b/chromium/ui/base/ime/win/osk_display_manager.cc @@ -0,0 +1,31 @@ +// 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/base/ime/win/osk_display_manager.h" + +#include "base/debug/leak_annotations.h" +#include "base/win/windows_version.h" +#include "ui/base/ime/win/on_screen_keyboard_display_manager_stub.h" +#include "ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h" + +namespace ui { + +// OnScreenKeyboardDisplayManager member definitions. +OnScreenKeyboardDisplayManager::OnScreenKeyboardDisplayManager() {} + +OnScreenKeyboardDisplayManager::~OnScreenKeyboardDisplayManager() {} + +OnScreenKeyboardDisplayManager* OnScreenKeyboardDisplayManager::GetInstance() { + static OnScreenKeyboardDisplayManager* instance = nullptr; + if (!instance) { + if (base::win::GetVersion() < base::win::VERSION_WIN8) + instance = new OnScreenKeyboardDisplayManagerStub(); + else + instance = new OnScreenKeyboardDisplayManagerTabTip(); + ANNOTATE_LEAKING_OBJECT_PTR(instance); + } + return instance; +} + +} // namespace ui diff --git a/chromium/ui/base/win/osk_display_manager.h b/chromium/ui/base/ime/win/osk_display_manager.h index b5392c4b686..489558669f3 100644 --- a/chromium/ui/base/win/osk_display_manager.h +++ b/chromium/ui/base/ime/win/osk_display_manager.h @@ -2,59 +2,46 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_ -#define UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_ +#ifndef UI_BASE_IME_WIN_OSK_DISPLAY_MANAGER_H_ +#define UI_BASE_IME_WIN_OSK_DISPLAY_MANAGER_H_ #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/strings/string16.h" -#include "ui/base/ui_base_export.h" +#include "ui/base/ime/ui_base_ime_export.h" #include "ui/gfx/geometry/rect.h" namespace ui { -class OnScreenKeyboardDetector; class OnScreenKeyboardObserver; // This class provides functionality to display the on screen keyboard on // Windows 8+. It optionally notifies observers that the OSK is displayed, // hidden, etc. -class UI_BASE_EXPORT OnScreenKeyboardDisplayManager { +class UI_BASE_IME_EXPORT OnScreenKeyboardDisplayManager { public: static OnScreenKeyboardDisplayManager* GetInstance(); - ~OnScreenKeyboardDisplayManager(); + virtual ~OnScreenKeyboardDisplayManager(); // Functions to display and dismiss the keyboard. // The optional |observer| parameter allows callers to be notified when the // keyboard is displayed, dismissed, etc. - bool DisplayVirtualKeyboard(OnScreenKeyboardObserver* observer); + virtual bool DisplayVirtualKeyboard(OnScreenKeyboardObserver* observer) = 0; // When the keyboard is dismissed, the registered observer if any is removed // after notifying it. - bool DismissVirtualKeyboard(); + virtual bool DismissVirtualKeyboard() = 0; // Removes a registered observer. - void RemoveObserver(OnScreenKeyboardObserver* observer); - - // Returns the path of the on screen keyboard exe (TabTip.exe) in the - // |osk_path| parameter. - // Returns true on success. - bool GetOSKPath(base::string16* osk_path); + virtual void RemoveObserver(OnScreenKeyboardObserver* observer) = 0; // Returns true if the virtual keyboard is currently visible. - bool IsKeyboardVisible() const; + virtual bool IsKeyboardVisible() const = 0; - private: + protected: OnScreenKeyboardDisplayManager(); - - std::unique_ptr<OnScreenKeyboardDetector> keyboard_detector_; - - // The location of TabTip.exe. - base::string16 osk_path_; - - DISALLOW_COPY_AND_ASSIGN(OnScreenKeyboardDisplayManager); }; } // namespace ui -#endif // UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_ +#endif // UI_BASE_IME_WIN_OSK_DISPLAY_MANAGER_H_ diff --git a/chromium/ui/base/win/osk_display_observer.h b/chromium/ui/base/ime/win/osk_display_observer.h index 8c5d09205e9..8c83f567505 100644 --- a/chromium/ui/base/win/osk_display_observer.h +++ b/chromium/ui/base/ime/win/osk_display_observer.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_BASE_WIN_OSK_OBSERVER_H_ -#define UI_BASE_WIN_OSK_OBSERVER_H_ +#ifndef UI_BASE_IME_WIN_OSK_DISPLAY_OBSERVER_H_ +#define UI_BASE_IME_WIN_OSK_DISPLAY_OBSERVER_H_ namespace gfx { class Rect; @@ -13,16 +13,15 @@ namespace ui { // Implemented by classes who wish to get notified about the on screen keyboard // becoming visible/hidden. -class UI_BASE_EXPORT OnScreenKeyboardObserver { +class UI_BASE_IME_EXPORT OnScreenKeyboardObserver { public: virtual ~OnScreenKeyboardObserver() {} - // The |keyboard_rect| parameter contains the bounds of the keyboard in - // pixels. - virtual void OnKeyboardVisible(const gfx::Rect& keyboard_rect_in_pixels) {} - virtual void OnKeyboardHidden(const gfx::Rect& keyboard_rect_in_pixels) {} + // The |keyboard_rect| parameter contains the bounds of the keyboard in dips. + virtual void OnKeyboardVisible(const gfx::Rect& keyboard_rect) {} + virtual void OnKeyboardHidden() {} }; } // namespace ui -#endif // UI_BASE_WIN_OSK_OBSERVER_H_ +#endif // UI_BASE_IME_WIN_OSK_DISPLAY_OBSERVER_H_ diff --git a/chromium/ui/base/ime/win/tsf_bridge.cc b/chromium/ui/base/ime/win/tsf_bridge.cc new file mode 100644 index 00000000000..b5b1ba2a38d --- /dev/null +++ b/chromium/ui/base/ime/win/tsf_bridge.cc @@ -0,0 +1,542 @@ +// 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 <msctf.h> + +#include <map> + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/no_destructor.h" +#include "base/threading/thread_local_storage.h" +#include "base/win/scoped_variant.h" +#include "ui/base/ime/text_input_client.h" +#include "ui/base/ime/win/tsf_bridge.h" +#include "ui/base/ime/win/tsf_text_store.h" +#include "ui/base/ui_base_features.h" + +namespace ui { + +namespace { + +// TSFBridgeImpl ----------------------------------------------------------- + +// A TLS implementation of TSFBridge. +class TSFBridgeImpl : public TSFBridge { + public: + TSFBridgeImpl(); + ~TSFBridgeImpl() override; + + bool Initialize(); + + // TsfBridge: + void OnTextInputTypeChanged(const TextInputClient* client) override; + void OnTextLayoutChanged() override; + bool CancelComposition() override; + bool ConfirmComposition() override; + void SetFocusedClient(HWND focused_window, TextInputClient* client) override; + void RemoveFocusedClient(TextInputClient* client) override; + Microsoft::WRL::ComPtr<ITfThreadMgr> GetThreadManager() override; + TextInputClient* GetFocusedTextInputClient() const override; + + private: + // Returns true if |tsf_document_map_| is successfully initialized. This + // method should be called from and only from Initialize(). + bool InitializeDocumentMapInternal(); + + // Returns true if |context| is successfully updated to be a disabled + // context, where an IME should be deactivated. This is suitable for some + // special input context such as password fields. + bool InitializeDisabledContext(ITfContext* context); + + // Returns true if a TSF document manager and a TSF context is successfully + // created with associating with given |text_store|. The returned + // |source_cookie| indicates the binding between |text_store| and |context|. + // You can pass nullptr to |text_store| and |source_cookie| when text store is + // not necessary. + bool CreateDocumentManager(TSFTextStore* text_store, + ITfDocumentMgr** document_manager, + ITfContext** context, + DWORD* source_cookie); + + // Returns true if |document_manager| is the focused document manager. + bool IsFocused(ITfDocumentMgr* document_manager); + + // Returns true if already initialized. + bool IsInitialized(); + + // Updates or clears the association maintained in the TSF runtime between + // |attached_window_handle_| and the current document manager. Keeping this + // association updated solves some tricky event ordering issues between + // logical text input focus managed by Chrome and native text input focus + // managed by the OS. + // Background: + // TSF runtime monitors some Win32 messages such as WM_ACTIVATE to + // change the focused document manager. This is problematic when + // TSFBridge::SetFocusedClient is called first then the target window + // receives WM_ACTIVATE. This actually occurs in Aura environment where + // WM_NCACTIVATE is used as a trigger to restore text input focus. + // Caveats: + // TSF runtime does not increment the reference count of the attached + // document manager. See the comment inside the method body for + // details. + void UpdateAssociateFocus(); + void ClearAssociateFocus(); + + // A triple of document manager, text store and binding cookie between + // a context owned by the document manager and the text store. This is a + // minimum working set of an editable document in TSF. + struct TSFDocument { + public: + TSFDocument() : cookie(TF_INVALID_COOKIE) {} + TSFDocument(const TSFDocument& src) + : document_manager(src.document_manager), cookie(src.cookie) {} + Microsoft::WRL::ComPtr<ITfDocumentMgr> document_manager; + scoped_refptr<TSFTextStore> text_store; + DWORD cookie; + }; + + // Returns a pointer to TSFDocument that is associated with the current + // TextInputType of |client_|. + TSFDocument* GetAssociatedDocument(); + + // An ITfThreadMgr object to be used in focus and document management. + Microsoft::WRL::ComPtr<ITfThreadMgr> thread_manager_; + + // A map from TextInputType to an editable document for TSF. We use multiple + // TSF documents that have different InputScopes and TSF attributes based on + // the TextInputType associated with the target document. For a TextInputType + // that is not coverted by this map, a default document, e.g. the document + // for TEXT_INPUT_TYPE_TEXT, should be used. + // Note that some IMEs don't change their state unless the document focus is + // changed. This is why we use multiple documents instead of changing TSF + // metadata of a single document on the fly. + typedef std::map<TextInputType, TSFDocument> TSFDocumentMap; + TSFDocumentMap tsf_document_map_; + + // An identifier of TSF client. + TfClientId client_id_ = TF_CLIENTID_NULL; + + // Current focused text input client. Do not free |client_|. + TextInputClient* client_ = nullptr; + + // Represents the window that is currently owns text input focus. + HWND attached_window_handle_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(TSFBridgeImpl); +}; + +TSFBridgeImpl::TSFBridgeImpl() = default; + +TSFBridgeImpl::~TSFBridgeImpl() { + DCHECK(base::MessageLoopForUI::IsCurrent()); + if (!IsInitialized()) + return; + for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); + it != tsf_document_map_.end(); ++it) { + Microsoft::WRL::ComPtr<ITfContext> context; + Microsoft::WRL::ComPtr<ITfSource> source; + if (it->second.cookie != TF_INVALID_COOKIE && + SUCCEEDED( + it->second.document_manager->GetBase(context.GetAddressOf())) && + SUCCEEDED(context.CopyTo(source.GetAddressOf()))) { + source->UnadviseSink(it->second.cookie); + } + } + tsf_document_map_.clear(); + + client_id_ = TF_CLIENTID_NULL; +} + +bool TSFBridgeImpl::Initialize() { + DCHECK(base::MessageLoopForUI::IsCurrent()); + if (client_id_ != TF_CLIENTID_NULL) { + DVLOG(1) << "Already initialized."; + return false; + } + + if (FAILED(::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL, + IID_PPV_ARGS(&thread_manager_)))) { + DVLOG(1) << "Failed to create ThreadManager instance."; + return false; + } + + if (FAILED(thread_manager_->Activate(&client_id_))) { + DVLOG(1) << "Failed to activate Thread Manager."; + return false; + } + + if (!InitializeDocumentMapInternal()) + return false; + + // Japanese IME expects the default value of this compartment is + // TF_SENTENCEMODE_PHRASEPREDICT like IMM32 implementation. This value is + // managed per thread, so that it is enough to set this value at once. This + // value does not affect other language's IME behaviors. + Microsoft::WRL::ComPtr<ITfCompartmentMgr> thread_compartment_manager; + if (FAILED( + thread_manager_.CopyTo(thread_compartment_manager.GetAddressOf()))) { + DVLOG(1) << "Failed to get ITfCompartmentMgr."; + return false; + } + + Microsoft::WRL::ComPtr<ITfCompartment> sentence_compartment; + if (FAILED(thread_compartment_manager->GetCompartment( + GUID_COMPARTMENT_KEYBOARD_INPUTMODE_SENTENCE, + sentence_compartment.GetAddressOf()))) { + DVLOG(1) << "Failed to get sentence compartment."; + return false; + } + + base::win::ScopedVariant sentence_variant; + sentence_variant.Set(TF_SENTENCEMODE_PHRASEPREDICT); + if (FAILED( + sentence_compartment->SetValue(client_id_, sentence_variant.ptr()))) { + DVLOG(1) << "Failed to change the sentence mode."; + return false; + } + + return true; +} + +void TSFBridgeImpl::OnTextInputTypeChanged(const TextInputClient* client) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + DCHECK(IsInitialized()); + + if (client != client_) { + // Called from not focusing client. Do nothing. + return; + } + + UpdateAssociateFocus(); + + TSFDocument* document = GetAssociatedDocument(); + if (!document) + return; + thread_manager_->SetFocus(document->document_manager.Get()); + OnTextLayoutChanged(); +} + +void TSFBridgeImpl::OnTextLayoutChanged() { + TSFDocument* document = GetAssociatedDocument(); + if (!document) + return; + if (!document->text_store) + return; + document->text_store->SendOnLayoutChange(); +} + +bool TSFBridgeImpl::CancelComposition() { + DCHECK(base::MessageLoopForUI::IsCurrent()); + DCHECK(IsInitialized()); + + TSFDocument* document = GetAssociatedDocument(); + if (!document) + return false; + if (!document->text_store) + return false; + + return document->text_store->CancelComposition(); +} + +bool TSFBridgeImpl::ConfirmComposition() { + DCHECK(base::MessageLoopForUI::IsCurrent()); + DCHECK(IsInitialized()); + + TSFDocument* document = GetAssociatedDocument(); + if (!document) + return false; + if (!document->text_store) + return false; + + return document->text_store->ConfirmComposition(); +} + +void TSFBridgeImpl::SetFocusedClient(HWND focused_window, + TextInputClient* client) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + DCHECK(client); + DCHECK(IsInitialized()); + if (attached_window_handle_ != focused_window) + ClearAssociateFocus(); + client_ = client; + attached_window_handle_ = focused_window; + + for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); + it != tsf_document_map_.end(); ++it) { + if (it->second.text_store.get() == nullptr) + continue; + it->second.text_store->SetFocusedTextInputClient(focused_window, client); + } + + // Synchronize text input type state. + OnTextInputTypeChanged(client); +} + +void TSFBridgeImpl::RemoveFocusedClient(TextInputClient* client) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + DCHECK(IsInitialized()); + if (client_ != client) + return; + ClearAssociateFocus(); + client_ = nullptr; + attached_window_handle_ = nullptr; + for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); + it != tsf_document_map_.end(); ++it) { + if (it->second.text_store.get() == nullptr) + continue; + it->second.text_store->SetFocusedTextInputClient(nullptr, nullptr); + } +} + +TextInputClient* TSFBridgeImpl::GetFocusedTextInputClient() const { + return client_; +} + +Microsoft::WRL::ComPtr<ITfThreadMgr> TSFBridgeImpl::GetThreadManager() { + DCHECK(base::MessageLoopForUI::IsCurrent()); + DCHECK(IsInitialized()); + return thread_manager_; +} + +bool TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store, + ITfDocumentMgr** document_manager, + ITfContext** context, + DWORD* source_cookie) { + if (FAILED(thread_manager_->CreateDocumentMgr(document_manager))) { + DVLOG(1) << "Failed to create Document Manager."; + return false; + } + + DWORD edit_cookie = TF_INVALID_EDIT_COOKIE; + if (FAILED((*document_manager) + ->CreateContext(client_id_, 0, + static_cast<ITextStoreACP*>(text_store), + context, &edit_cookie))) { + DVLOG(1) << "Failed to create Context."; + return false; + } + + if (FAILED((*document_manager)->Push(*context))) { + DVLOG(1) << "Failed to push context."; + return false; + } + + if (!text_store || !source_cookie) + return true; + + Microsoft::WRL::ComPtr<ITfSource> source; + if (FAILED((*context)->QueryInterface(IID_PPV_ARGS(&source)))) { + DVLOG(1) << "Failed to get source."; + return false; + } + + if (FAILED(source->AdviseSink(IID_ITfTextEditSink, + static_cast<ITfTextEditSink*>(text_store), + source_cookie))) { + DVLOG(1) << "AdviseSink failed."; + return false; + } + + if (*source_cookie == TF_INVALID_COOKIE) { + DVLOG(1) << "The result of cookie is invalid."; + return false; + } + return true; +} + +bool TSFBridgeImpl::InitializeDocumentMapInternal() { + const TextInputType kTextInputTypes[] = { + TEXT_INPUT_TYPE_NONE, TEXT_INPUT_TYPE_TEXT, + TEXT_INPUT_TYPE_PASSWORD, TEXT_INPUT_TYPE_SEARCH, + TEXT_INPUT_TYPE_EMAIL, TEXT_INPUT_TYPE_NUMBER, + TEXT_INPUT_TYPE_TELEPHONE, TEXT_INPUT_TYPE_URL, + }; + for (size_t i = 0; i < arraysize(kTextInputTypes); ++i) { + const TextInputType input_type = kTextInputTypes[i]; + Microsoft::WRL::ComPtr<ITfContext> context; + Microsoft::WRL::ComPtr<ITfDocumentMgr> document_manager; + DWORD cookie = TF_INVALID_COOKIE; + const bool use_null_text_store = (input_type == TEXT_INPUT_TYPE_NONE); + DWORD* cookie_ptr = use_null_text_store ? nullptr : &cookie; + scoped_refptr<TSFTextStore> text_store = + use_null_text_store ? nullptr : new TSFTextStore(); + if (!CreateDocumentManager(text_store.get(), + document_manager.GetAddressOf(), + context.GetAddressOf(), cookie_ptr)) + return false; + const bool use_disabled_context = (input_type == TEXT_INPUT_TYPE_PASSWORD || + input_type == TEXT_INPUT_TYPE_NONE); + if (use_disabled_context && !InitializeDisabledContext(context.Get())) + return false; + tsf_document_map_[input_type].text_store = text_store; + tsf_document_map_[input_type].document_manager = document_manager; + tsf_document_map_[input_type].cookie = cookie; + } + return true; +} + +bool TSFBridgeImpl::InitializeDisabledContext(ITfContext* context) { + Microsoft::WRL::ComPtr<ITfCompartmentMgr> compartment_mgr; + if (FAILED(context->QueryInterface(IID_PPV_ARGS(&compartment_mgr)))) { + DVLOG(1) << "Failed to get CompartmentMgr."; + return false; + } + + Microsoft::WRL::ComPtr<ITfCompartment> disabled_compartment; + if (FAILED(compartment_mgr->GetCompartment( + GUID_COMPARTMENT_KEYBOARD_DISABLED, + disabled_compartment.GetAddressOf()))) { + DVLOG(1) << "Failed to get keyboard disabled compartment."; + return false; + } + + base::win::ScopedVariant variant; + variant.Set(1); + if (FAILED(disabled_compartment->SetValue(client_id_, variant.ptr()))) { + DVLOG(1) << "Failed to disable the DocumentMgr."; + return false; + } + + Microsoft::WRL::ComPtr<ITfCompartment> empty_context; + if (FAILED(compartment_mgr->GetCompartment(GUID_COMPARTMENT_EMPTYCONTEXT, + empty_context.GetAddressOf()))) { + DVLOG(1) << "Failed to get empty context compartment."; + return false; + } + base::win::ScopedVariant empty_context_variant; + empty_context_variant.Set(static_cast<int32_t>(1)); + if (FAILED( + empty_context->SetValue(client_id_, empty_context_variant.ptr()))) { + DVLOG(1) << "Failed to set empty context."; + return false; + } + + return true; +} + +bool TSFBridgeImpl::IsFocused(ITfDocumentMgr* document_manager) { + Microsoft::WRL::ComPtr<ITfDocumentMgr> focused_document_manager; + if (FAILED( + thread_manager_->GetFocus(focused_document_manager.GetAddressOf()))) + return false; + return focused_document_manager.Get() == document_manager; +} + +bool TSFBridgeImpl::IsInitialized() { + return client_id_ != TF_CLIENTID_NULL; +} + +void TSFBridgeImpl::UpdateAssociateFocus() { + if (attached_window_handle_ == nullptr) + return; + TSFDocument* document = GetAssociatedDocument(); + if (document == nullptr) { + ClearAssociateFocus(); + return; + } + // NOTE: ITfThreadMgr::AssociateFocus does not increment the ref count of + // the document manager to be attached. It is our responsibility to make sure + // the attached document manager will not be destroyed while it is attached. + // This should be true as long as TSFBridge::Shutdown() is called late phase + // of UI thread shutdown. + Microsoft::WRL::ComPtr<ITfDocumentMgr> previous_focus; + thread_manager_->AssociateFocus(attached_window_handle_, + document->document_manager.Get(), + previous_focus.GetAddressOf()); +} + +void TSFBridgeImpl::ClearAssociateFocus() { + if (attached_window_handle_ == nullptr) + return; + Microsoft::WRL::ComPtr<ITfDocumentMgr> previous_focus; + thread_manager_->AssociateFocus(attached_window_handle_, nullptr, + previous_focus.GetAddressOf()); +} + +TSFBridgeImpl::TSFDocument* TSFBridgeImpl::GetAssociatedDocument() { + if (!client_) + return nullptr; + TSFDocumentMap::iterator it = + tsf_document_map_.find(client_->GetTextInputType()); + if (it == tsf_document_map_.end()) { + it = tsf_document_map_.find(TEXT_INPUT_TYPE_TEXT); + // This check is necessary because it's possible that we failed to + // initialize |tsf_document_map_| and it has no TEXT_INPUT_TYPE_TEXT. + if (it == tsf_document_map_.end()) + return nullptr; + } + return &it->second; +} + +void Finalize(void* data) { + TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(data); + delete delegate; +} + +base::ThreadLocalStorage::Slot& TSFBridgeTLS() { + static base::NoDestructor<base::ThreadLocalStorage::Slot> tsf_bridge_tls( + &Finalize); + return *tsf_bridge_tls; +} + +} // namespace + +// TsfBridge ----------------------------------------------------------------- + +TSFBridge::TSFBridge() {} + +TSFBridge::~TSFBridge() {} + +// static +void TSFBridge::Initialize() { + if (!base::MessageLoopForUI::IsCurrent()) { + DVLOG(1) << "Do not use TSFBridge without UI thread."; + return; + } + TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get()); + if (delegate) + return; + // If we aren't supporting TSF early out. + if (!base::FeatureList::IsEnabled(features::kTSFImeSupport)) + return; + delegate = new TSFBridgeImpl(); + TSFBridgeTLS().Set(delegate); + delegate->Initialize(); +} + +// static +TSFBridge* TSFBridge::ReplaceForTesting(TSFBridge* bridge) { + if (!base::MessageLoopForUI::IsCurrent()) { + DVLOG(1) << "Do not use TSFBridge without UI thread."; + return nullptr; + } + TSFBridge* old_bridge = TSFBridge::GetInstance(); + TSFBridgeTLS().Set(bridge); + return old_bridge; +} + +// static +void TSFBridge::Shutdown() { + if (!base::MessageLoopForUI::IsCurrent()) { + DVLOG(1) << "Do not use TSFBridge without UI thread."; + } + TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get()); + TSFBridgeTLS().Set(nullptr); + delete delegate; +} + +// static +TSFBridge* TSFBridge::GetInstance() { + if (!base::MessageLoopForUI::IsCurrent()) { + DVLOG(1) << "Do not use TSFBridge without UI thread."; + return nullptr; + } + TSFBridgeImpl* delegate = static_cast<TSFBridgeImpl*>(TSFBridgeTLS().Get()); + DCHECK(delegate) << "Do no call GetInstance before TSFBridge::Initialize."; + return delegate; +} + +} // namespace ui diff --git a/chromium/ui/base/ime/win/tsf_bridge.h b/chromium/ui/base/ime/win/tsf_bridge.h new file mode 100644 index 00000000000..0d24edd6fac --- /dev/null +++ b/chromium/ui/base/ime/win/tsf_bridge.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_BASE_IME_WIN_TSF_BRIDGE_H_ +#define UI_BASE_IME_WIN_TSF_BRIDGE_H_ + +#include <msctf.h> +#include <windows.h> +#include <wrl/client.h> + +#include "base/macros.h" +#include "ui/base/ime/ui_base_ime_export.h" + +namespace ui { +class TextInputClient; + +// TSFBridge provides high level IME related operations on top of Text Services +// Framework (TSF). TSFBridge is managed by TLS because TSF related stuff is +// associated with each thread and not allowed to access across thread boundary. +// To be consistent with IMM32 behavior, TSFBridge is shared in the same thread. +// TSFBridge is used by the web content text inputting field, for example +// DisableIME() should be called if a password field is focused. +// +// TSFBridge also manages connectivity between TSFTextStore which is the backend +// of text inputting and current focused TextInputClient. +// +// All methods in this class must be used in UI thread. +class UI_BASE_IME_EXPORT TSFBridge { + public: + virtual ~TSFBridge(); + + // Returns the thread local TSFBridge instance. Initialize() must be called + // first. Do not cache this pointer and use it after TSFBridge Shutdown(). + static TSFBridge* GetInstance(); + + // Sets the thread local instance. Must be called before any calls to + // GetInstance(). + static void Initialize(); + + // Injects an alternative TSFBridge such as MockTSFBridge for testing. The + // injected object should be released by the caller. This function returns + // previous TSFBridge pointer with ownership. + static TSFBridge* ReplaceForTesting(TSFBridge* bridge); + + // Destroys the thread local instance. + static void Shutdown(); + + // Handles TextInputTypeChanged event. RWHVW is responsible for calling this + // handler whenever renderer's input text type is changed. Does nothing + // unless |client| is focused. + virtual void OnTextInputTypeChanged(const TextInputClient* client) = 0; + + // Sends an event to TSF manager that the text layout should be updated. + virtual void OnTextLayoutChanged() = 0; + + // Cancels the ongoing composition if exists. + // Returns true if there is no composition. + // Returns false if an edit session is on-going. + // Returns false if an error occures. + virtual bool CancelComposition() = 0; + + // Confirms the ongoing composition if exists. + // Returns true if there is no composition. + // Returns false if an edit session is on-going. + // Returns false if an error occures. + virtual bool ConfirmComposition() = 0; + + // Sets currently focused TextInputClient. + // Caller must free |client|. + virtual void SetFocusedClient(HWND focused_window, + TextInputClient* client) = 0; + + // Removes currently focused TextInputClient. + // Caller must free |client|. + virtual void RemoveFocusedClient(TextInputClient* client) = 0; + + // Obtains current thread manager. + virtual Microsoft::WRL::ComPtr<ITfThreadMgr> GetThreadManager() = 0; + + // Returns the focused text input client. + virtual TextInputClient* GetFocusedTextInputClient() const = 0; + + protected: + // Uses GetInstance() instead. + TSFBridge(); + + private: + DISALLOW_COPY_AND_ASSIGN(TSFBridge); +}; + +} // namespace ui + +#endif // UI_BASE_IME_WIN_TSF_BRIDGE_H_ diff --git a/chromium/ui/base/ime/win/tsf_event_router.cc b/chromium/ui/base/ime/win/tsf_event_router.cc new file mode 100644 index 00000000000..a67ad224f7c --- /dev/null +++ b/chromium/ui/base/ime/win/tsf_event_router.cc @@ -0,0 +1,296 @@ +// 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/win/tsf_event_router.h" + +#include <msctf.h> +#include <wrl/client.h> +#include <set> +#include <utility> + +#include "base/bind.h" +#include "ui/base/win/atl_module.h" +#include "ui/gfx/range/range.h" + +namespace ui { + +// TSFEventRouter::Delegate ------------------------------------ + +// The implementation class of ITfUIElementSink, whose member functions will be +// called back by TSF when the UI element status is changed, for example when +// the candidate window is opened or closed. This class also implements +// ITfTextEditSink, whose member function is called back by TSF when the text +// editting session is finished. +class ATL_NO_VTABLE TSFEventRouter::Delegate + : public ATL::CComObjectRootEx<CComSingleThreadModel>, + public ITfUIElementSink, + public ITfTextEditSink { + public: + BEGIN_COM_MAP(Delegate) + COM_INTERFACE_ENTRY(ITfUIElementSink) + COM_INTERFACE_ENTRY(ITfTextEditSink) + END_COM_MAP() + + Delegate(); + ~Delegate(); + + // ITfTextEditSink: + STDMETHOD(OnEndEdit) + (ITfContext* context, + TfEditCookie read_only_cookie, + ITfEditRecord* edit_record) override; + + // ITfUiElementSink: + STDMETHOD(BeginUIElement)(DWORD element_id, BOOL* is_show) override; + STDMETHOD(UpdateUIElement)(DWORD element_id) override; + STDMETHOD(EndUIElement)(DWORD element_id) override; + + // Sets |thread_manager| to be monitored. |thread_manager| can be nullptr. + void SetManager(ITfThreadMgr* thread_manager); + + // Returns true if the IME is composing text. + bool IsImeComposing(); + + // Sets |router| to be forwarded TSF-related events. + void SetRouter(TSFEventRouter* router); + + private: + // Returns current composition range. Returns gfx::Range::InvalidRange if + // there is no composition. + static gfx::Range GetCompositionRange(ITfContext* context); + + // Returns true if the given |element_id| represents the candidate window. + bool IsCandidateWindowInternal(DWORD element_id); + + // A context associated with this class. + Microsoft::WRL::ComPtr<ITfContext> context_; + + // The ITfSource associated with |context_|. + Microsoft::WRL::ComPtr<ITfSource> context_source_; + + // The cookie for |context_source_|. + DWORD context_source_cookie_; + + // A UIElementMgr associated with this class. + Microsoft::WRL::ComPtr<ITfUIElementMgr> ui_element_manager_; + + // The ITfSouce associated with |ui_element_manager_|. + Microsoft::WRL::ComPtr<ITfSource> ui_source_; + + // The set of currently opened candidate window ids. + std::set<DWORD> open_candidate_window_ids_; + + // The cookie for |ui_source_|. + DWORD ui_source_cookie_ = TF_INVALID_COOKIE; + + TSFEventRouter* router_ = nullptr; + gfx::Range previous_composition_range_; + + DISALLOW_COPY_AND_ASSIGN(Delegate); +}; + +TSFEventRouter::Delegate::Delegate() + : previous_composition_range_(gfx::Range::InvalidRange()) {} + +TSFEventRouter::Delegate::~Delegate() = default; + +void TSFEventRouter::Delegate::SetRouter(TSFEventRouter* router) { + router_ = router; +} + +STDMETHODIMP TSFEventRouter::Delegate::OnEndEdit(ITfContext* context, + TfEditCookie read_only_cookie, + ITfEditRecord* edit_record) { + if (!edit_record || !context) + return E_INVALIDARG; + if (!router_) + return S_OK; + + // |edit_record| can be used to obtain updated ranges in terms of text + // contents and/or text attributes. Here we are interested only in text update + // so we use TF_GTP_INCL_TEXT and check if there is any range which contains + // updated text. + Microsoft::WRL::ComPtr<IEnumTfRanges> ranges; + if (FAILED(edit_record->GetTextAndPropertyUpdates(TF_GTP_INCL_TEXT, nullptr, + 0, ranges.GetAddressOf()))) + return S_OK; // Don't care about failures. + + ULONG fetched_count = 0; + Microsoft::WRL::ComPtr<ITfRange> range; + if (FAILED(ranges->Next(1, range.GetAddressOf(), &fetched_count))) + return S_OK; // Don't care about failures. + + const gfx::Range composition_range = GetCompositionRange(context); + + if (!previous_composition_range_.IsValid() && composition_range.IsValid()) + router_->OnTSFStartComposition(); + + // |fetched_count| != 0 means there is at least one range that contains + // updated text. + if (fetched_count != 0) + router_->OnTextUpdated(composition_range); + + if (previous_composition_range_.IsValid() && !composition_range.IsValid()) + router_->OnTSFEndComposition(); + + previous_composition_range_ = composition_range; + return S_OK; +} + +STDMETHODIMP TSFEventRouter::Delegate::BeginUIElement(DWORD element_id, + BOOL* is_show) { + if (is_show) + *is_show = TRUE; // Without this the UI element will not be shown. + + if (!IsCandidateWindowInternal(element_id)) + return S_OK; + + std::pair<std::set<DWORD>::iterator, bool> insert_result = + open_candidate_window_ids_.insert(element_id); + // Don't call if |router_| is null or |element_id| is already handled. + if (router_ && insert_result.second) + router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); + + return S_OK; +} + +STDMETHODIMP TSFEventRouter::Delegate::UpdateUIElement(DWORD element_id) { + return S_OK; +} + +STDMETHODIMP TSFEventRouter::Delegate::EndUIElement(DWORD element_id) { + if ((open_candidate_window_ids_.erase(element_id) != 0) && router_) + router_->OnCandidateWindowCountChanged(open_candidate_window_ids_.size()); + return S_OK; +} + +void TSFEventRouter::Delegate::SetManager(ITfThreadMgr* thread_manager) { + context_.Reset(); + + if (context_source_) { + context_source_->UnadviseSink(context_source_cookie_); + context_source_.Reset(); + } + context_source_cookie_ = TF_INVALID_COOKIE; + + ui_element_manager_.Reset(); + if (ui_source_) { + ui_source_->UnadviseSink(ui_source_cookie_); + ui_source_.Reset(); + } + ui_source_cookie_ = TF_INVALID_COOKIE; + + if (!thread_manager) + return; + + Microsoft::WRL::ComPtr<ITfDocumentMgr> document_manager; + if (FAILED(thread_manager->GetFocus(document_manager.GetAddressOf())) || + !document_manager.Get() || + FAILED(document_manager->GetBase(context_.GetAddressOf())) || + FAILED(context_.CopyTo(context_source_.GetAddressOf()))) + return; + context_source_->AdviseSink(IID_ITfTextEditSink, + static_cast<ITfTextEditSink*>(this), + &context_source_cookie_); + + if (FAILED( + thread_manager->QueryInterface(IID_PPV_ARGS(&ui_element_manager_))) || + FAILED(ui_element_manager_.CopyTo(ui_source_.GetAddressOf()))) + return; + ui_source_->AdviseSink(IID_ITfUIElementSink, + static_cast<ITfUIElementSink*>(this), + &ui_source_cookie_); +} + +bool TSFEventRouter::Delegate::IsImeComposing() { + return context_ && GetCompositionRange(context_.Get()).IsValid(); +} + +// static +gfx::Range TSFEventRouter::Delegate::GetCompositionRange(ITfContext* context) { + DCHECK(context); + Microsoft::WRL::ComPtr<ITfContextComposition> context_composition; + if (FAILED(context->QueryInterface(IID_PPV_ARGS(&context_composition)))) + return gfx::Range::InvalidRange(); + Microsoft::WRL::ComPtr<IEnumITfCompositionView> enum_composition_view; + if (FAILED(context_composition->EnumCompositions( + enum_composition_view.GetAddressOf()))) + return gfx::Range::InvalidRange(); + Microsoft::WRL::ComPtr<ITfCompositionView> composition_view; + if (enum_composition_view->Next(1, composition_view.GetAddressOf(), + nullptr) != S_OK) + return gfx::Range::InvalidRange(); + + Microsoft::WRL::ComPtr<ITfRange> range; + if (FAILED(composition_view->GetRange(range.GetAddressOf()))) + return gfx::Range::InvalidRange(); + + Microsoft::WRL::ComPtr<ITfRangeACP> range_acp; + if (FAILED(range.CopyTo(range_acp.GetAddressOf()))) + return gfx::Range::InvalidRange(); + + LONG start = 0; + LONG length = 0; + if (FAILED(range_acp->GetExtent(&start, &length))) + return gfx::Range::InvalidRange(); + + return gfx::Range(start, start + length); +} + +bool TSFEventRouter::Delegate::IsCandidateWindowInternal(DWORD element_id) { + DCHECK(ui_element_manager_.Get()); + Microsoft::WRL::ComPtr<ITfUIElement> ui_element; + if (FAILED(ui_element_manager_->GetUIElement(element_id, + ui_element.GetAddressOf()))) + return false; + Microsoft::WRL::ComPtr<ITfCandidateListUIElement> candidate_list_ui_element; + return SUCCEEDED(ui_element.CopyTo(candidate_list_ui_element.GetAddressOf())); +} + +// TSFEventRouter ------------------------------------------------------------ + +TSFEventRouter::TSFEventRouter(TSFEventRouterObserver* observer) + : observer_(observer) { + DCHECK(observer_); + CComObject<Delegate>* delegate; + ui::win::CreateATLModuleIfNeeded(); + if (SUCCEEDED(CComObject<Delegate>::CreateInstance(&delegate))) { + delegate->AddRef(); + delegate_.Attach(delegate); + delegate_->SetRouter(this); + } +} + +TSFEventRouter::~TSFEventRouter() { + if (delegate_) { + delegate_->SetManager(nullptr); + delegate_->SetRouter(nullptr); + } +} + +bool TSFEventRouter::IsImeComposing() { + return delegate_->IsImeComposing(); +} + +void TSFEventRouter::OnCandidateWindowCountChanged(size_t window_count) { + observer_->OnCandidateWindowCountChanged(window_count); +} + +void TSFEventRouter::OnTSFStartComposition() { + observer_->OnTSFStartComposition(); +} + +void TSFEventRouter::OnTextUpdated(const gfx::Range& composition_range) { + observer_->OnTextUpdated(composition_range); +} + +void TSFEventRouter::OnTSFEndComposition() { + observer_->OnTSFEndComposition(); +} + +void TSFEventRouter::SetManager(ITfThreadMgr* thread_manager) { + delegate_->SetManager(thread_manager); +} + +} // namespace ui diff --git a/chromium/ui/base/ime/win/tsf_event_router.h b/chromium/ui/base/ime/win/tsf_event_router.h new file mode 100644 index 00000000000..d094e1ba9b1 --- /dev/null +++ b/chromium/ui/base/ime/win/tsf_event_router.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_IME_WIN_TSF_EVENT_ROUTER_H_ +#define UI_BASE_IME_WIN_TSF_EVENT_ROUTER_H_ + +#include <atlbase.h> +#include <atlcom.h> +#include <msctf.h> + +#include <set> + +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "ui/base/ime/text_input_type.h" +#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/gfx/range/range.h" + +namespace ui { + +class TSFEventRouterObserver { + public: + TSFEventRouterObserver() {} + + // Called when the number of currently opened candidate windows changes. + virtual void OnCandidateWindowCountChanged(size_t window_count) {} + + // Called when a composition is started. + virtual void OnTSFStartComposition() {} + + // Called when the text contents are updated. If there is no composition, + // gfx::Range::InvalidRange is passed to |composition_range|. + virtual void OnTextUpdated(const gfx::Range& composition_range) {} + + // Called when a composition is terminated. + virtual void OnTSFEndComposition() {} + + protected: + virtual ~TSFEventRouterObserver() {} + + private: + DISALLOW_COPY_AND_ASSIGN(TSFEventRouterObserver); +}; + +// This class monitors TSF related events and forwards them to given +// |observer|. +class UI_BASE_IME_EXPORT TSFEventRouter { + public: + // Do not pass NULL to |observer|. + explicit TSFEventRouter(TSFEventRouterObserver* observer); + virtual ~TSFEventRouter(); + + // Returns true if the IME is composing text. + bool IsImeComposing(); + + // Callbacks from the TSFEventRouterDelegate: + void OnCandidateWindowCountChanged(size_t window_count); + void OnTSFStartComposition(); + void OnTextUpdated(const gfx::Range& composition_range); + void OnTSFEndComposition(); + + // Sets |thread_manager| to be monitored. |thread_manager| can be NULL. + void SetManager(ITfThreadMgr* thread_manager); + + private: + class Delegate; + + CComPtr<Delegate> delegate_; + + TSFEventRouterObserver* observer_; + + DISALLOW_COPY_AND_ASSIGN(TSFEventRouter); +}; + +} // namespace ui + +#endif // UI_BASE_IME_WIN_TSF_EVENT_ROUTER_H_ diff --git a/chromium/ui/base/ime/win/tsf_text_store.cc b/chromium/ui/base/ime/win/tsf_text_store.cc new file mode 100644 index 00000000000..6fb766ded9e --- /dev/null +++ b/chromium/ui/base/ime/win/tsf_text_store.cc @@ -0,0 +1,925 @@ +// 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. + +#define INITGUID // required for GUID_PROP_INPUTSCOPE +#include "ui/base/ime/win/tsf_text_store.h" + +#include <InputScope.h> +#include <OleCtl.h> +#include <wrl/client.h> + +#include <algorithm> + +#include "base/win/scoped_variant.h" +#include "ui/base/ime/text_input_client.h" +#include "ui/base/ime/win/tsf_input_scope.h" +#include "ui/gfx/geometry/rect.h" + +namespace ui { +namespace { + +// We support only one view. +const TsViewCookie kViewCookie = 1; + +} // namespace + +TSFTextStore::TSFTextStore() { + if (FAILED(::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, CLSCTX_ALL, + IID_PPV_ARGS(&category_manager_)))) { + LOG(FATAL) << "Failed to initialize CategoryMgr."; + return; + } + if (FAILED(::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr, + CLSCTX_ALL, + IID_PPV_ARGS(&display_attribute_manager_)))) { + LOG(FATAL) << "Failed to initialize DisplayAttributeMgr."; + return; + } +} + +TSFTextStore::~TSFTextStore() {} + +ULONG STDMETHODCALLTYPE TSFTextStore::AddRef() { + return InterlockedIncrement(&ref_count_); +} + +ULONG STDMETHODCALLTYPE TSFTextStore::Release() { + const LONG count = InterlockedDecrement(&ref_count_); + if (!count) { + delete this; + return 0; + } + return static_cast<ULONG>(count); +} + +STDMETHODIMP TSFTextStore::QueryInterface(REFIID iid, void** result) { + if (iid == IID_IUnknown || iid == IID_ITextStoreACP) { + *result = static_cast<ITextStoreACP*>(this); + } else if (iid == IID_ITfContextOwnerCompositionSink) { + *result = static_cast<ITfContextOwnerCompositionSink*>(this); + } else if (iid == IID_ITfTextEditSink) { + *result = static_cast<ITfTextEditSink*>(this); + } else { + *result = nullptr; + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +STDMETHODIMP TSFTextStore::AdviseSink(REFIID iid, + IUnknown* unknown, + DWORD mask) { + if (!IsEqualGUID(iid, IID_ITextStoreACPSink)) + return E_INVALIDARG; + if (text_store_acp_sink_) { + if (text_store_acp_sink_.Get() == unknown) { + text_store_acp_sink_mask_ = mask; + return S_OK; + } else { + return CONNECT_E_ADVISELIMIT; + } + } + if (FAILED(unknown->QueryInterface(IID_PPV_ARGS(&text_store_acp_sink_)))) + return E_UNEXPECTED; + text_store_acp_sink_mask_ = mask; + + return S_OK; +} + +STDMETHODIMP TSFTextStore::FindNextAttrTransition( + LONG acp_start, + LONG acp_halt, + ULONG num_filter_attributes, + const TS_ATTRID* filter_attributes, + DWORD flags, + LONG* acp_next, + BOOL* found, + LONG* found_offset) { + if (!acp_next || !found || !found_offset) + return E_INVALIDARG; + // We don't support any attributes. + // So we always return "not found". + *acp_next = 0; + *found = FALSE; + *found_offset = 0; + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetACPFromPoint(TsViewCookie view_cookie, + const POINT* point, + DWORD flags, + LONG* acp) { + NOTIMPLEMENTED(); + if (view_cookie != kViewCookie) + return E_INVALIDARG; + return E_NOTIMPL; +} + +STDMETHODIMP TSFTextStore::GetActiveView(TsViewCookie* view_cookie) { + if (!view_cookie) + return E_INVALIDARG; + // We support only one view. + *view_cookie = kViewCookie; + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetEmbedded(LONG acp_pos, + REFGUID service, + REFIID iid, + IUnknown** unknown) { + // We don't support any embedded objects. + NOTIMPLEMENTED(); + if (!unknown) + return E_INVALIDARG; + *unknown = nullptr; + return E_NOTIMPL; +} + +STDMETHODIMP TSFTextStore::GetEndACP(LONG* acp) { + if (!acp) + return E_INVALIDARG; + if (!HasReadLock()) + return TS_E_NOLOCK; + *acp = string_buffer_.size(); + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetFormattedText(LONG acp_start, + LONG acp_end, + IDataObject** data_object) { + NOTIMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP TSFTextStore::GetScreenExt(TsViewCookie view_cookie, RECT* rect) { + if (view_cookie != kViewCookie) + return E_INVALIDARG; + if (!rect) + return E_INVALIDARG; + + // {0, 0, 0, 0} means that the document rect is not currently displayed. + SetRect(rect, 0, 0, 0, 0); + + if (!IsWindow(window_handle_)) + return E_FAIL; + + // Currently ui::TextInputClient does not expose the document rect. So use + // the Win32 client rectangle instead. + // TODO(yukawa): Upgrade TextInputClient so that the client can retrieve the + // document rectangle. + RECT client_rect = {}; + if (!GetClientRect(window_handle_, &client_rect)) + return E_FAIL; + POINT left_top = {client_rect.left, client_rect.top}; + POINT right_bottom = {client_rect.right, client_rect.bottom}; + if (!ClientToScreen(window_handle_, &left_top)) + return E_FAIL; + if (!ClientToScreen(window_handle_, &right_bottom)) + return E_FAIL; + + rect->left = left_top.x; + rect->top = left_top.y; + rect->right = right_bottom.x; + rect->bottom = right_bottom.y; + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetSelection(ULONG selection_index, + ULONG selection_buffer_size, + TS_SELECTION_ACP* selection_buffer, + ULONG* fetched_count) { + if (!selection_buffer) + return E_INVALIDARG; + if (!fetched_count) + return E_INVALIDARG; + if (!HasReadLock()) + return TS_E_NOLOCK; + *fetched_count = 0; + if ((selection_buffer_size > 0) && + ((selection_index == 0) || (selection_index == TS_DEFAULT_SELECTION))) { + selection_buffer[0].acpStart = selection_.start(); + selection_buffer[0].acpEnd = selection_.end(); + selection_buffer[0].style.ase = TS_AE_END; + selection_buffer[0].style.fInterimChar = FALSE; + *fetched_count = 1; + } + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetStatus(TS_STATUS* status) { + if (!status) + return E_INVALIDARG; + + status->dwDynamicFlags = 0; + // We use transitory contexts and we don't support hidden text. + // TODO(dtapuska): Remove TS_SS_TRANSITORY it was added to fix + // https://crbug.com/148355 + status->dwStaticFlags = TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT; + + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetText(LONG acp_start, + LONG acp_end, + wchar_t* text_buffer, + ULONG text_buffer_size, + ULONG* text_buffer_copied, + TS_RUNINFO* run_info_buffer, + ULONG run_info_buffer_size, + ULONG* run_info_buffer_copied, + LONG* next_acp) { + if (!text_buffer_copied || !run_info_buffer_copied) + return E_INVALIDARG; + if (!text_buffer && text_buffer_size != 0) + return E_INVALIDARG; + if (!run_info_buffer && run_info_buffer_size != 0) + return E_INVALIDARG; + if (!next_acp) + return E_INVALIDARG; + if (!HasReadLock()) + return TF_E_NOLOCK; + const LONG string_buffer_size = string_buffer_.size(); + if (acp_end == -1) + acp_end = string_buffer_size; + if (!((0 <= acp_start) && (acp_start <= acp_end) && + (acp_end <= string_buffer_size))) { + return TF_E_INVALIDPOS; + } + acp_end = std::min(acp_end, acp_start + static_cast<LONG>(text_buffer_size)); + *text_buffer_copied = acp_end - acp_start; + + const base::string16& result = + string_buffer_.substr(acp_start, *text_buffer_copied); + for (size_t i = 0; i < result.size(); ++i) { + text_buffer[i] = result[i]; + } + + if (run_info_buffer_size) { + run_info_buffer[0].uCount = *text_buffer_copied; + run_info_buffer[0].type = TS_RT_PLAIN; + *run_info_buffer_copied = 1; + } + + *next_acp = acp_end; + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetTextExt(TsViewCookie view_cookie, + LONG acp_start, + LONG acp_end, + RECT* rect, + BOOL* clipped) { + if (!rect || !clipped) + return E_INVALIDARG; + if (!text_input_client_) + return E_UNEXPECTED; + if (view_cookie != kViewCookie) + return E_INVALIDARG; + if (!HasReadLock()) + return TS_E_NOLOCK; + if (!((static_cast<LONG>(committed_size_) <= acp_start) && + (acp_start <= acp_end) && + (acp_end <= static_cast<LONG>(string_buffer_.size())))) { + return TS_E_INVALIDPOS; + } + + // According to a behavior of notepad.exe and wordpad.exe, top left corner of + // rect indicates a first character's one, and bottom right corner of rect + // indicates a last character's one. + // We use RECT instead of gfx::Rect since left position may be bigger than + // right position when composition has multiple lines. + RECT result; + gfx::Rect tmp_rect; + const uint32_t start_pos = acp_start - committed_size_; + const uint32_t end_pos = acp_end - committed_size_; + + if (start_pos == end_pos) { + // According to MSDN document, if |acp_start| and |acp_end| are equal it is + // OK to just return E_INVALIDARG. + // http://msdn.microsoft.com/en-us/library/ms538435 + // But when using Pinin IME of Windows 8, this method is called with the + // equal values of |acp_start| and |acp_end|. So we handle this condition. + if (start_pos == 0) { + if (text_input_client_->GetCompositionCharacterBounds(0, &tmp_rect)) { + tmp_rect.set_width(0); + result = tmp_rect.ToRECT(); + } else if (string_buffer_.size() == committed_size_) { + result = text_input_client_->GetCaretBounds().ToRECT(); + } else { + return TS_E_NOLAYOUT; + } + } else if (text_input_client_->GetCompositionCharacterBounds(start_pos - 1, + &tmp_rect)) { + result.left = tmp_rect.right(); + result.right = tmp_rect.right(); + result.top = tmp_rect.y(); + result.bottom = tmp_rect.bottom(); + } else { + return TS_E_NOLAYOUT; + } + } else { + if (text_input_client_->GetCompositionCharacterBounds(start_pos, + &tmp_rect)) { + result.left = tmp_rect.x(); + result.top = tmp_rect.y(); + result.right = tmp_rect.right(); + result.bottom = tmp_rect.bottom(); + if (text_input_client_->GetCompositionCharacterBounds(end_pos - 1, + &tmp_rect)) { + result.right = tmp_rect.right(); + result.bottom = tmp_rect.bottom(); + } else { + // We may not be able to get the last character bounds, so we use the + // first character bounds instead of returning TS_E_NOLAYOUT. + } + } else { + // Hack for PPAPI flash. PPAPI flash does not support GetCaretBounds, so + // it's better to return previous caret rectangle instead. + // TODO(nona, kinaba): Remove this hack. + if (start_pos == 0) { + result = text_input_client_->GetCaretBounds().ToRECT(); + } else { + return TS_E_NOLAYOUT; + } + } + } + + *rect = result; + *clipped = FALSE; + return S_OK; +} + +STDMETHODIMP TSFTextStore::GetWnd(TsViewCookie view_cookie, + HWND* window_handle) { + if (!window_handle) + return E_INVALIDARG; + if (view_cookie != kViewCookie) + return E_INVALIDARG; + *window_handle = window_handle_; + return S_OK; +} + +STDMETHODIMP TSFTextStore::InsertEmbedded(DWORD flags, + LONG acp_start, + LONG acp_end, + IDataObject* data_object, + TS_TEXTCHANGE* change) { + // We don't support any embedded objects. + NOTIMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP TSFTextStore::InsertEmbeddedAtSelection(DWORD flags, + IDataObject* data_object, + LONG* acp_start, + LONG* acp_end, + TS_TEXTCHANGE* change) { + // We don't support any embedded objects. + NOTIMPLEMENTED(); + return E_NOTIMPL; +} + +STDMETHODIMP TSFTextStore::InsertTextAtSelection(DWORD flags, + const wchar_t* text_buffer, + ULONG text_buffer_size, + LONG* acp_start, + LONG* acp_end, + TS_TEXTCHANGE* text_change) { + const LONG start_pos = selection_.start(); + const LONG end_pos = selection_.end(); + const LONG new_end_pos = start_pos + text_buffer_size; + + if (flags & TS_IAS_QUERYONLY) { + if (!HasReadLock()) + return TS_E_NOLOCK; + if (acp_start) + *acp_start = start_pos; + if (acp_end) { + *acp_end = end_pos; + } + return S_OK; + } + + if (!HasReadWriteLock()) + return TS_E_NOLOCK; + if (!text_buffer) + return E_INVALIDARG; + + DCHECK_LE(start_pos, end_pos); + string_buffer_ = string_buffer_.substr(0, start_pos) + + base::string16(text_buffer, text_buffer + text_buffer_size) + + string_buffer_.substr(end_pos); + if (acp_start) + *acp_start = start_pos; + if (acp_end) + *acp_end = new_end_pos; + if (text_change) { + text_change->acpStart = start_pos; + text_change->acpOldEnd = end_pos; + text_change->acpNewEnd = new_end_pos; + } + selection_.set_start(start_pos); + selection_.set_end(new_end_pos); + return S_OK; +} + +STDMETHODIMP TSFTextStore::QueryInsert(LONG acp_test_start, + LONG acp_test_end, + ULONG text_size, + LONG* acp_result_start, + LONG* acp_result_end) { + if (!acp_result_start || !acp_result_end || acp_test_start > acp_test_end) + return E_INVALIDARG; + const LONG committed_size = static_cast<LONG>(committed_size_); + const LONG buffer_size = static_cast<LONG>(string_buffer_.size()); + *acp_result_start = + std::min(std::max(committed_size, acp_test_start), buffer_size); + *acp_result_end = + std::min(std::max(committed_size, acp_test_end), buffer_size); + return S_OK; +} + +STDMETHODIMP TSFTextStore::QueryInsertEmbedded(const GUID* service, + const FORMATETC* format, + BOOL* insertable) { + if (!format) + return E_INVALIDARG; + // We don't support any embedded objects. + if (insertable) + *insertable = FALSE; + return S_OK; +} + +STDMETHODIMP TSFTextStore::RequestAttrsAtPosition( + LONG acp_pos, + ULONG attribute_buffer_size, + const TS_ATTRID* attribute_buffer, + DWORD flags) { + // We don't support any document attributes. + // This method just returns S_OK, and the subsequently called + // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. + return S_OK; +} + +STDMETHODIMP TSFTextStore::RequestAttrsTransitioningAtPosition( + LONG acp_pos, + ULONG attribute_buffer_size, + const TS_ATTRID* attribute_buffer, + DWORD flags) { + // We don't support any document attributes. + // This method just returns S_OK, and the subsequently called + // RetrieveRequestedAttrs() returns 0 as the number of supported attributes. + return S_OK; +} + +STDMETHODIMP TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { + if (!text_store_acp_sink_.Get()) + return E_FAIL; + if (!result) + return E_INVALIDARG; + + if (current_lock_type_ != 0) { + if (lock_flags & TS_LF_SYNC) { + // Can't lock synchronously. + *result = TS_E_SYNCHRONOUS; + return S_OK; + } + // Queue the lock request. + lock_queue_.push_back(lock_flags & TS_LF_READWRITE); + *result = TS_S_ASYNC; + return S_OK; + } + + // Lock + current_lock_type_ = (lock_flags & TS_LF_READWRITE); + + edit_flag_ = false; + const size_t last_committed_size = committed_size_; + + // Grant the lock. + *result = text_store_acp_sink_->OnLockGranted(current_lock_type_); + + // Unlock + current_lock_type_ = 0; + + // Handles the pending lock requests. + while (!lock_queue_.empty()) { + current_lock_type_ = lock_queue_.front(); + lock_queue_.pop_front(); + text_store_acp_sink_->OnLockGranted(current_lock_type_); + current_lock_type_ = 0; + } + + if (!edit_flag_) { + return S_OK; + } + + // If the text store is edited in OnLockGranted(), we may need to call + // TextInputClient::InsertText() or TextInputClient::SetCompositionText(). + const size_t new_committed_size = committed_size_; + const base::string16& new_committed_string = string_buffer_.substr( + last_committed_size, new_committed_size - last_committed_size); + const base::string16& composition_string = + string_buffer_.substr(new_committed_size); + + // If there is new committed string, calls TextInputClient::InsertText(). + if ((!new_committed_string.empty()) && text_input_client_) { + text_input_client_->InsertText(new_committed_string); + } + + // Calls TextInputClient::SetCompositionText(). + CompositionText composition_text; + composition_text.text = composition_string; + composition_text.ime_text_spans = text_spans_; + // Adjusts the offset. + for (size_t i = 0; i < composition_text.ime_text_spans.size(); ++i) { + composition_text.ime_text_spans[i].start_offset -= new_committed_size; + composition_text.ime_text_spans[i].end_offset -= new_committed_size; + } + if (selection_.start() < new_committed_size) { + composition_text.selection.set_start(0); + } else { + composition_text.selection.set_start(selection_.start() - + new_committed_size); + } + if (selection_.end() < new_committed_size) { + composition_text.selection.set_end(0); + } else { + composition_text.selection.set_end(selection_.end() - new_committed_size); + } + if (text_input_client_) + text_input_client_->SetCompositionText(composition_text); + + // If there is no composition string, clear the text store status. + // And call OnSelectionChange(), OnLayoutChange(), and OnTextChange(). + if ((composition_string.empty()) && (new_committed_size != 0)) { + string_buffer_.clear(); + committed_size_ = 0; + selection_.set_start(0); + selection_.set_end(0); + if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) + text_store_acp_sink_->OnSelectionChange(); + if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) + text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); + if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { + TS_TEXTCHANGE textChange; + textChange.acpStart = 0; + textChange.acpOldEnd = new_committed_size; + textChange.acpNewEnd = 0; + text_store_acp_sink_->OnTextChange(0, &textChange); + } + } + + return S_OK; +} + +STDMETHODIMP TSFTextStore::RequestSupportedAttrs( + DWORD /* flags */, // Seems that we should ignore this. + ULONG attribute_buffer_size, + const TS_ATTRID* attribute_buffer) { + if (!attribute_buffer) + return E_INVALIDARG; + if (!text_input_client_) + return E_FAIL; + // We support only input scope attribute. + for (size_t i = 0; i < attribute_buffer_size; ++i) { + if (IsEqualGUID(GUID_PROP_INPUTSCOPE, attribute_buffer[i])) + return S_OK; + } + return E_FAIL; +} + +STDMETHODIMP TSFTextStore::RetrieveRequestedAttrs( + ULONG attribute_buffer_size, + TS_ATTRVAL* attribute_buffer, + ULONG* attribute_buffer_copied) { + if (!attribute_buffer_copied) + return E_INVALIDARG; + if (!attribute_buffer) + return E_INVALIDARG; + if (!text_input_client_) + return E_UNEXPECTED; + // We support only input scope attribute. + *attribute_buffer_copied = 0; + if (attribute_buffer_size == 0) + return S_OK; + + attribute_buffer[0].dwOverlapId = 0; + attribute_buffer[0].idAttr = GUID_PROP_INPUTSCOPE; + attribute_buffer[0].varValue.vt = VT_UNKNOWN; + attribute_buffer[0].varValue.punkVal = + tsf_inputscope::CreateInputScope(text_input_client_->GetTextInputType(), + text_input_client_->GetTextInputMode()); + attribute_buffer[0].varValue.punkVal->AddRef(); + *attribute_buffer_copied = 1; + return S_OK; +} + +STDMETHODIMP TSFTextStore::SetSelection( + ULONG selection_buffer_size, + const TS_SELECTION_ACP* selection_buffer) { + if (!HasReadWriteLock()) + return TF_E_NOLOCK; + if (selection_buffer_size > 0) { + const LONG start_pos = selection_buffer[0].acpStart; + const LONG end_pos = selection_buffer[0].acpEnd; + if (!((static_cast<LONG>(committed_size_) <= start_pos) && + (start_pos <= end_pos) && + (end_pos <= static_cast<LONG>(string_buffer_.size())))) { + return TF_E_INVALIDPOS; + } + selection_.set_start(start_pos); + selection_.set_end(end_pos); + } + return S_OK; +} + +STDMETHODIMP TSFTextStore::SetText(DWORD flags, + LONG acp_start, + LONG acp_end, + const wchar_t* text_buffer, + ULONG text_buffer_size, + TS_TEXTCHANGE* text_change) { + if (!HasReadWriteLock()) + return TS_E_NOLOCK; + if (!((static_cast<LONG>(committed_size_) <= acp_start) && + (acp_start <= acp_end) && + (acp_end <= static_cast<LONG>(string_buffer_.size())))) { + return TS_E_INVALIDPOS; + } + + TS_SELECTION_ACP selection; + selection.acpStart = acp_start; + selection.acpEnd = acp_end; + selection.style.ase = TS_AE_NONE; + selection.style.fInterimChar = 0; + + HRESULT ret; + ret = SetSelection(1, &selection); + if (ret != S_OK) + return ret; + + TS_TEXTCHANGE change; + ret = InsertTextAtSelection(0, text_buffer, text_buffer_size, &acp_start, + &acp_end, &change); + if (ret != S_OK) + return ret; + + if (text_change) + *text_change = change; + + return S_OK; +} + +STDMETHODIMP TSFTextStore::UnadviseSink(IUnknown* unknown) { + if (text_store_acp_sink_.Get() != unknown) + return CONNECT_E_NOCONNECTION; + text_store_acp_sink_.Reset(); + text_store_acp_sink_mask_ = 0; + return S_OK; +} + +STDMETHODIMP TSFTextStore::OnStartComposition( + ITfCompositionView* composition_view, + BOOL* ok) { + if (ok) + *ok = TRUE; + return S_OK; +} + +STDMETHODIMP TSFTextStore::OnUpdateComposition( + ITfCompositionView* composition_view, + ITfRange* range) { + return S_OK; +} + +STDMETHODIMP TSFTextStore::OnEndComposition( + ITfCompositionView* composition_view) { + return S_OK; +} + +STDMETHODIMP TSFTextStore::OnEndEdit(ITfContext* context, + TfEditCookie read_only_edit_cookie, + ITfEditRecord* edit_record) { + if (!context || !edit_record) + return E_INVALIDARG; + + size_t committed_size; + ImeTextSpans spans; + if (!GetCompositionStatus(context, read_only_edit_cookie, &committed_size, + &spans)) { + return S_OK; + } + text_spans_ = spans; + committed_size_ = committed_size; + edit_flag_ = true; + return S_OK; +} + +bool TSFTextStore::GetDisplayAttribute(TfGuidAtom guid_atom, + TF_DISPLAYATTRIBUTE* attribute) { + GUID guid; + if (FAILED(category_manager_->GetGUID(guid_atom, &guid))) + return false; + + Microsoft::WRL::ComPtr<ITfDisplayAttributeInfo> display_attribute_info; + if (FAILED(display_attribute_manager_->GetDisplayAttributeInfo( + guid, display_attribute_info.GetAddressOf(), nullptr))) { + return false; + } + return SUCCEEDED(display_attribute_info->GetAttributeInfo(attribute)); +} + +bool TSFTextStore::GetCompositionStatus( + ITfContext* context, + const TfEditCookie read_only_edit_cookie, + size_t* committed_size, + ImeTextSpans* spans) { + DCHECK(context); + DCHECK(committed_size); + DCHECK(spans); + const GUID* rgGuids[2] = {&GUID_PROP_COMPOSING, &GUID_PROP_ATTRIBUTE}; + Microsoft::WRL::ComPtr<ITfReadOnlyProperty> track_property; + if (FAILED(context->TrackProperties(rgGuids, 2, nullptr, 0, + track_property.GetAddressOf()))) { + return false; + } + + *committed_size = 0; + spans->clear(); + Microsoft::WRL::ComPtr<ITfRange> start_to_end_range; + Microsoft::WRL::ComPtr<ITfRange> end_range; + if (FAILED(context->GetStart(read_only_edit_cookie, + start_to_end_range.GetAddressOf()))) { + return false; + } + if (FAILED(context->GetEnd(read_only_edit_cookie, end_range.GetAddressOf()))) + return false; + if (FAILED(start_to_end_range->ShiftEndToRange( + read_only_edit_cookie, end_range.Get(), TF_ANCHOR_END))) { + return false; + } + + Microsoft::WRL::ComPtr<IEnumTfRanges> ranges; + if (FAILED(track_property->EnumRanges(read_only_edit_cookie, + ranges.GetAddressOf(), + start_to_end_range.Get()))) { + return false; + } + + while (true) { + Microsoft::WRL::ComPtr<ITfRange> range; + if (ranges->Next(1, range.GetAddressOf(), nullptr) != S_OK) + break; + base::win::ScopedVariant value; + Microsoft::WRL::ComPtr<IEnumTfPropertyValue> enum_prop_value; + if (FAILED(track_property->GetValue(read_only_edit_cookie, range.Get(), + value.Receive()))) { + return false; + } + if (FAILED(value.AsInput()->punkVal->QueryInterface( + IID_PPV_ARGS(&enum_prop_value)))) + return false; + + TF_PROPERTYVAL property_value; + bool is_composition = false; + bool has_display_attribute = false; + TF_DISPLAYATTRIBUTE display_attribute = {}; + while (enum_prop_value->Next(1, &property_value, nullptr) == S_OK) { + if (IsEqualGUID(property_value.guidId, GUID_PROP_COMPOSING)) { + is_composition = (property_value.varValue.lVal == TRUE); + } else if (IsEqualGUID(property_value.guidId, GUID_PROP_ATTRIBUTE)) { + TfGuidAtom guid_atom = + static_cast<TfGuidAtom>(property_value.varValue.lVal); + if (GetDisplayAttribute(guid_atom, &display_attribute)) + has_display_attribute = true; + } + VariantClear(&property_value.varValue); + } + + Microsoft::WRL::ComPtr<ITfRangeACP> range_acp; + range.CopyTo(range_acp.GetAddressOf()); + LONG start_pos, length; + range_acp->GetExtent(&start_pos, &length); + if (!is_composition) { + if (*committed_size < static_cast<size_t>(start_pos + length)) + *committed_size = start_pos + length; + } else { + ImeTextSpan span; + span.start_offset = start_pos; + span.end_offset = start_pos + length; + span.underline_color = SK_ColorBLACK; + span.background_color = SK_ColorTRANSPARENT; + if (has_display_attribute) { + span.thickness = display_attribute.fBoldLine + ? ImeTextSpan::Thickness::kThick + : ImeTextSpan::Thickness::kThin; + } + spans->push_back(span); + } + } + return true; +} + +void TSFTextStore::SetFocusedTextInputClient( + HWND focused_window, + TextInputClient* text_input_client) { + window_handle_ = focused_window; + text_input_client_ = text_input_client; +} + +void TSFTextStore::RemoveFocusedTextInputClient( + TextInputClient* text_input_client) { + if (text_input_client_ == text_input_client) { + window_handle_ = nullptr; + text_input_client_ = nullptr; + } +} + +bool TSFTextStore::CancelComposition() { + // If there is an on-going document lock, we must not edit the text. + if (edit_flag_) + return false; + + if (string_buffer_.empty()) + return true; + + // Unlike ImmNotifyIME(NI_COMPOSITIONSTR, CPS_CANCEL, 0) in IMM32, TSF does + // not have a dedicated method to cancel composition. However, CUAS actually + // has a protocol conversion from CPS_CANCEL into TSF operations. According + // to the observations on Windows 7, TIPs are expected to cancel composition + // when an on-going composition text is replaced with an empty string. So + // we use the same operation to cancel composition here to minimize the risk + // of potential compatibility issues. + + const size_t previous_buffer_size = string_buffer_.size(); + string_buffer_.clear(); + committed_size_ = 0; + selection_.set_start(0); + selection_.set_end(0); + if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) + text_store_acp_sink_->OnSelectionChange(); + if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) + text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); + if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { + TS_TEXTCHANGE textChange = {}; + textChange.acpStart = 0; + textChange.acpOldEnd = previous_buffer_size; + textChange.acpNewEnd = 0; + text_store_acp_sink_->OnTextChange(0, &textChange); + } + return true; +} + +bool TSFTextStore::ConfirmComposition() { + // If there is an on-going document lock, we must not edit the text. + if (edit_flag_) + return false; + + if (string_buffer_.empty()) + return true; + + // See the comment in TSFTextStore::CancelComposition. + // This logic is based on the observation about how to emulate + // ImmNotifyIME(NI_COMPOSITIONSTR, CPS_COMPLETE, 0) by CUAS. + + const base::string16& composition_text = + string_buffer_.substr(committed_size_); + if (!composition_text.empty()) + text_input_client_->InsertText(composition_text); + + const size_t previous_buffer_size = string_buffer_.size(); + string_buffer_.clear(); + committed_size_ = 0; + selection_.set_start(0); + selection_.set_end(0); + if (text_store_acp_sink_mask_ & TS_AS_SEL_CHANGE) + text_store_acp_sink_->OnSelectionChange(); + if (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE) + text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); + if (text_store_acp_sink_mask_ & TS_AS_TEXT_CHANGE) { + TS_TEXTCHANGE textChange = {}; + textChange.acpStart = 0; + textChange.acpOldEnd = previous_buffer_size; + textChange.acpNewEnd = 0; + text_store_acp_sink_->OnTextChange(0, &textChange); + } + return true; +} + +void TSFTextStore::SendOnLayoutChange() { + if (text_store_acp_sink_ && (text_store_acp_sink_mask_ & TS_AS_LAYOUT_CHANGE)) + text_store_acp_sink_->OnLayoutChange(TS_LC_CHANGE, 0); +} + +bool TSFTextStore::HasReadLock() const { + return (current_lock_type_ & TS_LF_READ) == TS_LF_READ; +} + +bool TSFTextStore::HasReadWriteLock() const { + return (current_lock_type_ & TS_LF_READWRITE) == TS_LF_READWRITE; +} + +} // namespace ui diff --git a/chromium/ui/base/ime/win/tsf_text_store.h b/chromium/ui/base/ime/win/tsf_text_store.h new file mode 100644 index 00000000000..687ca8f6d62 --- /dev/null +++ b/chromium/ui/base/ime/win/tsf_text_store.h @@ -0,0 +1,311 @@ +// 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_WIN_TSF_TEXT_STORE_H_ +#define UI_BASE_IME_WIN_TSF_TEXT_STORE_H_ + +#include <msctf.h> +#include <wrl/client.h> +#include <deque> + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "ui/base/ime/ime_text_span.h" +#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/gfx/range/range.h" + +namespace ui { +class TextInputClient; + +// TSFTextStore is used to interact with the input method via TSF manager. +// TSFTextStore have a string buffer which is manipulated by TSF manager through +// ITextStoreACP interface methods such as SetText(). +// When the input method updates the composition, TSFTextStore calls +// TextInputClient::SetCompositionText(). And when the input method finishes the +// composition, TSFTextStore calls TextInputClient::InsertText() and clears the +// buffer. +// +// How TSFTextStore works: +// - The user enters "a". +// - The input method set composition as "a". +// - TSF manager calls TSFTextStore::RequestLock(). +// - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted(). +// - In OnLockGranted(), TSF manager calls +// - TSFTextStore::OnStartComposition() +// - TSFTextStore::SetText() +// The string buffer is set as "a". +// - TSFTextStore::OnUpdateComposition() +// - TSFTextStore::OnEndEdit() +// TSFTextStore can get the composition information such as underlines. +// - TSFTextStore calls TextInputClient::SetCompositionText(). +// "a" is shown with an underline as composition string. +// - The user enters <space>. +// - The input method set composition as "A". +// - TSF manager calls TSFTextStore::RequestLock(). +// - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted(). +// - In OnLockGranted(), TSF manager calls +// - TSFTextStore::SetText() +// The string buffer is set as "A". +// - TSFTextStore::OnUpdateComposition() +// - TSFTextStore::OnEndEdit() +// - TSFTextStore calls TextInputClient::SetCompositionText(). +// "A" is shown with an underline as composition string. +// - The user enters <enter>. +// - The input method commits "A". +// - TSF manager calls TSFTextStore::RequestLock(). +// - TSFTextStore callbacks ITextStoreACPSink::OnLockGranted(). +// - In OnLockGranted(), TSF manager calls +// - TSFTextStore::OnEndComposition() +// - TSFTextStore::OnEndEdit() +// TSFTextStore knows "A" is committed. +// - TSFTextStore calls TextInputClient::InsertText(). +// "A" is shown as committed string. +// - TSFTextStore clears the string buffer. +// - TSFTextStore calls OnSelectionChange(), OnLayoutChange() and +// OnTextChange() of ITextStoreACPSink to let TSF manager know that the +// string buffer has been changed. +// +// About the locking scheme: +// When TSF manager manipulates the string buffer it calls RequestLock() to get +// the lock of the document. If TSFTextStore can grant the lock request, it +// callbacks ITextStoreACPSink::OnLockGranted(). +// RequestLock() is called from only one thread, but called recursively in +// OnLockGranted() or OnSelectionChange() or OnLayoutChange() or OnTextChange(). +// If the document is locked and the lock request is asynchronous, TSFTextStore +// queues the request. The queued requests will be handled after the current +// lock is removed. +// More information about document locks can be found here: +// http://msdn.microsoft.com/en-us/library/ms538064 +// +// More information about TSF can be found here: +// http://msdn.microsoft.com/en-us/library/ms629032 +class UI_BASE_IME_EXPORT TSFTextStore : public ITextStoreACP, + public ITfContextOwnerCompositionSink, + public ITfTextEditSink { + public: + TSFTextStore(); + virtual ~TSFTextStore(); + + // ITextStoreACP: + STDMETHOD_(ULONG, AddRef)() override; + STDMETHOD_(ULONG, Release)() override; + STDMETHOD(QueryInterface)(REFIID iid, void** ppv) override; + STDMETHOD(AdviseSink)(REFIID iid, IUnknown* unknown, DWORD mask) override; + STDMETHOD(FindNextAttrTransition) + (LONG acp_start, + LONG acp_halt, + ULONG num_filter_attributes, + const TS_ATTRID* filter_attributes, + DWORD flags, + LONG* acp_next, + BOOL* found, + LONG* found_offset) override; + STDMETHOD(GetACPFromPoint) + (TsViewCookie view_cookie, + const POINT* point, + DWORD flags, + LONG* acp) override; + STDMETHOD(GetActiveView)(TsViewCookie* view_cookie) override; + STDMETHOD(GetEmbedded) + (LONG acp_pos, REFGUID service, REFIID iid, IUnknown** unknown) override; + STDMETHOD(GetEndACP)(LONG* acp) override; + STDMETHOD(GetFormattedText) + (LONG acp_start, LONG acp_end, IDataObject** data_object) override; + STDMETHOD(GetScreenExt)(TsViewCookie view_cookie, RECT* rect) override; + STDMETHOD(GetSelection) + (ULONG selection_index, + ULONG selection_buffer_size, + TS_SELECTION_ACP* selection_buffer, + ULONG* fetched_count) override; + STDMETHOD(GetStatus)(TS_STATUS* pdcs) override; + STDMETHOD(GetText) + (LONG acp_start, + LONG acp_end, + wchar_t* text_buffer, + ULONG text_buffer_size, + ULONG* text_buffer_copied, + TS_RUNINFO* run_info_buffer, + ULONG run_info_buffer_size, + ULONG* run_info_buffer_copied, + LONG* next_acp) override; + STDMETHOD(GetTextExt) + (TsViewCookie view_cookie, + LONG acp_start, + LONG acp_end, + RECT* rect, + BOOL* clipped) override; + STDMETHOD(GetWnd)(TsViewCookie view_cookie, HWND* window_handle) override; + STDMETHOD(InsertEmbedded) + (DWORD flags, + LONG acp_start, + LONG acp_end, + IDataObject* data_object, + TS_TEXTCHANGE* change) override; + STDMETHOD(InsertEmbeddedAtSelection) + (DWORD flags, + IDataObject* data_object, + LONG* acp_start, + LONG* acp_end, + TS_TEXTCHANGE* change) override; + STDMETHOD(InsertTextAtSelection) + (DWORD flags, + const wchar_t* text_buffer, + ULONG text_buffer_size, + LONG* acp_start, + LONG* acp_end, + TS_TEXTCHANGE* text_change) override; + STDMETHOD(QueryInsert) + (LONG acp_test_start, + LONG acp_test_end, + ULONG text_size, + LONG* acp_result_start, + LONG* acp_result_end) override; + STDMETHOD(QueryInsertEmbedded) + (const GUID* service, const FORMATETC* format, BOOL* insertable) override; + STDMETHOD(RequestAttrsAtPosition) + (LONG acp_pos, + ULONG attribute_buffer_size, + const TS_ATTRID* attribute_buffer, + DWORD flags) override; + STDMETHOD(RequestAttrsTransitioningAtPosition) + (LONG acp_pos, + ULONG attribute_buffer_size, + const TS_ATTRID* attribute_buffer, + DWORD flags) override; + STDMETHOD(RequestLock)(DWORD lock_flags, HRESULT* result) override; + STDMETHOD(RequestSupportedAttrs) + (DWORD flags, + ULONG attribute_buffer_size, + const TS_ATTRID* attribute_buffer) override; + STDMETHOD(RetrieveRequestedAttrs) + (ULONG attribute_buffer_size, + TS_ATTRVAL* attribute_buffer, + ULONG* attribute_buffer_copied) override; + STDMETHOD(SetSelection) + (ULONG selection_buffer_size, + const TS_SELECTION_ACP* selection_buffer) override; + STDMETHOD(SetText) + (DWORD flags, + LONG acp_start, + LONG acp_end, + const wchar_t* text_buffer, + ULONG text_buffer_size, + TS_TEXTCHANGE* text_change) override; + STDMETHOD(UnadviseSink)(IUnknown* unknown) override; + + // ITfContextOwnerCompositionSink: + STDMETHOD(OnStartComposition) + (ITfCompositionView* composition_view, BOOL* ok) override; + STDMETHOD(OnUpdateComposition) + (ITfCompositionView* composition_view, ITfRange* range) override; + STDMETHOD(OnEndComposition)(ITfCompositionView* composition_view) override; + + // ITfTextEditSink: + STDMETHOD(OnEndEdit) + (ITfContext* context, + TfEditCookie read_only_edit_cookie, + ITfEditRecord* edit_record) override; + + // Sets currently focused TextInputClient. + void SetFocusedTextInputClient(HWND focused_window, + TextInputClient* text_input_client); + // Removes currently focused TextInputClient. + void RemoveFocusedTextInputClient(TextInputClient* text_input_client); + + // Cancels the ongoing composition if exists. + bool CancelComposition(); + + // Confirms the ongoing composition if exists. + bool ConfirmComposition(); + + // Sends OnLayoutChange() via |text_store_acp_sink_|. + void SendOnLayoutChange(); + + private: + friend class TSFTextStoreTest; + friend class TSFTextStoreTestCallback; + + // Checks if the document has a read-only lock. + bool HasReadLock() const; + + // Checks if the document has a read and write lock. + bool HasReadWriteLock() const; + + // Gets the display attribute structure. + bool GetDisplayAttribute(TfGuidAtom guid_atom, + TF_DISPLAYATTRIBUTE* attribute); + + // Gets the committed string size and underline information of the context. + bool GetCompositionStatus(ITfContext* context, + const TfEditCookie read_only_edit_cookie, + size_t* committed_size, + ImeTextSpans* spans); + + // The refrence count of this instance. + volatile LONG ref_count_ = 0; + + // A pointer of ITextStoreACPSink, this instance is given in AdviseSink. + Microsoft::WRL::ComPtr<ITextStoreACPSink> text_store_acp_sink_; + + // The current mask of |text_store_acp_sink_|. + DWORD text_store_acp_sink_mask_ = 0; + + // HWND of the current view window which is set in SetFocusedTextInputClient. + HWND window_handle_ = nullptr; + + // Current TextInputClient which is set in SetFocusedTextInputClient. + TextInputClient* text_input_client_ = nullptr; + + // TODO(dtapuska): determine if we can expose more the entire document + // more than the committed string and composition string to the TIP. + // |string_buffer_| contains committed string and composition string. + // Example: "aoi" is committed, and "umi" is under composition. + // |string_buffer_|: "aoiumi" + // |committed_size_|: 3 + base::string16 string_buffer_; + size_t committed_size_ = 0; + + // |selection_start_| and |selection_end_| indicates the selection range. + // Example: "iue" is selected + // |string_buffer_|: "aiueo" + // |selection_.start()|: 1 + // |selection_.end()|: 4 + gfx::Range selection_; + + // |start_offset| and |end_offset| of |text_spans_| indicates + // the offsets in |string_buffer_|. + // Example: "aoi" is committed. There are two underlines in "umi" and "no". + // |string_buffer_|: "aoiumino" + // |committed_size_|: 3 + // text_spans_[0].start_offset: 3 + // text_spans_[0].end_offset: 6 + // text_spans_[1].start_offset: 6 + // text_spans_[1].end_offset: 8 + ImeTextSpans text_spans_; + + // |edit_flag_| indicates that the status is edited during + // ITextStoreACPSink::OnLockGranted(). + bool edit_flag_ = false; + + // The type of current lock. + // 0: No lock. + // TS_LF_READ: read-only lock. + // TS_LF_READWRITE: read/write lock. + DWORD current_lock_type_ = 0; + + // Queue of the lock request used in RequestLock(). + std::deque<DWORD> lock_queue_; + + // Category manager and Display attribute manager are used to obtain the + // attributes of the composition string. + Microsoft::WRL::ComPtr<ITfCategoryMgr> category_manager_; + Microsoft::WRL::ComPtr<ITfDisplayAttributeMgr> display_attribute_manager_; + + DISALLOW_COPY_AND_ASSIGN(TSFTextStore); +}; + +} // namespace ui + +#endif // UI_BASE_IME_WIN_TSF_TEXT_STORE_H_ diff --git a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc new file mode 100644 index 00000000000..76c3eb149d9 --- /dev/null +++ b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc @@ -0,0 +1,1302 @@ +// 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/win/tsf_text_store.h" + +#include <initguid.h> // for GUID_NULL and GUID_PROP_INPUTSCOPE + +#include <InputScope.h> +#include <OleCtl.h> +#include <wrl/client.h> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/win/scoped_com_initializer.h" +#include "base/win/scoped_variant.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/ime/text_input_client.h" +#include "ui/events/event.h" +#include "ui/gfx/geometry/rect.h" + +using testing::_; +using testing::Invoke; +using testing::Return; + +namespace ui { +namespace { + +class MockTextInputClient : public TextInputClient { + public: + ~MockTextInputClient() {} + MOCK_METHOD1(SetCompositionText, void(const ui::CompositionText&)); + MOCK_METHOD0(ConfirmCompositionText, void()); + MOCK_METHOD0(ClearCompositionText, void()); + MOCK_METHOD1(InsertText, void(const base::string16&)); + MOCK_METHOD1(InsertChar, void(const ui::KeyEvent&)); + MOCK_CONST_METHOD0(GetTextInputType, ui::TextInputType()); + MOCK_CONST_METHOD0(GetTextInputMode, ui::TextInputMode()); + MOCK_CONST_METHOD0(GetTextDirection, base::i18n::TextDirection()); + MOCK_CONST_METHOD0(GetTextInputFlags, int()); + MOCK_CONST_METHOD0(CanComposeInline, bool()); + MOCK_CONST_METHOD0(GetCaretBounds, gfx::Rect()); + MOCK_CONST_METHOD2(GetCompositionCharacterBounds, bool(uint32_t, gfx::Rect*)); + MOCK_CONST_METHOD0(HasCompositionText, bool()); + MOCK_CONST_METHOD1(GetTextRange, bool(gfx::Range*)); + MOCK_CONST_METHOD1(GetCompositionTextRange, bool(gfx::Range*)); + MOCK_CONST_METHOD1(GetSelectionRange, bool(gfx::Range*)); + MOCK_METHOD1(SetSelectionRange, bool(const gfx::Range&)); + MOCK_METHOD1(DeleteRange, bool(const gfx::Range&)); + MOCK_CONST_METHOD2(GetTextFromRange, + bool(const gfx::Range&, base::string16*)); + MOCK_METHOD0(OnInputMethodChanged, void()); + MOCK_METHOD1(ChangeTextDirectionAndLayoutAlignment, + bool(base::i18n::TextDirection)); + MOCK_METHOD2(ExtendSelectionAndDelete, void(size_t, size_t)); + MOCK_METHOD1(EnsureCaretNotInRect, void(const gfx::Rect&)); + MOCK_CONST_METHOD1(IsTextEditCommandEnabled, bool(TextEditCommand)); + MOCK_METHOD1(SetTextEditCommandForNextKeyEvent, void(TextEditCommand)); + MOCK_CONST_METHOD0(GetClientSourceInfo, const std::string&()); +}; + +class MockStoreACPSink : public ITextStoreACPSink { + public: + MockStoreACPSink() : ref_count_(0) {} + + // IUnknown + ULONG STDMETHODCALLTYPE AddRef() override { + return InterlockedIncrement(&ref_count_); + } + ULONG STDMETHODCALLTYPE Release() override { + const LONG count = InterlockedDecrement(&ref_count_); + if (!count) { + delete this; + return 0; + } + return static_cast<ULONG>(count); + } + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** report) override { + if (iid == IID_IUnknown || iid == IID_ITextStoreACPSink) { + *report = static_cast<ITextStoreACPSink*>(this); + } else { + *report = nullptr; + return E_NOINTERFACE; + } + AddRef(); + return S_OK; + } + + // ITextStoreACPSink + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + OnTextChange, + HRESULT(DWORD, const TS_TEXTCHANGE*)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, OnSelectionChange, HRESULT()); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + OnLayoutChange, + HRESULT(TsLayoutCode, TsViewCookie)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, OnStatusChange, HRESULT(DWORD)); + MOCK_METHOD4_WITH_CALLTYPE(STDMETHODCALLTYPE, + OnAttrsChange, + HRESULT(LONG, LONG, ULONG, const TS_ATTRID*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, OnLockGranted, HRESULT(DWORD)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + OnStartEditTransaction, + HRESULT()); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, + OnEndEditTransaction, + HRESULT()); + + private: + virtual ~MockStoreACPSink() {} + + volatile LONG ref_count_; +}; + +const HWND kWindowHandle = reinterpret_cast<HWND>(1); + +} // namespace + +class TSFTextStoreTest : public testing::Test { + protected: + void SetUp() override { + text_store_ = new TSFTextStore(); + sink_ = new MockStoreACPSink(); + EXPECT_EQ(S_OK, text_store_->AdviseSink(IID_ITextStoreACPSink, sink_.get(), + TS_AS_ALL_SINKS)); + text_store_->SetFocusedTextInputClient(kWindowHandle, &text_input_client_); + } + + void TearDown() override { + EXPECT_EQ(S_OK, text_store_->UnadviseSink(sink_.get())); + sink_ = nullptr; + text_store_ = nullptr; + } + + // Accessors to the internal state of TSFTextStore. + base::string16* string_buffer() { return &text_store_->string_buffer_; } + size_t* committed_size() { return &text_store_->committed_size_; } + + base::win::ScopedCOMInitializer com_initializer_; + MockTextInputClient text_input_client_; + scoped_refptr<TSFTextStore> text_store_; + scoped_refptr<MockStoreACPSink> sink_; +}; + +class TSFTextStoreTestCallback { + public: + explicit TSFTextStoreTestCallback(TSFTextStore* text_store) + : text_store_(text_store) { + CHECK(text_store_); + } + virtual ~TSFTextStoreTestCallback() {} + + protected: + // Accessors to the internal state of TSFTextStore. + bool* edit_flag() { return &text_store_->edit_flag_; } + base::string16* string_buffer() { return &text_store_->string_buffer_; } + size_t* committed_size() { return &text_store_->committed_size_; } + gfx::Range* selection() { return &text_store_->selection_; } + ImeTextSpans* text_spans() { return &text_store_->text_spans_; } + + void SetInternalState(const base::string16& new_string_buffer, + LONG new_committed_size, + LONG new_selection_start, + LONG new_selection_end) { + ASSERT_LE(0, new_committed_size); + ASSERT_LE(new_committed_size, new_selection_start); + ASSERT_LE(new_selection_start, new_selection_end); + ASSERT_LE(new_selection_end, static_cast<LONG>(new_string_buffer.size())); + *string_buffer() = new_string_buffer; + *committed_size() = new_committed_size; + selection()->set_start(new_selection_start); + selection()->set_end(new_selection_end); + } + + bool HasReadLock() const { return text_store_->HasReadLock(); } + bool HasReadWriteLock() const { return text_store_->HasReadWriteLock(); } + + void GetSelectionTest(LONG expected_acp_start, LONG expected_acp_end) { + TS_SELECTION_ACP selection = {}; + ULONG fetched = 0; + EXPECT_EQ(S_OK, text_store_->GetSelection(0, 1, &selection, &fetched)); + EXPECT_EQ(1u, fetched); + EXPECT_EQ(expected_acp_start, selection.acpStart); + EXPECT_EQ(expected_acp_end, selection.acpEnd); + } + + void SetSelectionTest(LONG acp_start, LONG acp_end, HRESULT expected_result) { + TS_SELECTION_ACP selection = {}; + selection.acpStart = acp_start; + selection.acpEnd = acp_end; + selection.style.ase = TS_AE_NONE; + selection.style.fInterimChar = 0; + EXPECT_EQ(expected_result, text_store_->SetSelection(1, &selection)); + if (expected_result == S_OK) { + GetSelectionTest(acp_start, acp_end); + } + } + + void SetTextTest(LONG acp_start, + LONG acp_end, + const base::string16& text, + HRESULT error_code) { + TS_TEXTCHANGE change = {}; + ASSERT_EQ(error_code, + text_store_->SetText(0, acp_start, acp_end, text.c_str(), + text.size(), &change)); + if (error_code == S_OK) { + EXPECT_EQ(acp_start, change.acpStart); + EXPECT_EQ(acp_end, change.acpOldEnd); + EXPECT_EQ(acp_start + text.size(), (size_t)change.acpNewEnd); + } + } + + void GetTextTest(LONG acp_start, + LONG acp_end, + const base::string16& expected_string, + LONG expected_next_acp) { + wchar_t buffer[1024] = {}; + ULONG text_buffer_copied = 0; + TS_RUNINFO run_info = {}; + ULONG run_info_buffer_copied = 0; + LONG next_acp = 0; + ASSERT_EQ(S_OK, text_store_->GetText(acp_start, acp_end, buffer, 1024, + &text_buffer_copied, &run_info, 1, + &run_info_buffer_copied, &next_acp)); + ASSERT_EQ(expected_string.size(), text_buffer_copied); + EXPECT_EQ(expected_string, + base::string16(buffer, buffer + text_buffer_copied)); + EXPECT_EQ(1u, run_info_buffer_copied); + EXPECT_EQ(expected_string.size(), run_info.uCount); + EXPECT_EQ(TS_RT_PLAIN, run_info.type); + EXPECT_EQ(expected_next_acp, next_acp); + } + + void GetTextErrorTest(LONG acp_start, LONG acp_end, HRESULT error_code) { + wchar_t buffer[1024] = {}; + ULONG text_buffer_copied = 0; + TS_RUNINFO run_info = {}; + ULONG run_info_buffer_copied = 0; + LONG next_acp = 0; + EXPECT_EQ(error_code, + text_store_->GetText(acp_start, acp_end, buffer, 1024, + &text_buffer_copied, &run_info, 1, + &run_info_buffer_copied, &next_acp)); + } + + void InsertTextAtSelectionTest(const wchar_t* buffer, + ULONG buffer_size, + LONG expected_start, + LONG expected_end, + LONG expected_change_start, + LONG expected_change_old_end, + LONG expected_change_new_end) { + LONG start = 0; + LONG end = 0; + TS_TEXTCHANGE change = {}; + EXPECT_EQ(S_OK, text_store_->InsertTextAtSelection(0, buffer, buffer_size, + &start, &end, &change)); + EXPECT_EQ(expected_start, start); + EXPECT_EQ(expected_end, end); + EXPECT_EQ(expected_change_start, change.acpStart); + EXPECT_EQ(expected_change_old_end, change.acpOldEnd); + EXPECT_EQ(expected_change_new_end, change.acpNewEnd); + } + + void InsertTextAtSelectionQueryOnlyTest(const wchar_t* buffer, + ULONG buffer_size, + LONG expected_start, + LONG expected_end) { + LONG start = 0; + LONG end = 0; + EXPECT_EQ(S_OK, text_store_->InsertTextAtSelection(TS_IAS_QUERYONLY, buffer, + buffer_size, &start, + &end, nullptr)); + EXPECT_EQ(expected_start, start); + EXPECT_EQ(expected_end, end); + } + + void GetTextExtTest(TsViewCookie view_cookie, + LONG acp_start, + LONG acp_end, + LONG expected_left, + LONG expected_top, + LONG expected_right, + LONG expected_bottom) { + RECT rect = {}; + BOOL clipped = FALSE; + EXPECT_EQ(S_OK, text_store_->GetTextExt(view_cookie, acp_start, acp_end, + &rect, &clipped)); + EXPECT_EQ(expected_left, rect.left); + EXPECT_EQ(expected_top, rect.top); + EXPECT_EQ(expected_right, rect.right); + EXPECT_EQ(expected_bottom, rect.bottom); + EXPECT_EQ(FALSE, clipped); + } + + void GetTextExtNoLayoutTest(TsViewCookie view_cookie, + LONG acp_start, + LONG acp_end) { + RECT rect = {}; + BOOL clipped = FALSE; + EXPECT_EQ(TS_E_NOLAYOUT, text_store_->GetTextExt(view_cookie, acp_start, + acp_end, &rect, &clipped)); + } + + scoped_refptr<TSFTextStore> text_store_; + + private: + DISALLOW_COPY_AND_ASSIGN(TSFTextStoreTestCallback); +}; + +namespace { + +const HRESULT kInvalidResult = 0x12345678; + +TEST_F(TSFTextStoreTest, GetStatusTest) { + TS_STATUS status = {}; + EXPECT_EQ(S_OK, text_store_->GetStatus(&status)); + EXPECT_EQ(0u, status.dwDynamicFlags); + EXPECT_EQ((ULONG)(TS_SS_TRANSITORY | TS_SS_NOHIDDENTEXT), + status.dwStaticFlags); +} + +TEST_F(TSFTextStoreTest, QueryInsertTest) { + LONG result_start = 0; + LONG result_end = 0; + *string_buffer() = L""; + *committed_size() = 0; + EXPECT_EQ(E_INVALIDARG, + text_store_->QueryInsert(0, 0, 0, nullptr, &result_end)); + EXPECT_EQ(E_INVALIDARG, + text_store_->QueryInsert(0, 0, 0, &result_start, nullptr)); + EXPECT_EQ(S_OK, + text_store_->QueryInsert(0, 0, 0, &result_start, &result_end)); + EXPECT_EQ(0, result_start); + EXPECT_EQ(0, result_end); + *string_buffer() = L"1234"; + *committed_size() = 1; + EXPECT_EQ(S_OK, + text_store_->QueryInsert(0, 1, 0, &result_start, &result_end)); + EXPECT_EQ(1, result_start); + EXPECT_EQ(1, result_end); + EXPECT_EQ(E_INVALIDARG, + text_store_->QueryInsert(1, 0, 0, &result_start, &result_end)); + EXPECT_EQ(S_OK, + text_store_->QueryInsert(2, 2, 0, &result_start, &result_end)); + EXPECT_EQ(2, result_start); + EXPECT_EQ(2, result_end); + EXPECT_EQ(S_OK, + text_store_->QueryInsert(2, 3, 0, &result_start, &result_end)); + EXPECT_EQ(2, result_start); + EXPECT_EQ(3, result_end); + EXPECT_EQ(E_INVALIDARG, + text_store_->QueryInsert(3, 2, 0, &result_start, &result_end)); + EXPECT_EQ(S_OK, + text_store_->QueryInsert(3, 4, 0, &result_start, &result_end)); + EXPECT_EQ(3, result_start); + EXPECT_EQ(4, result_end); + EXPECT_EQ(S_OK, + text_store_->QueryInsert(3, 5, 0, &result_start, &result_end)); + EXPECT_EQ(3, result_start); + EXPECT_EQ(4, result_end); +} + +class SyncRequestLockTestCallback : public TSFTextStoreTestCallback { + public: + explicit SyncRequestLockTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT LockGranted1(DWORD flags) { + EXPECT_TRUE(HasReadLock()); + EXPECT_FALSE(HasReadWriteLock()); + return S_OK; + } + + HRESULT LockGranted2(DWORD flags) { + EXPECT_TRUE(HasReadLock()); + EXPECT_TRUE(HasReadWriteLock()); + return S_OK; + } + + HRESULT LockGranted3(DWORD flags) { + EXPECT_TRUE(HasReadLock()); + EXPECT_FALSE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ | TS_LF_SYNC, &result)); + EXPECT_EQ(TS_E_SYNCHRONOUS, result); + return S_OK; + } + + HRESULT LockGranted4(DWORD flags) { + EXPECT_TRUE(HasReadLock()); + EXPECT_FALSE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, + text_store_->RequestLock(TS_LF_READWRITE | TS_LF_SYNC, &result)); + EXPECT_EQ(TS_E_SYNCHRONOUS, result); + return S_OK; + } + + HRESULT LockGranted5(DWORD flags) { + EXPECT_TRUE(HasReadLock()); + EXPECT_TRUE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ | TS_LF_SYNC, &result)); + EXPECT_EQ(TS_E_SYNCHRONOUS, result); + return S_OK; + } + + HRESULT LockGranted6(DWORD flags) { + EXPECT_TRUE(HasReadLock()); + EXPECT_TRUE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, + text_store_->RequestLock(TS_LF_READWRITE | TS_LF_SYNC, &result)); + EXPECT_EQ(TS_E_SYNCHRONOUS, result); + return S_OK; + } + + private: + DISALLOW_COPY_AND_ASSIGN(SyncRequestLockTestCallback); +}; + +TEST_F(TSFTextStoreTest, SynchronousRequestLockTest) { + SyncRequestLockTestCallback callback(text_store_.get()); + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &SyncRequestLockTestCallback::LockGranted1)) + .WillOnce(Invoke(&callback, &SyncRequestLockTestCallback::LockGranted2)) + .WillOnce(Invoke(&callback, &SyncRequestLockTestCallback::LockGranted3)) + .WillOnce(Invoke(&callback, &SyncRequestLockTestCallback::LockGranted4)) + .WillOnce(Invoke(&callback, &SyncRequestLockTestCallback::LockGranted5)) + .WillOnce(Invoke(&callback, &SyncRequestLockTestCallback::LockGranted6)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ | TS_LF_SYNC, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, + text_store_->RequestLock(TS_LF_READWRITE | TS_LF_SYNC, &result)); + EXPECT_EQ(S_OK, result); + + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ | TS_LF_SYNC, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ | TS_LF_SYNC, &result)); + EXPECT_EQ(S_OK, result); + + result = kInvalidResult; + EXPECT_EQ(S_OK, + text_store_->RequestLock(TS_LF_READWRITE | TS_LF_SYNC, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, + text_store_->RequestLock(TS_LF_READWRITE | TS_LF_SYNC, &result)); + EXPECT_EQ(S_OK, result); +} + +class AsyncRequestLockTestCallback : public TSFTextStoreTestCallback { + public: + explicit AsyncRequestLockTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store), state_(0) {} + + HRESULT LockGranted1(DWORD flags) { + EXPECT_EQ(0, state_); + state_ = 1; + EXPECT_TRUE(HasReadLock()); + EXPECT_FALSE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ, &result)); + EXPECT_EQ(TS_S_ASYNC, result); + EXPECT_EQ(1, state_); + state_ = 2; + return S_OK; + } + + HRESULT LockGranted2(DWORD flags) { + EXPECT_EQ(2, state_); + EXPECT_TRUE(HasReadLock()); + EXPECT_FALSE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(TS_S_ASYNC, result); + EXPECT_EQ(2, state_); + state_ = 3; + return S_OK; + } + + HRESULT LockGranted3(DWORD flags) { + EXPECT_EQ(3, state_); + EXPECT_TRUE(HasReadLock()); + EXPECT_TRUE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(TS_S_ASYNC, result); + EXPECT_EQ(3, state_); + state_ = 4; + return S_OK; + } + + HRESULT LockGranted4(DWORD flags) { + EXPECT_EQ(4, state_); + EXPECT_TRUE(HasReadLock()); + EXPECT_TRUE(HasReadWriteLock()); + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ, &result)); + EXPECT_EQ(TS_S_ASYNC, result); + EXPECT_EQ(4, state_); + state_ = 5; + return S_OK; + } + + HRESULT LockGranted5(DWORD flags) { + EXPECT_EQ(5, state_); + EXPECT_TRUE(HasReadLock()); + EXPECT_FALSE(HasReadWriteLock()); + state_ = 6; + return S_OK; + } + + private: + int state_; + + DISALLOW_COPY_AND_ASSIGN(AsyncRequestLockTestCallback); +}; + +TEST_F(TSFTextStoreTest, AsynchronousRequestLockTest) { + AsyncRequestLockTestCallback callback(text_store_.get()); + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &AsyncRequestLockTestCallback::LockGranted1)) + .WillOnce(Invoke(&callback, &AsyncRequestLockTestCallback::LockGranted2)) + .WillOnce(Invoke(&callback, &AsyncRequestLockTestCallback::LockGranted3)) + .WillOnce(Invoke(&callback, &AsyncRequestLockTestCallback::LockGranted4)) + .WillOnce(Invoke(&callback, &AsyncRequestLockTestCallback::LockGranted5)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ, &result)); + EXPECT_EQ(S_OK, result); +} + +class RequestLockTextChangeTestCallback : public TSFTextStoreTestCallback { + public: + explicit RequestLockTextChangeTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store), state_(0) {} + + HRESULT LockGranted1(DWORD flags) { + EXPECT_EQ(0, state_); + state_ = 1; + EXPECT_TRUE(HasReadLock()); + EXPECT_TRUE(HasReadWriteLock()); + + *edit_flag() = true; + SetInternalState(L"012345", 6, 6, 6); + text_spans()->clear(); + + state_ = 2; + return S_OK; + } + + void InsertText(const base::string16& text) { + EXPECT_EQ(2, state_); + EXPECT_EQ(L"012345", text); + state_ = 3; + } + + void SetCompositionText(const ui::CompositionText& composition) { + EXPECT_EQ(3, state_); + EXPECT_EQ(L"", composition.text); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(0u, composition.selection.end()); + EXPECT_EQ(0u, composition.ime_text_spans.size()); + state_ = 4; + } + + HRESULT OnTextChange(DWORD flags, const TS_TEXTCHANGE* change) { + EXPECT_EQ(4, state_); + HRESULT result = kInvalidResult; + state_ = 5; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + EXPECT_EQ(6, state_); + state_ = 7; + return S_OK; + } + + HRESULT LockGranted2(DWORD flags) { + EXPECT_EQ(5, state_); + EXPECT_TRUE(HasReadLock()); + EXPECT_TRUE(HasReadWriteLock()); + state_ = 6; + return S_OK; + } + + private: + int state_; + + DISALLOW_COPY_AND_ASSIGN(RequestLockTextChangeTestCallback); +}; + +TEST_F(TSFTextStoreTest, RequestLockOnTextChangeTest) { + RequestLockTextChangeTestCallback callback(text_store_.get()); + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce( + Invoke(&callback, &RequestLockTextChangeTestCallback::LockGranted1)) + .WillOnce( + Invoke(&callback, &RequestLockTextChangeTestCallback::LockGranted2)); + + EXPECT_CALL(*sink_, OnSelectionChange()).WillOnce(Return(S_OK)); + EXPECT_CALL(*sink_, OnLayoutChange(_, _)).WillOnce(Return(S_OK)); + EXPECT_CALL(*sink_, OnTextChange(_, _)) + .WillOnce( + Invoke(&callback, &RequestLockTextChangeTestCallback::OnTextChange)); + EXPECT_CALL(text_input_client_, InsertText(_)) + .WillOnce( + Invoke(&callback, &RequestLockTextChangeTestCallback::InsertText)); + EXPECT_CALL(text_input_client_, SetCompositionText(_)) + .WillOnce(Invoke(&callback, + &RequestLockTextChangeTestCallback::SetCompositionText)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); +} + +class SelectionTestCallback : public TSFTextStoreTestCallback { + public: + explicit SelectionTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT ReadLockGranted(DWORD flags) { + SetInternalState(L"", 0, 0, 0); + + GetSelectionTest(0, 0); + SetSelectionTest(0, 0, TF_E_NOLOCK); + + SetInternalState(L"012345", 0, 0, 3); + + GetSelectionTest(0, 3); + SetSelectionTest(0, 0, TF_E_NOLOCK); + + return S_OK; + } + + HRESULT ReadWriteLockGranted(DWORD flags) { + SetInternalState(L"", 0, 0, 0); + + SetSelectionTest(0, 0, S_OK); + GetSelectionTest(0, 0); + SetSelectionTest(0, 1, TF_E_INVALIDPOS); + SetSelectionTest(1, 0, TF_E_INVALIDPOS); + SetSelectionTest(1, 1, TF_E_INVALIDPOS); + + SetInternalState(L"0123456", 3, 3, 3); + + SetSelectionTest(0, 0, TF_E_INVALIDPOS); + SetSelectionTest(0, 1, TF_E_INVALIDPOS); + SetSelectionTest(0, 3, TF_E_INVALIDPOS); + SetSelectionTest(0, 6, TF_E_INVALIDPOS); + SetSelectionTest(0, 7, TF_E_INVALIDPOS); + SetSelectionTest(0, 8, TF_E_INVALIDPOS); + + SetSelectionTest(1, 0, TF_E_INVALIDPOS); + SetSelectionTest(1, 1, TF_E_INVALIDPOS); + SetSelectionTest(1, 3, TF_E_INVALIDPOS); + SetSelectionTest(1, 6, TF_E_INVALIDPOS); + SetSelectionTest(1, 7, TF_E_INVALIDPOS); + SetSelectionTest(1, 8, TF_E_INVALIDPOS); + + SetSelectionTest(3, 0, TF_E_INVALIDPOS); + SetSelectionTest(3, 1, TF_E_INVALIDPOS); + SetSelectionTest(3, 3, S_OK); + SetSelectionTest(3, 6, S_OK); + SetSelectionTest(3, 7, S_OK); + SetSelectionTest(3, 8, TF_E_INVALIDPOS); + + SetSelectionTest(6, 0, TF_E_INVALIDPOS); + SetSelectionTest(6, 1, TF_E_INVALIDPOS); + SetSelectionTest(6, 3, TF_E_INVALIDPOS); + SetSelectionTest(6, 6, S_OK); + SetSelectionTest(6, 7, S_OK); + SetSelectionTest(6, 8, TF_E_INVALIDPOS); + + SetSelectionTest(7, 0, TF_E_INVALIDPOS); + SetSelectionTest(7, 1, TF_E_INVALIDPOS); + SetSelectionTest(7, 3, TF_E_INVALIDPOS); + SetSelectionTest(7, 6, TF_E_INVALIDPOS); + SetSelectionTest(7, 7, S_OK); + SetSelectionTest(7, 8, TF_E_INVALIDPOS); + + SetSelectionTest(8, 0, TF_E_INVALIDPOS); + SetSelectionTest(8, 1, TF_E_INVALIDPOS); + SetSelectionTest(8, 3, TF_E_INVALIDPOS); + SetSelectionTest(8, 6, TF_E_INVALIDPOS); + SetSelectionTest(8, 7, TF_E_INVALIDPOS); + SetSelectionTest(8, 8, TF_E_INVALIDPOS); + + return S_OK; + } +}; + +TEST_F(TSFTextStoreTest, SetGetSelectionTest) { + SelectionTestCallback callback(text_store_.get()); + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &SelectionTestCallback::ReadLockGranted)) + .WillOnce( + Invoke(&callback, &SelectionTestCallback::ReadWriteLockGranted)); + + TS_SELECTION_ACP selection_buffer = {}; + ULONG fetched_count = 0; + EXPECT_EQ(TS_E_NOLOCK, + text_store_->GetSelection(0, 1, &selection_buffer, &fetched_count)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ, &result)); + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); +} + +class SetGetTextTestCallback : public TSFTextStoreTestCallback { + public: + explicit SetGetTextTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT ReadLockGranted(DWORD flags) { + SetTextTest(0, 0, L"", TF_E_NOLOCK); + + GetTextTest(0, -1, L"", 0); + GetTextTest(0, 0, L"", 0); + GetTextErrorTest(0, 1, TF_E_INVALIDPOS); + + SetInternalState(L"0123456", 3, 3, 3); + + GetTextErrorTest(-1, -1, TF_E_INVALIDPOS); + GetTextErrorTest(-1, 0, TF_E_INVALIDPOS); + GetTextErrorTest(-1, 1, TF_E_INVALIDPOS); + GetTextErrorTest(-1, 3, TF_E_INVALIDPOS); + GetTextErrorTest(-1, 6, TF_E_INVALIDPOS); + GetTextErrorTest(-1, 7, TF_E_INVALIDPOS); + GetTextErrorTest(-1, 8, TF_E_INVALIDPOS); + + GetTextTest(0, -1, L"0123456", 7); + GetTextTest(0, 0, L"", 0); + GetTextTest(0, 1, L"0", 1); + GetTextTest(0, 3, L"012", 3); + GetTextTest(0, 6, L"012345", 6); + GetTextTest(0, 7, L"0123456", 7); + GetTextErrorTest(0, 8, TF_E_INVALIDPOS); + + GetTextTest(1, -1, L"123456", 7); + GetTextErrorTest(1, 0, TF_E_INVALIDPOS); + GetTextTest(1, 1, L"", 1); + GetTextTest(1, 3, L"12", 3); + GetTextTest(1, 6, L"12345", 6); + GetTextTest(1, 7, L"123456", 7); + GetTextErrorTest(1, 8, TF_E_INVALIDPOS); + + GetTextTest(3, -1, L"3456", 7); + GetTextErrorTest(3, 0, TF_E_INVALIDPOS); + GetTextErrorTest(3, 1, TF_E_INVALIDPOS); + GetTextTest(3, 3, L"", 3); + GetTextTest(3, 6, L"345", 6); + GetTextTest(3, 7, L"3456", 7); + GetTextErrorTest(3, 8, TF_E_INVALIDPOS); + + GetTextTest(6, -1, L"6", 7); + GetTextErrorTest(6, 0, TF_E_INVALIDPOS); + GetTextErrorTest(6, 1, TF_E_INVALIDPOS); + GetTextErrorTest(6, 3, TF_E_INVALIDPOS); + GetTextTest(6, 6, L"", 6); + GetTextTest(6, 7, L"6", 7); + GetTextErrorTest(6, 8, TF_E_INVALIDPOS); + + GetTextTest(7, -1, L"", 7); + GetTextErrorTest(7, 0, TF_E_INVALIDPOS); + GetTextErrorTest(7, 1, TF_E_INVALIDPOS); + GetTextErrorTest(7, 3, TF_E_INVALIDPOS); + GetTextErrorTest(7, 6, TF_E_INVALIDPOS); + GetTextTest(7, 7, L"", 7); + GetTextErrorTest(7, 8, TF_E_INVALIDPOS); + + GetTextErrorTest(8, -1, TF_E_INVALIDPOS); + GetTextErrorTest(8, 0, TF_E_INVALIDPOS); + GetTextErrorTest(8, 1, TF_E_INVALIDPOS); + GetTextErrorTest(8, 3, TF_E_INVALIDPOS); + GetTextErrorTest(8, 6, TF_E_INVALIDPOS); + GetTextErrorTest(8, 7, TF_E_INVALIDPOS); + GetTextErrorTest(8, 8, TF_E_INVALIDPOS); + + return S_OK; + } + + HRESULT ReadWriteLockGranted(DWORD flags) { + SetInternalState(L"", 0, 0, 0); + SetTextTest(0, 0, L"", S_OK); + + SetInternalState(L"", 0, 0, 0); + SetTextTest(0, 1, L"", TS_E_INVALIDPOS); + + SetInternalState(L"0123456", 3, 3, 3); + + SetTextTest(0, 0, L"", TS_E_INVALIDPOS); + SetTextTest(0, 1, L"", TS_E_INVALIDPOS); + SetTextTest(0, 3, L"", TS_E_INVALIDPOS); + SetTextTest(0, 6, L"", TS_E_INVALIDPOS); + SetTextTest(0, 7, L"", TS_E_INVALIDPOS); + SetTextTest(0, 8, L"", TS_E_INVALIDPOS); + + SetTextTest(1, 0, L"", TS_E_INVALIDPOS); + SetTextTest(1, 1, L"", TS_E_INVALIDPOS); + SetTextTest(1, 3, L"", TS_E_INVALIDPOS); + SetTextTest(1, 6, L"", TS_E_INVALIDPOS); + SetTextTest(1, 7, L"", TS_E_INVALIDPOS); + SetTextTest(1, 8, L"", TS_E_INVALIDPOS); + + SetTextTest(3, 0, L"", TS_E_INVALIDPOS); + SetTextTest(3, 1, L"", TS_E_INVALIDPOS); + + SetTextTest(3, 3, L"", S_OK); + GetTextTest(0, -1, L"0123456", 7); + GetSelectionTest(3, 3); + SetInternalState(L"0123456", 3, 3, 3); + + SetTextTest(3, 6, L"", S_OK); + GetTextTest(0, -1, L"0126", 4); + GetSelectionTest(3, 3); + SetInternalState(L"0123456", 3, 3, 3); + + SetTextTest(3, 7, L"", S_OK); + GetTextTest(0, -1, L"012", 3); + GetSelectionTest(3, 3); + SetInternalState(L"0123456", 3, 3, 3); + + SetTextTest(3, 8, L"", TS_E_INVALIDPOS); + + SetTextTest(6, 0, L"", TS_E_INVALIDPOS); + SetTextTest(6, 1, L"", TS_E_INVALIDPOS); + SetTextTest(6, 3, L"", TS_E_INVALIDPOS); + + SetTextTest(6, 6, L"", S_OK); + GetTextTest(0, -1, L"0123456", 7); + GetSelectionTest(6, 6); + SetInternalState(L"0123456", 3, 3, 3); + + SetTextTest(6, 7, L"", S_OK); + GetTextTest(0, -1, L"012345", 6); + GetSelectionTest(6, 6); + SetInternalState(L"0123456", 3, 3, 3); + + SetTextTest(6, 8, L"", TS_E_INVALIDPOS); + + SetTextTest(7, 0, L"", TS_E_INVALIDPOS); + SetTextTest(7, 1, L"", TS_E_INVALIDPOS); + SetTextTest(7, 3, L"", TS_E_INVALIDPOS); + SetTextTest(7, 6, L"", TS_E_INVALIDPOS); + + SetTextTest(7, 7, L"", S_OK); + GetTextTest(0, -1, L"0123456", 7); + GetSelectionTest(7, 7); + SetInternalState(L"0123456", 3, 3, 3); + + SetTextTest(7, 8, L"", TS_E_INVALIDPOS); + + SetInternalState(L"0123456", 3, 3, 3); + SetTextTest(3, 3, L"abc", S_OK); + GetTextTest(0, -1, L"012abc3456", 10); + GetSelectionTest(3, 6); + + SetInternalState(L"0123456", 3, 3, 3); + SetTextTest(3, 6, L"abc", S_OK); + GetTextTest(0, -1, L"012abc6", 7); + GetSelectionTest(3, 6); + + SetInternalState(L"0123456", 3, 3, 3); + SetTextTest(3, 7, L"abc", S_OK); + GetTextTest(0, -1, L"012abc", 6); + GetSelectionTest(3, 6); + + SetInternalState(L"0123456", 3, 3, 3); + SetTextTest(6, 6, L"abc", S_OK); + GetTextTest(0, -1, L"012345abc6", 10); + GetSelectionTest(6, 9); + + SetInternalState(L"0123456", 3, 3, 3); + SetTextTest(6, 7, L"abc", S_OK); + GetTextTest(0, -1, L"012345abc", 9); + GetSelectionTest(6, 9); + + SetInternalState(L"0123456", 3, 3, 3); + SetTextTest(7, 7, L"abc", S_OK); + GetTextTest(0, -1, L"0123456abc", 10); + GetSelectionTest(7, 10); + + return S_OK; + } + + private: + DISALLOW_COPY_AND_ASSIGN(SetGetTextTestCallback); +}; + +TEST_F(TSFTextStoreTest, SetGetTextTest) { + SetGetTextTestCallback callback(text_store_.get()); + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &SetGetTextTestCallback::ReadLockGranted)) + .WillOnce( + Invoke(&callback, &SetGetTextTestCallback::ReadWriteLockGranted)); + + wchar_t buffer[1024] = {}; + ULONG text_buffer_copied = 0; + TS_RUNINFO run_info = {}; + ULONG run_info_buffer_copied = 0; + LONG next_acp = 0; + EXPECT_EQ(TF_E_NOLOCK, text_store_->GetText( + 0, -1, buffer, 1024, &text_buffer_copied, + &run_info, 1, &run_info_buffer_copied, &next_acp)); + TS_TEXTCHANGE change = {}; + EXPECT_EQ(TF_E_NOLOCK, text_store_->SetText(0, 0, 0, L"abc", 3, &change)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ, &result)); + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); +} + +class InsertTextAtSelectionTestCallback : public TSFTextStoreTestCallback { + public: + explicit InsertTextAtSelectionTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT ReadLockGranted(DWORD flags) { + const wchar_t kBuffer[] = L"0123456789"; + + SetInternalState(L"abcedfg", 0, 0, 0); + InsertTextAtSelectionQueryOnlyTest(kBuffer, 10, 0, 0); + GetSelectionTest(0, 0); + InsertTextAtSelectionQueryOnlyTest(kBuffer, 0, 0, 0); + + SetInternalState(L"abcedfg", 0, 2, 5); + InsertTextAtSelectionQueryOnlyTest(kBuffer, 10, 2, 5); + GetSelectionTest(2, 5); + InsertTextAtSelectionQueryOnlyTest(kBuffer, 0, 2, 5); + + LONG start = 0; + LONG end = 0; + TS_TEXTCHANGE change = {}; + EXPECT_EQ(TS_E_NOLOCK, text_store_->InsertTextAtSelection( + 0, kBuffer, 10, &start, &end, &change)); + return S_OK; + } + + HRESULT ReadWriteLockGranted(DWORD flags) { + SetInternalState(L"abcedfg", 0, 0, 0); + + const wchar_t kBuffer[] = L"0123456789"; + InsertTextAtSelectionQueryOnlyTest(kBuffer, 10, 0, 0); + GetSelectionTest(0, 0); + InsertTextAtSelectionQueryOnlyTest(kBuffer, 0, 0, 0); + + SetInternalState(L"", 0, 0, 0); + InsertTextAtSelectionTest(kBuffer, 10, 0, 10, 0, 0, 10); + GetSelectionTest(0, 10); + GetTextTest(0, -1, L"0123456789", 10); + + SetInternalState(L"abcedfg", 0, 0, 0); + InsertTextAtSelectionTest(kBuffer, 10, 0, 10, 0, 0, 10); + GetSelectionTest(0, 10); + GetTextTest(0, -1, L"0123456789abcedfg", 17); + + SetInternalState(L"abcedfg", 0, 0, 3); + InsertTextAtSelectionTest(kBuffer, 0, 0, 0, 0, 3, 0); + GetSelectionTest(0, 0); + GetTextTest(0, -1, L"edfg", 4); + + SetInternalState(L"abcedfg", 0, 3, 7); + InsertTextAtSelectionTest(kBuffer, 10, 3, 13, 3, 7, 13); + GetSelectionTest(3, 13); + GetTextTest(0, -1, L"abc0123456789", 13); + + SetInternalState(L"abcedfg", 0, 7, 7); + InsertTextAtSelectionTest(kBuffer, 10, 7, 17, 7, 7, 17); + GetSelectionTest(7, 17); + GetTextTest(0, -1, L"abcedfg0123456789", 17); + + return S_OK; + } + + private: + DISALLOW_COPY_AND_ASSIGN(InsertTextAtSelectionTestCallback); +}; + +TEST_F(TSFTextStoreTest, InsertTextAtSelectionTest) { + InsertTextAtSelectionTestCallback callback(text_store_.get()); + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, + &InsertTextAtSelectionTestCallback::ReadLockGranted)) + .WillOnce(Invoke( + &callback, &InsertTextAtSelectionTestCallback::ReadWriteLockGranted)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); +} + +class ScenarioTestCallback : public TSFTextStoreTestCallback { + public: + explicit ScenarioTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT LockGranted1(DWORD flags) { + SetSelectionTest(0, 0, S_OK); + + SetTextTest(0, 0, L"abc", S_OK); + SetTextTest(1, 2, L"xyz", S_OK); + + GetTextTest(0, -1, L"axyzc", 5); + + text_spans()->clear(); + ImeTextSpan text_span; + text_span.start_offset = 0; + text_span.end_offset = 5; + text_span.underline_color = SK_ColorBLACK; + text_span.thickness = ImeTextSpan::Thickness::kThin; + text_span.background_color = SK_ColorTRANSPARENT; + text_spans()->push_back(text_span); + *edit_flag() = true; + *committed_size() = 0; + return S_OK; + } + + void SetCompositionText1(const ui::CompositionText& composition) { + EXPECT_EQ(L"axyzc", composition.text); + EXPECT_EQ(1u, composition.selection.start()); + EXPECT_EQ(4u, composition.selection.end()); + ASSERT_EQ(1u, composition.ime_text_spans.size()); + EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[0].underline_color); + EXPECT_EQ(SK_ColorTRANSPARENT, + composition.ime_text_spans[0].background_color); + EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); + EXPECT_EQ(5u, composition.ime_text_spans[0].end_offset); + EXPECT_EQ(ImeTextSpan::Thickness::kThin, + composition.ime_text_spans[0].thickness); + } + + HRESULT LockGranted2(DWORD flags) { + SetTextTest(3, 4, L"ZCP", S_OK); + GetTextTest(0, -1, L"axyZCPc", 7); + + text_spans()->clear(); + ImeTextSpan text_span; + text_span.start_offset = 3; + text_span.end_offset = 5; + text_span.underline_color = SK_ColorBLACK; + text_span.thickness = ImeTextSpan::Thickness::kThick; + text_spans()->push_back(text_span); + text_span.start_offset = 5; + text_span.end_offset = 7; + text_span.underline_color = SK_ColorBLACK; + text_span.thickness = ImeTextSpan::Thickness::kThin; + text_spans()->push_back(text_span); + + *edit_flag() = true; + *committed_size() = 3; + + return S_OK; + } + + void InsertText2(const base::string16& text) { EXPECT_EQ(L"axy", text); } + + void SetCompositionText2(const ui::CompositionText& composition) { + EXPECT_EQ(L"ZCPc", composition.text); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(3u, composition.selection.end()); + ASSERT_EQ(2u, composition.ime_text_spans.size()); + EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[0].underline_color); + EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); + EXPECT_EQ(2u, composition.ime_text_spans[0].end_offset); + EXPECT_EQ(ImeTextSpan::Thickness::kThick, + composition.ime_text_spans[0].thickness); + EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[1].underline_color); + EXPECT_EQ(2u, composition.ime_text_spans[1].start_offset); + EXPECT_EQ(4u, composition.ime_text_spans[1].end_offset); + EXPECT_EQ(ImeTextSpan::Thickness::kThin, + composition.ime_text_spans[1].thickness); + } + + HRESULT LockGranted3(DWORD flags) { + GetTextTest(0, -1, L"axyZCPc", 7); + + text_spans()->clear(); + *edit_flag() = true; + *committed_size() = 7; + + return S_OK; + } + + void InsertText3(const base::string16& text) { EXPECT_EQ(L"ZCPc", text); } + + void SetCompositionText3(const ui::CompositionText& composition) { + EXPECT_EQ(L"", composition.text); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(0u, composition.selection.end()); + EXPECT_EQ(0u, composition.ime_text_spans.size()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScenarioTestCallback); +}; + +TEST_F(TSFTextStoreTest, ScenarioTest) { + ScenarioTestCallback callback(text_store_.get()); + EXPECT_CALL(text_input_client_, SetCompositionText(_)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::SetCompositionText1)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::SetCompositionText2)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::SetCompositionText3)); + + EXPECT_CALL(text_input_client_, InsertText(_)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::InsertText2)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::InsertText3)); + + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::LockGranted1)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::LockGranted2)) + .WillOnce(Invoke(&callback, &ScenarioTestCallback::LockGranted3)); + + // OnSelectionChange will be called once after LockGranted3(). + EXPECT_CALL(*sink_, OnSelectionChange()).WillOnce(Return(S_OK)); + + // OnLayoutChange will be called once after LockGranted3(). + EXPECT_CALL(*sink_, OnLayoutChange(_, _)).WillOnce(Return(S_OK)); + + // OnTextChange will be called once after LockGranted3(). + EXPECT_CALL(*sink_, OnTextChange(_, _)).WillOnce(Return(S_OK)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); +} + +class GetTextExtTestCallback : public TSFTextStoreTestCallback { + public: + explicit GetTextExtTestCallback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store), + layout_prepared_character_num_(0) {} + + HRESULT LockGranted(DWORD flags) { + SetInternalState(L"0123456789012", 0, 0, 0); + layout_prepared_character_num_ = 13; + + TsViewCookie view_cookie = 0; + EXPECT_EQ(S_OK, text_store_->GetActiveView(&view_cookie)); + GetTextExtTest(view_cookie, 0, 0, 11, 12, 11, 20); + GetTextExtTest(view_cookie, 0, 1, 11, 12, 20, 20); + GetTextExtTest(view_cookie, 0, 2, 11, 12, 30, 20); + GetTextExtTest(view_cookie, 9, 9, 100, 12, 100, 20); + GetTextExtTest(view_cookie, 9, 10, 101, 12, 110, 20); + GetTextExtTest(view_cookie, 10, 10, 110, 12, 110, 20); + GetTextExtTest(view_cookie, 11, 11, 20, 112, 20, 120); + GetTextExtTest(view_cookie, 11, 12, 21, 112, 30, 120); + GetTextExtTest(view_cookie, 9, 12, 101, 12, 30, 120); + GetTextExtTest(view_cookie, 9, 13, 101, 12, 40, 120); + GetTextExtTest(view_cookie, 0, 13, 11, 12, 40, 120); + GetTextExtTest(view_cookie, 13, 13, 40, 112, 40, 120); + + layout_prepared_character_num_ = 12; + GetTextExtNoLayoutTest(view_cookie, 13, 13); + + layout_prepared_character_num_ = 0; + GetTextExtNoLayoutTest(view_cookie, 0, 0); + + SetInternalState(L"", 0, 0, 0); + GetTextExtTest(view_cookie, 0, 0, 1, 2, 4, 6); + + // Last character is not availabe due to timing issue of async API. + // In this case, we will get first character bounds instead of whole text + // bounds. + SetInternalState(L"abc", 0, 0, 3); + layout_prepared_character_num_ = 2; + GetTextExtTest(view_cookie, 0, 0, 11, 12, 11, 20); + + // TODO(nona, kinaba): Remove following test case after PPAPI supporting + // GetCompositionCharacterBounds. + SetInternalState(L"a", 0, 0, 1); + layout_prepared_character_num_ = 0; + GetTextExtTest(view_cookie, 0, 1, 1, 2, 4, 6); + return S_OK; + } + + bool GetCompositionCharacterBounds(uint32_t index, gfx::Rect* rect) { + if (index >= layout_prepared_character_num_) + return false; + rect->set_x((index % 10) * 10 + 11); + rect->set_y((index / 10) * 100 + 12); + rect->set_width(9); + rect->set_height(8); + return true; + } + + gfx::Rect GetCaretBounds() { return gfx::Rect(1, 2, 3, 4); } + + private: + uint32_t layout_prepared_character_num_; + + DISALLOW_COPY_AND_ASSIGN(GetTextExtTestCallback); +}; + +TEST_F(TSFTextStoreTest, GetTextExtTest) { + GetTextExtTestCallback callback(text_store_.get()); + EXPECT_CALL(text_input_client_, GetCaretBounds()) + .WillRepeatedly( + Invoke(&callback, &GetTextExtTestCallback::GetCaretBounds)); + + EXPECT_CALL(text_input_client_, GetCompositionCharacterBounds(_, _)) + .WillRepeatedly(Invoke( + &callback, &GetTextExtTestCallback::GetCompositionCharacterBounds)); + + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &GetTextExtTestCallback::LockGranted)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READ, &result)); + EXPECT_EQ(S_OK, result); +} + +TEST_F(TSFTextStoreTest, RequestSupportedAttrs) { + EXPECT_CALL(text_input_client_, GetTextInputType()) + .WillRepeatedly(Return(TEXT_INPUT_TYPE_TEXT)); + EXPECT_CALL(text_input_client_, GetTextInputMode()) + .WillRepeatedly(Return(TEXT_INPUT_MODE_DEFAULT)); + + EXPECT_HRESULT_FAILED(text_store_->RequestSupportedAttrs(0, 1, nullptr)); + + const TS_ATTRID kUnknownAttributes[] = {GUID_NULL}; + EXPECT_HRESULT_FAILED(text_store_->RequestSupportedAttrs( + 0, arraysize(kUnknownAttributes), kUnknownAttributes)) + << "Must fail for unknown attributes"; + + const TS_ATTRID kAttributes[] = {GUID_NULL, GUID_PROP_INPUTSCOPE, GUID_NULL}; + EXPECT_EQ(S_OK, text_store_->RequestSupportedAttrs(0, arraysize(kAttributes), + kAttributes)) + << "InputScope must be supported"; + + { + SCOPED_TRACE("Check if RequestSupportedAttrs fails while focus is lost"); + // Emulate focus lost + text_store_->SetFocusedTextInputClient(nullptr, nullptr); + EXPECT_HRESULT_FAILED(text_store_->RequestSupportedAttrs(0, 0, nullptr)); + EXPECT_HRESULT_FAILED(text_store_->RequestSupportedAttrs( + 0, arraysize(kAttributes), kAttributes)); + } +} + +TEST_F(TSFTextStoreTest, RetrieveRequestedAttrs) { + EXPECT_CALL(text_input_client_, GetTextInputType()) + .WillRepeatedly(Return(TEXT_INPUT_TYPE_TEXT)); + EXPECT_CALL(text_input_client_, GetTextInputMode()) + .WillRepeatedly(Return(TEXT_INPUT_MODE_DEFAULT)); + + ULONG num_copied = 0xfffffff; + EXPECT_HRESULT_FAILED( + text_store_->RetrieveRequestedAttrs(1, nullptr, &num_copied)); + + { + SCOPED_TRACE("Make sure if InputScope is supported"); + TS_ATTRVAL buffer[2] = {}; + num_copied = 0xfffffff; + ASSERT_EQ(S_OK, text_store_->RetrieveRequestedAttrs(arraysize(buffer), + buffer, &num_copied)); + bool input_scope_found = false; + for (size_t i = 0; i < num_copied; ++i) { + base::win::ScopedVariant variant; + // Move ownership from |buffer[i].varValue| to |variant|. + std::swap(*variant.Receive(), buffer[i].varValue); + if (IsEqualGUID(buffer[i].idAttr, GUID_PROP_INPUTSCOPE)) { + EXPECT_EQ(VT_UNKNOWN, variant.type()); + Microsoft::WRL::ComPtr<ITfInputScope> input_scope; + EXPECT_HRESULT_SUCCEEDED(variant.AsInput()->punkVal->QueryInterface( + IID_PPV_ARGS(&input_scope))); + input_scope_found = true; + // we do not break here to clean up all the retrieved VARIANTs. + } + } + EXPECT_TRUE(input_scope_found); + } + { + SCOPED_TRACE("Check if RetrieveRequestedAttrs fails while focus is lost"); + // Emulate focus lost + text_store_->SetFocusedTextInputClient(nullptr, nullptr); + num_copied = 0xfffffff; + TS_ATTRVAL buffer[2] = {}; + EXPECT_HRESULT_FAILED(text_store_->RetrieveRequestedAttrs( + arraysize(buffer), buffer, &num_copied)); + } +} + +} // namespace +} // namespace ui diff --git a/chromium/ui/base/l10n/l10n_util_unittest.cc b/chromium/ui/base/l10n/l10n_util_unittest.cc index 2bfd8c8ad24..35dc4754b70 100644 --- a/chromium/ui/base/l10n/l10n_util_unittest.cc +++ b/chromium/ui/base/l10n/l10n_util_unittest.cc @@ -12,7 +12,6 @@ #include "base/i18n/rtl.h" #include "base/i18n/time_formatting.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/path_service.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/ui/base/material_design/material_design_controller.cc b/chromium/ui/base/material_design/material_design_controller.cc index e19300ed6e1..5abb641690c 100644 --- a/chromium/ui/base/material_design/material_design_controller.cc +++ b/chromium/ui/base/material_design/material_design_controller.cc @@ -14,17 +14,14 @@ #include "ui/base/ui_base_switches.h" #if defined(OS_CHROMEOS) -#include "ui/base/touch/touch_device.h" -#include "ui/events/devices/device_data_manager.h" - -#if defined(USE_OZONE) #include <fcntl.h> #include "base/files/file_enumerator.h" +#include "base/files/scoped_file.h" #include "base/threading/thread_restrictions.h" +#include "ui/base/touch/touch_device.h" +#include "ui/events/devices/device_data_manager.h" #include "ui/events/ozone/evdev/event_device_info.h" // nogncheck -#endif // defined(USE_OZONE) - #endif // defined(OS_CHROMEOS) #if defined(OS_WIN) @@ -33,6 +30,48 @@ #endif namespace ui { +namespace { + +#if defined(OS_CHROMEOS) + +// Whether to use MATERIAL_TOUCH_OPTIMIZED when a touch device is detected. +// Enabled by default on ChromeOS. +const base::Feature kTouchOptimizedUi = {"TouchOptimizedUi", + base::FEATURE_ENABLED_BY_DEFAULT}; + +MaterialDesignController::Mode GetDefaultTouchDeviceMode() { + return base::FeatureList::IsEnabled(kTouchOptimizedUi) + ? MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED + : MaterialDesignController::MATERIAL_HYBRID; +} + +bool HasTouchscreen() { + // If a scan of available devices has already completed, use that. + if (DeviceDataManager::HasInstance() && + DeviceDataManager::GetInstance()->AreDeviceListsComplete()) + return GetTouchScreensAvailability() == TouchScreensAvailability::ENABLED; + + // Otherwise perform our own scan to determine the presence of a touchscreen. + // Note this is a one-time call that occurs during device startup or restart. + base::FileEnumerator file_enum( + base::FilePath(FILE_PATH_LITERAL("/dev/input")), false, + base::FileEnumerator::FILES, FILE_PATH_LITERAL("event*[0-9]")); + for (base::FilePath path = file_enum.Next(); !path.empty(); + path = file_enum.Next()) { + EventDeviceInfo devinfo; + base::ScopedFD fd( + open(path.value().c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC)); + if (fd.is_valid() && devinfo.Initialize(fd.get(), path) && + devinfo.HasTouchscreen()) + return true; + } + + return false; +} + +#endif // OS_CHROMEOS + +} // namespace bool MaterialDesignController::is_mode_initialized_ = false; @@ -53,6 +92,8 @@ void MaterialDesignController::Initialize() { SetMode(MATERIAL_HYBRID); } else if (switch_value == switches::kTopChromeMDMaterialTouchOptimized) { SetMode(MATERIAL_TOUCH_OPTIMIZED); + } else if (switch_value == switches::kTopChromeMDMaterialRefresh) { + SetMode(MATERIAL_REFRESH); } else if (switch_value == switches::kTopChromeMDMaterialAuto) { #if defined(OS_WIN) // TODO(girard): add support for switching between modes when @@ -79,7 +120,8 @@ MaterialDesignController::Mode MaterialDesignController::GetMode() { // static bool MaterialDesignController::IsSecondaryUiMaterial() { - return base::FeatureList::IsEnabled(features::kSecondaryUiMd); + return base::FeatureList::IsEnabled(features::kSecondaryUiMd) || + GetMode() == MATERIAL_REFRESH; } // static @@ -88,37 +130,18 @@ bool MaterialDesignController::IsTouchOptimizedUiEnabled() { } // static +bool MaterialDesignController::IsNewerMaterialUi() { + return IsTouchOptimizedUiEnabled() || GetMode() == MATERIAL_REFRESH; +} + +// static MaterialDesignController::Mode MaterialDesignController::DefaultMode() { #if defined(OS_CHROMEOS) - // If a scan of available devices has already completed, use material-hybrid - // if a touchscreen is present. - if (DeviceDataManager::HasInstance() && - DeviceDataManager::GetInstance()->AreDeviceListsComplete()) { - return GetTouchScreensAvailability() == TouchScreensAvailability::ENABLED - ? MATERIAL_HYBRID - : MATERIAL_NORMAL; - } - -#if defined(USE_OZONE) - // Otherwise perform our own scan to determine the presence of a touchscreen. - // Note this is a one-time call that occurs during device startup or restart. - base::ThreadRestrictions::ScopedAllowIO allow_io; - base::FileEnumerator file_enum( - base::FilePath(FILE_PATH_LITERAL("/dev/input")), false, - base::FileEnumerator::FILES, FILE_PATH_LITERAL("event*[0-9]")); - for (base::FilePath path = file_enum.Next(); !path.empty(); - path = file_enum.Next()) { - EventDeviceInfo devinfo; - int fd = open(path.value().c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC); - if (fd >= 0) { - if (devinfo.Initialize(fd, path) && devinfo.HasTouchscreen()) { - close(fd); - return MATERIAL_HYBRID; - } - close(fd); - } - } -#endif // defined(USE_OZONE) + // This is called (once) early in device startup to initialize core UI, so + // the UI thread should be blocked to perform the device query. + base::ScopedAllowBlocking allow_io; + if (HasTouchscreen()) + return GetDefaultTouchDeviceMode(); #endif // defined(OS_CHROMEOS) return MATERIAL_NORMAL; diff --git a/chromium/ui/base/material_design/material_design_controller.h b/chromium/ui/base/material_design/material_design_controller.h index 731b304ad51..d9612eabcbb 100644 --- a/chromium/ui/base/material_design/material_design_controller.h +++ b/chromium/ui/base/material_design/material_design_controller.h @@ -25,7 +25,9 @@ class UI_BASE_EXPORT MaterialDesignController { // Material design targeted at mouse/touch hybrid devices. MATERIAL_HYBRID = 1, // Material design that is more optimized for touch devices. - MATERIAL_TOUCH_OPTIMIZED = 2 + MATERIAL_TOUCH_OPTIMIZED = 2, + // Material Refresh design targeted at mouse devices. + MATERIAL_REFRESH = 3, }; // Initializes |mode_|. Must be called before checking |mode_|. @@ -38,9 +40,12 @@ class UI_BASE_EXPORT MaterialDesignController { // should be extended to cover secondary UI. static bool IsSecondaryUiMaterial(); - // Returns true if the touch-optimized UI material design mode is enabled; + // 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 the per-platform default material design variant. static Mode DefaultMode(); diff --git a/chromium/ui/base/models/list_model.h b/chromium/ui/base/models/list_model.h index d55719c3b83..d8abe8a630e 100644 --- a/chromium/ui/base/models/list_model.h +++ b/chromium/ui/base/models/list_model.h @@ -13,7 +13,6 @@ #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/observer_list.h" #include "ui/base/models/list_model_observer.h" diff --git a/chromium/ui/base/models/list_model_unittest.cc b/chromium/ui/base/models/list_model_unittest.cc index 38ec54040c0..b5917bf2afc 100644 --- a/chromium/ui/base/models/list_model_unittest.cc +++ b/chromium/ui/base/models/list_model_unittest.cc @@ -10,7 +10,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace ui { diff --git a/chromium/ui/base/models/simple_menu_model.cc b/chromium/ui/base/models/simple_menu_model.cc index 081f550689f..b7b34292c48 100644 --- a/chromium/ui/base/models/simple_menu_model.cc +++ b/chromium/ui/base/models/simple_menu_model.cc @@ -83,6 +83,20 @@ void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) { AddItem(command_id, l10n_util::GetStringUTF16(string_id)); } +void SimpleMenuModel::AddItemWithIcon(int command_id, + const base::string16& label, + const gfx::ImageSkia& icon) { + Item item(command_id, TYPE_COMMAND, label); + item.icon = gfx::Image(icon); + AppendItem(std::move(item)); +} + +void SimpleMenuModel::AddItemWithStringIdAndIcon(int command_id, + int string_id, + const gfx::ImageSkia& icon) { + AddItemWithIcon(command_id, l10n_util::GetStringUTF16(string_id), icon); +} + void SimpleMenuModel::AddCheckItem(int command_id, const base::string16& label) { AppendItem(Item(command_id, TYPE_CHECK, label)); @@ -386,8 +400,8 @@ void SimpleMenuModel::MenuWillClose() { // called after this. It's more convenient for the delegate to be called // afterwards though, so post a task. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&SimpleMenuModel::OnMenuClosed, method_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&SimpleMenuModel::OnMenuClosed, + method_factory_.GetWeakPtr())); } void SimpleMenuModel::SetMenuModelDelegate( diff --git a/chromium/ui/base/models/simple_menu_model.h b/chromium/ui/base/models/simple_menu_model.h index 72bf5e0907e..ecc620a073a 100644 --- a/chromium/ui/base/models/simple_menu_model.h +++ b/chromium/ui/base/models/simple_menu_model.h @@ -75,6 +75,12 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { // Methods for adding items to the model. void AddItem(int command_id, const base::string16& label); void AddItemWithStringId(int command_id, int string_id); + void AddItemWithIcon(int command_id, + const base::string16& label, + const gfx::ImageSkia& icon); + void AddItemWithStringIdAndIcon(int command_id, + int string_id, + const gfx::ImageSkia& icon); void AddCheckItem(int command_id, const base::string16& label); void AddCheckItemWithStringId(int command_id, int string_id); void AddRadioItem(int command_id, const base::string16& label, int group_id); diff --git a/chromium/ui/base/models/tree_node_iterator_unittest.cc b/chromium/ui/base/models/tree_node_iterator_unittest.cc index a986c5de684..b1c09f506f5 100644 --- a/chromium/ui/base/models/tree_node_iterator_unittest.cc +++ b/chromium/ui/base/models/tree_node_iterator_unittest.cc @@ -5,7 +5,6 @@ #include "ui/base/models/tree_node_iterator.h" #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/models/tree_node_model.h" diff --git a/chromium/ui/base/models/tree_node_model_unittest.cc b/chromium/ui/base/models/tree_node_model_unittest.cc index 0649f65670b..b39d82bccf3 100644 --- a/chromium/ui/base/models/tree_node_model_unittest.cc +++ b/chromium/ui/base/models/tree_node_model_unittest.cc @@ -8,7 +8,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/string16.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/ui/base/mojo/DEPS b/chromium/ui/base/mojo/DEPS index e0c12f73402..0e01cbe71d1 100644 --- a/chromium/ui/base/mojo/DEPS +++ b/chromium/ui/base/mojo/DEPS @@ -1,4 +1,4 @@ include_rules = [ "+mojo/public/cpp/bindings", - "+third_party/WebKit/public/mojom/clipboard/clipboard.mojom-shared.h" + "+third_party/blink/public/mojom/clipboard/clipboard.mojom-shared.h" ] diff --git a/chromium/ui/base/mojo/clipboard.typemap b/chromium/ui/base/mojo/clipboard.typemap index ebd328b5ad3..5f9f30d5182 100644 --- a/chromium/ui/base/mojo/clipboard.typemap +++ b/chromium/ui/base/mojo/clipboard.typemap @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -mojom = "//third_party/WebKit/public/mojom/clipboard/clipboard.mojom" +mojom = "//third_party/blink/public/mojom/clipboard/clipboard.mojom" public_headers = [ "//ui/base/clipboard/clipboard_types.h" ] traits_headers = [ "//ui/base/mojo/clipboard_struct_traits.h" ] deps = [ diff --git a/chromium/ui/base/mojo/clipboard_struct_traits.h b/chromium/ui/base/mojo/clipboard_struct_traits.h index 280a1f2bc97..fb3f50106b1 100644 --- a/chromium/ui/base/mojo/clipboard_struct_traits.h +++ b/chromium/ui/base/mojo/clipboard_struct_traits.h @@ -5,7 +5,7 @@ #ifndef UI_BASE_MOJO_CLIPBOARD_STRUCT_TRAITS_H_ #define UI_BASE_MOJO_CLIPBOARD_STRUCT_TRAITS_H_ -#include "third_party/WebKit/public/mojom/clipboard/clipboard.mojom-shared.h" +#include "third_party/blink/public/mojom/clipboard/clipboard.mojom-shared.h" #include "ui/base/clipboard/clipboard_types.h" namespace mojo { diff --git a/chromium/ui/base/nine_image_painter_factory.cc b/chromium/ui/base/nine_image_painter_factory.cc index 12f59535e3d..57d41f58822 100644 --- a/chromium/ui/base/nine_image_painter_factory.cc +++ b/chromium/ui/base/nine_image_painter_factory.cc @@ -6,7 +6,6 @@ #include <stddef.h> -#include "base/memory/ptr_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/nine_image_painter.h" diff --git a/chromium/ui/base/resource/data_pack.cc b/chromium/ui/base/resource/data_pack.cc index 170fa1d05d3..f6cbde5fcc5 100644 --- a/chromium/ui/base/resource/data_pack.cc +++ b/chromium/ui/base/resource/data_pack.cc @@ -14,7 +14,6 @@ #include "base/files/memory_mapped_file.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/ref_counted_memory.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" diff --git a/chromium/ui/base/resource/resource_bundle.cc b/chromium/ui/base/resource/resource_bundle.cc index 3bfd3cd883b..bc8da0f069e 100644 --- a/chromium/ui/base/resource/resource_bundle.cc +++ b/chromium/ui/base/resource/resource_bundle.cc @@ -16,7 +16,6 @@ #include "base/files/file_util.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/ref_counted_memory.h" #include "base/path_service.h" #include "base/stl_util.h" @@ -214,10 +213,8 @@ void ResourceBundle::InitSharedInstanceWithPakPath(const base::FilePath& path) { // static void ResourceBundle::CleanupSharedInstance() { - if (g_shared_instance_) { - delete g_shared_instance_; - g_shared_instance_ = NULL; - } + delete g_shared_instance_; + g_shared_instance_ = NULL; } // static diff --git a/chromium/ui/base/resource/resource_bundle_android.cc b/chromium/ui/base/resource/resource_bundle_android.cc index a91fdabe964..2ebf50bab98 100644 --- a/chromium/ui/base/resource/resource_bundle_android.cc +++ b/chromium/ui/base/resource/resource_bundle_android.cc @@ -8,7 +8,6 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/path_service.h" #include "jni/ResourceBundle_jni.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chromium/ui/base/resource/resource_bundle_win.cc b/chromium/ui/base/resource/resource_bundle_win.cc index 54fca6fa0c6..d9f74a5656e 100644 --- a/chromium/ui/base/resource/resource_bundle_win.cc +++ b/chromium/ui/base/resource/resource_bundle_win.cc @@ -5,7 +5,6 @@ #include "ui/base/resource/resource_bundle_win.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/path_service.h" #include "base/strings/utf_string_conversions.h" #include "skia/ext/image_operations.h" diff --git a/chromium/ui/base/ui_base_features.cc b/chromium/ui/base/ui_base_features.cc index 3e1850d3f45..6de12e2caf4 100644 --- a/chromium/ui/base/ui_base_features.cc +++ b/chromium/ui/base/ui_base_features.cc @@ -12,10 +12,23 @@ namespace features { +// If enabled, the emoji picker context menu item may be shown for editable +// text areas. +const base::Feature kEnableEmojiContextMenu{"EnableEmojiContextMenu", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables the floating virtual keyboard behavior. const base::Feature kEnableFloatingVirtualKeyboard = { "enable-floating-virtual-keyboard", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables the full screen handwriting virtual keyboard behavior. +const base::Feature kEnableFullscreenHandwritingVirtualKeyboard = { + "enable-fullscreen-handwriting-virtual-keyboard", + base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kEnableStylusVirtualKeyboard = { + "enable-stylus-virtual-keyboard", base::FEATURE_DISABLED_BY_DEFAULT}; + // Applies the material design mode to elements throughout Chrome (not just top // Chrome). const base::Feature kSecondaryUiMd = {"SecondaryUiMd", @@ -45,11 +58,22 @@ const base::Feature kDirectManipulationStylus = { // Enables using WM_POINTER instead of WM_TOUCH for touch events. const base::Feature kPointerEventsForTouch = {"PointerEventsForTouch", base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables using TSF (over IMM32) for IME. +const base::Feature kTSFImeSupport = {"TSFImeSupport", + base::FEATURE_DISABLED_BY_DEFAULT}; bool IsUsingWMPointerForTouch() { return base::win::GetVersion() >= base::win::VERSION_WIN8 && base::FeatureList::IsEnabled(kPointerEventsForTouch); } + +// Enables DirectManipulation API for processing Precision Touchpad events. +const base::Feature kPrecisionTouchpad{"PrecisionTouchpad", + base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables Swipe left/right to navigation back/forward API for processing +// Precision Touchpad events. +const base::Feature kPrecisionTouchpadScrollPhase{ + "PrecisionTouchpadScrollPhase", base::FEATURE_ENABLED_BY_DEFAULT}; #endif // defined(OS_WIN) // Used to have ash run in its own process. This implicitly turns on the @@ -69,4 +93,28 @@ bool IsMusEnabled() { #endif } +#if defined(OS_MACOSX) +// When enabled, the NSWindows for apps will be created in the app's process, +// and will forward input to the browser process. +const base::Feature kHostWindowsInAppShimProcess{ + "HostWindowsInAppShimProcess", base::FEATURE_DISABLED_BY_DEFAULT}; + +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_DISABLED_BY_DEFAULT}; + +// Returns whether a Views-capable browser build should use the Cocoa browser +// UI. +bool IsViewsBrowserCocoa() { + return !base::FeatureList::IsEnabled(kViewsBrowserWindows); +} +#endif // BUILDFLAG(MAC_VIEWS_BROWSER) +#endif // defined(OS_MACOSX) + } // namespace features diff --git a/chromium/ui/base/ui_base_features.h b/chromium/ui/base/ui_base_features.h index acff7278164..d5337023507 100644 --- a/chromium/ui/base/ui_base_features.h +++ b/chromium/ui/base/ui_base_features.h @@ -8,11 +8,16 @@ #include "base/feature_list.h" #include "build/build_config.h" #include "ui/base/ui_base_export.h" +#include "ui/base/ui_features.h" namespace features { // Keep sorted! +UI_BASE_EXPORT extern const base::Feature kEnableEmojiContextMenu; UI_BASE_EXPORT extern const base::Feature kEnableFloatingVirtualKeyboard; +UI_BASE_EXPORT extern const base::Feature + kEnableFullscreenHandwritingVirtualKeyboard; +UI_BASE_EXPORT extern const base::Feature kEnableStylusVirtualKeyboard; UI_BASE_EXPORT extern const base::Feature kSecondaryUiMd; UI_BASE_EXPORT extern const base::Feature kTouchableAppContextMenu; @@ -21,6 +26,9 @@ UI_BASE_EXPORT bool IsTouchableAppContextMenuEnabled(); #if defined(OS_WIN) UI_BASE_EXPORT extern const base::Feature kDirectManipulationStylus; UI_BASE_EXPORT extern const base::Feature kPointerEventsForTouch; +UI_BASE_EXPORT extern const base::Feature kPrecisionTouchpad; +UI_BASE_EXPORT extern const base::Feature kPrecisionTouchpadScrollPhase; +UI_BASE_EXPORT extern const base::Feature kTSFImeSupport; // Returns true if the system should use WM_POINTER events for touch events. UI_BASE_EXPORT bool IsUsingWMPointerForTouch(); @@ -38,6 +46,20 @@ UI_BASE_EXPORT extern const base::Feature kMus; // TODO(sky): rename this to IsWindowServiceEnabled(). UI_BASE_EXPORT bool IsMusEnabled(); +#if defined(OS_MACOSX) +// 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) + } // 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 254140166bc..c8d658d43fa 100644 --- a/chromium/ui/base/ui_base_switches.cc +++ b/chromium/ui/base/ui_base_switches.cc @@ -14,6 +14,9 @@ const char kDisableAVFoundationOverlays[] = "disable-avfoundation-overlays"; // based overlay display path. const char kDisableMacOverlays[] = "disable-mac-overlays"; +// Disable animations for showing and hiding modal dialogs. +const char kDisableModalAnimations[] = "disable-modal-animations"; + // Disable use of cross-process CALayers to display content directly from the // GPU process on Mac. const char kDisableRemoteCoreAnimation[] = "disable-remote-core-animation"; @@ -78,8 +81,9 @@ const char kTopChromeMDMaterialHybrid[] = "material-hybrid"; // |kTopChromeMD| switch. const char kTopChromeMDMaterialTouchOptimized[] = "material-touch-optimized"; -// Classic, non-material, mode for the |kTopChromeMD| switch. -const char kTopChromeMDNonMaterial[] = "non-material"; +// Material design mode that represents a refresh of the Chrome UI for the +// |kTopChromeMD| switch. +const char kTopChromeMDMaterialRefresh[] = "material-refresh"; // Disable partial swap which is needed for some OpenGL drivers / emulators. const char kUIDisablePartialSwap[] = "ui-disable-partial-swap"; @@ -96,6 +100,11 @@ const char kUIDisablePartialSwap[] = "ui-disable-partial-swap"; // Red: Overdrawn four or more times. const char kShowOverdrawFeedback[] = "show-overdraw-feedback"; +// Use Skia Deferred Display List, with this option, SkiaRenderer will record +// frames to Skia DDLs and play them back on the GPU thread. This flag is only +// be effective with --use-skia-renderer. +const char kUseSkiaDeferredDisplayList[] = "use-skia-deferred-display-list"; + // Use SkiaRenderer instead of GLRenderer for direct rendering. const char kUseSkiaRenderer[] = "use-skia-renderer"; diff --git a/chromium/ui/base/ui_base_switches.h b/chromium/ui/base/ui_base_switches.h index 2ce580331e3..22a9f4983d7 100644 --- a/chromium/ui/base/ui_base_switches.h +++ b/chromium/ui/base/ui_base_switches.h @@ -15,6 +15,7 @@ namespace switches { #if defined(OS_MACOSX) && !defined(OS_IOS) UI_BASE_EXPORT extern const char kDisableAVFoundationOverlays[]; UI_BASE_EXPORT extern const char kDisableMacOverlays[]; +UI_BASE_EXPORT extern const char kDisableModalAnimations[]; UI_BASE_EXPORT extern const char kDisableRemoteCoreAnimation[]; UI_BASE_EXPORT extern const char kShowMacOverlayBorders[]; #endif @@ -38,8 +39,9 @@ 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 kTopChromeMDNonMaterial[]; +UI_BASE_EXPORT extern const char kTopChromeMDMaterialRefresh[]; UI_BASE_EXPORT extern const char kUIDisablePartialSwap[]; +UI_BASE_EXPORT extern const char kUseSkiaDeferredDisplayList[]; UI_BASE_EXPORT extern const char kUseSkiaRenderer[]; // Test related. diff --git a/chromium/ui/base/ui_features.gni b/chromium/ui/base/ui_features.gni index 806ef9505ad..25f99803ab7 100644 --- a/chromium/ui/base/ui_features.gni +++ b/chromium/ui/base/ui_features.gni @@ -9,7 +9,7 @@ declare_args() { use_xkbcommon = false # Whether the entire browser uses toolkit-views on Mac instead of Cocoa. - mac_views_browser = false + mac_views_browser = is_mac # Whether the platform provides a native accessibility toolkit. has_native_accessibility = use_atk || is_win || is_mac @@ -20,11 +20,6 @@ declare_args() { # Set to true to if mus (aka the UI service) is enabled. Use the features kMus # (or kMash in chrome code) to start in mus/mash. enable_mus = is_chromeos - - # Optimize parts of Chrome's UI written with web technologies (HTML/CSS/JS) - # for runtime performance purposes. This does more work at compile time for - # speed benefits at runtime (so we skip in debug builds). - optimize_webui = !is_debug } enable_hidpi = is_mac || is_win || is_linux diff --git a/chromium/ui/base/user_activity/user_activity_detector.cc b/chromium/ui/base/user_activity/user_activity_detector.cc index a163830f657..c45121d2c9e 100644 --- a/chromium/ui/base/user_activity/user_activity_detector.cc +++ b/chromium/ui/base/user_activity/user_activity_detector.cc @@ -51,15 +51,15 @@ UserActivityDetector::UserActivityDetector() { CHECK(!g_instance); g_instance = this; - ui::PlatformEventSource* platform_event_source = - ui::PlatformEventSource::GetInstance(); + PlatformEventSource* platform_event_source = + PlatformEventSource::GetInstance(); if (platform_event_source) platform_event_source->AddPlatformEventObserver(this); } UserActivityDetector::~UserActivityDetector() { - ui::PlatformEventSource* platform_event_source = - ui::PlatformEventSource::GetInstance(); + PlatformEventSource* platform_event_source = + PlatformEventSource::GetInstance(); if (platform_event_source) platform_event_source->RemovePlatformEventObserver(this); g_instance = nullptr; diff --git a/chromium/ui/base/user_activity/user_activity_detector.h b/chromium/ui/base/user_activity/user_activity_detector.h index dab5bc407a0..c12fbd4868c 100644 --- a/chromium/ui/base/user_activity/user_activity_detector.h +++ b/chromium/ui/base/user_activity/user_activity_detector.h @@ -18,7 +18,7 @@ namespace ui { class UserActivityObserver; // Watches for input events and notifies observers that the user is active. -class UI_BASE_EXPORT UserActivityDetector : public ui::PlatformEventObserver { +class UI_BASE_EXPORT UserActivityDetector : public PlatformEventObserver { public: // Minimum amount of time between notifications to observers. static const int kNotifyIntervalMs; @@ -49,7 +49,7 @@ class UI_BASE_EXPORT UserActivityDetector : public ui::PlatformEventObserver { // PlatformEventSource (e.g. the window server). void HandleExternalUserActivity(); - // ui::PlatformEventObserver: + // PlatformEventObserver: void WillProcessEvent(const PlatformEvent& platform_event) override {} void DidProcessEvent(const PlatformEvent& platform_event) override; diff --git a/chromium/ui/base/user_activity/user_activity_detector_unittest.cc b/chromium/ui/base/user_activity/user_activity_detector_unittest.cc index b2decd61f41..82b26d238a5 100644 --- a/chromium/ui/base/user_activity/user_activity_detector_unittest.cc +++ b/chromium/ui/base/user_activity/user_activity_detector_unittest.cc @@ -41,7 +41,7 @@ class TestUserActivityObserver : public UserActivityObserver { // A test implementation of PlatformEventSource that we can instantiate to make // sure that the PlatformEventSource has an instance while in unit tests. -class TestPlatformEventSource : public ui::PlatformEventSource { +class TestPlatformEventSource : public PlatformEventSource { public: TestPlatformEventSource() {} ~TestPlatformEventSource() override {} diff --git a/chromium/ui/base/win/direct_manipulation.cc b/chromium/ui/base/win/direct_manipulation.cc index 78f19277727..52daec79488 100644 --- a/chromium/ui/base/win/direct_manipulation.cc +++ b/chromium/ui/base/win/direct_manipulation.cc @@ -5,60 +5,96 @@ #include "ui/base/win/direct_manipulation.h" #include <objbase.h> +#include <cmath> +#include "base/memory/ptr_util.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/win/windows_version.h" +#include "ui/base/ui_base_features.h" +#include "ui/display/win/screen_win.h" namespace ui { namespace win { // static std::unique_ptr<DirectManipulationHelper> -DirectManipulationHelper::CreateInstance() { - // TODO(dtapuska): Do not create a DirectManipulationHelper on any windows - // versions as it only causes issues. High Precision Touchpad events seem to - // always be sent to apps with recent Windows 10 versions. This class should - // eventually be removed. See https://crbug.com/647038. +DirectManipulationHelper::CreateInstance(HWND window, + WindowEventTarget* event_target) { + if (!::IsWindow(window)) + return nullptr; + + if (!base::FeatureList::IsEnabled(features::kPrecisionTouchpad)) + return nullptr; + + // DM_POINTERHITTEST supported since Win10. + if (base::win::GetVersion() < base::win::VERSION_WIN10) + return nullptr; + + std::unique_ptr<DirectManipulationHelper> instance = + base::WrapUnique(new DirectManipulationHelper()); + instance->window_ = window; + + if (instance->Initialize(event_target)) + return instance; + return nullptr; } -DirectManipulationHelper::DirectManipulationHelper() {} +// static +std::unique_ptr<DirectManipulationHelper> +DirectManipulationHelper::CreateInstanceForTesting( + WindowEventTarget* event_target, + Microsoft::WRL::ComPtr<IDirectManipulationViewport> viewport) { + if (!base::FeatureList::IsEnabled(features::kPrecisionTouchpad)) + return nullptr; + + // DM_POINTERHITTEST supported since Win10. + if (base::win::GetVersion() < base::win::VERSION_WIN10) + return nullptr; + + std::unique_ptr<DirectManipulationHelper> instance = + base::WrapUnique(new DirectManipulationHelper()); + + instance->event_handler_ = Microsoft::WRL::Make<DirectManipulationHandler>( + instance.get(), event_target); + + instance->viewport_ = viewport; + + return instance; +} DirectManipulationHelper::~DirectManipulationHelper() { - if (view_port_outer_) - view_port_outer_->Abandon(); + if (viewport_) + viewport_->Abandon(); } -void DirectManipulationHelper::Initialize(HWND window) { - DCHECK(::IsWindow(window)); +DirectManipulationHelper::DirectManipulationHelper() {} + +// We only use Direct Manipulation as event handler so we can use any size for +// the fake viewport. +const RECT VIEWPORT_DEFAULT_RECT = {0, 0, 1000, 1000}; - // TODO(ananta) - // Remove the CHECK statements here and below and replace them with logs - // when this code stabilizes. +bool DirectManipulationHelper::Initialize(WindowEventTarget* event_target) { + // IDirectManipulationUpdateManager is the first COM object created by the + // application to retrieve other objects in the Direct Manipulation API. + // It also serves to activate and deactivate Direct Manipulation functionality + // on a per-HWND basis. HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&manager_)); - CHECK(SUCCEEDED(hr)); + if (!SUCCEEDED(hr)) + return false; - hr = ::CoCreateInstance(CLSID_DCompManipulationCompositor, nullptr, - CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&compositor_)); - CHECK(SUCCEEDED(hr)); + // Since we want to use fake viewport, we need UpdateManager to tell a fake + // fake render frame. + hr = manager_->GetUpdateManager(IID_PPV_ARGS(&update_manager_)); + if (!SUCCEEDED(hr)) + return false; - hr = manager_->GetUpdateManager(IID_PPV_ARGS(update_manager_.GetAddressOf())); - CHECK(SUCCEEDED(hr)); + hr = manager_->CreateViewport(nullptr, window_, IID_PPV_ARGS(&viewport_)); + if (!SUCCEEDED(hr)) + return false; - hr = compositor_->SetUpdateManager(update_manager_.Get()); - CHECK(SUCCEEDED(hr)); - - hr = compositor_.CopyTo(frame_info_.GetAddressOf()); - CHECK(SUCCEEDED(hr)); - - hr = manager_->CreateViewport(frame_info_.Get(), window, - IID_PPV_ARGS(view_port_outer_.GetAddressOf())); - CHECK(SUCCEEDED(hr)); - - // - // Enable the desired configuration for each viewport. - // DIRECTMANIPULATION_CONFIGURATION configuration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X | @@ -66,56 +102,360 @@ void DirectManipulationHelper::Initialize(HWND window) { DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA | DIRECTMANIPULATION_CONFIGURATION_RAILS_X | DIRECTMANIPULATION_CONFIGURATION_RAILS_Y | - DIRECTMANIPULATION_CONFIGURATION_SCALING | - DIRECTMANIPULATION_CONFIGURATION_SCALING_INERTIA; + DIRECTMANIPULATION_CONFIGURATION_SCALING; + + hr = viewport_->ActivateConfiguration(configuration); + if (!SUCCEEDED(hr)) + return false; + + // Since we are using fake viewport and only want to use Direct Manipulation + // for touchpad, we need to use MANUALUPDATE option. + hr = viewport_->SetViewportOptions( + DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE); + if (!SUCCEEDED(hr)) + return false; - hr = view_port_outer_->ActivateConfiguration(configuration); - CHECK(SUCCEEDED(hr)); + event_handler_ = + Microsoft::WRL::Make<DirectManipulationHandler>(this, event_target); + if (!SUCCEEDED(hr)) + return false; + + // We got Direct Manipulation transform from + // IDirectManipulationViewportEventHandler. + hr = viewport_->AddEventHandler(window_, event_handler_.Get(), + &view_port_handler_cookie_); + if (!SUCCEEDED(hr)) + return false; + + hr = viewport_->SetViewportRect(&VIEWPORT_DEFAULT_RECT); + if (!SUCCEEDED(hr)) + return false; + + manager_->Activate(window_); + + hr = viewport_->Enable(); + update_manager_->Update(nullptr); + if (!SUCCEEDED(hr)) + return false; + + return true; } -void DirectManipulationHelper::SetBounds(const gfx::Rect& bounds) { - Microsoft::WRL::ComPtr<IDirectManipulationPrimaryContent> - primary_content_outer; - HRESULT hr = view_port_outer_->GetPrimaryContent( - IID_PPV_ARGS(primary_content_outer.GetAddressOf())); - CHECK(SUCCEEDED(hr)); +void DirectManipulationHelper::Activate() { + manager_->Activate(window_); +} - Microsoft::WRL::ComPtr<IDirectManipulationContent> content_outer; - hr = primary_content_outer.CopyTo(content_outer.GetAddressOf()); - CHECK(SUCCEEDED(hr)); +void DirectManipulationHelper::Deactivate() { + manager_->Deactivate(window_); +} - RECT rect = bounds.ToRECT(); +bool DirectManipulationHelper::OnPointerHitTest( + WPARAM w_param, + WindowEventTarget* event_target) { + // Update the device scale factor. + event_handler_->SetDeviceScaleFactor( + display::win::ScreenWin::GetScaleFactorForHWND(window_)); - hr = view_port_outer_->SetViewportRect(&rect); - CHECK(SUCCEEDED(hr)); + // Only DM_POINTERHITTEST can be the first message of input sequence of + // touchpad input. + // TODO(chaopeng) Check if Windows API changes: + // For WM_POINTER, the pointer type will show the event from mouse. + // For WM_POINTERACTIVATE, the pointer id will be different with the following + // message. + event_handler_->SetWindowEventTarget(event_target); - hr = content_outer->SetContentRect(&rect); - CHECK(SUCCEEDED(hr)); + using GetPointerTypeFn = BOOL(WINAPI*)(UINT32, POINTER_INPUT_TYPE*); + UINT32 pointer_id = GET_POINTERID_WPARAM(w_param); + POINTER_INPUT_TYPE pointer_type; + static GetPointerTypeFn get_pointer_type = reinterpret_cast<GetPointerTypeFn>( + GetProcAddress(GetModuleHandleA("user32.dll"), "GetPointerType")); + if (get_pointer_type && get_pointer_type(pointer_id, &pointer_type) && + pointer_type == PT_TOUCHPAD && event_target) { + viewport_->SetContact(pointer_id); + // Request begin frame for fake viewport. + need_poll_events_ = true; + } + return need_poll_events_; } -void DirectManipulationHelper::Activate(HWND window) { - DCHECK(::IsWindow(window)); - manager_->Activate(window); +HRESULT DirectManipulationHelper::ResetViewport(bool need_poll_events) { + // By zooming the primary content to a rect that match the viewport rect, we + // reset the content's transform to identity. + HRESULT hr = viewport_->ZoomToRect( + static_cast<float>(VIEWPORT_DEFAULT_RECT.left), + static_cast<float>(VIEWPORT_DEFAULT_RECT.top), + static_cast<float>(VIEWPORT_DEFAULT_RECT.right), + static_cast<float>(VIEWPORT_DEFAULT_RECT.bottom), FALSE); + if (!SUCCEEDED(hr)) + return hr; + + need_poll_events_ = need_poll_events; + return S_OK; } -void DirectManipulationHelper::Deactivate(HWND window) { - DCHECK(::IsWindow(window)); - manager_->Deactivate(window); +bool DirectManipulationHelper::PollForNextEvent() { + // Simulate 1 frame in update_manager_. + update_manager_->Update(nullptr); + return need_poll_events_; } -void DirectManipulationHelper::HandleMouseWheel(HWND window, - UINT message, - WPARAM w_param, - LPARAM l_param) { - MSG msg = {window, message, w_param, l_param}; +void DirectManipulationHelper::SetDeviceScaleFactorForTesting(float factor) { + event_handler_->SetDeviceScaleFactor(factor); +} - HRESULT hr = view_port_outer_->SetContact(DIRECTMANIPULATION_MOUSEFOCUS); - if (SUCCEEDED(hr)) { - BOOL handled = FALSE; - manager_->ProcessInput(&msg, &handled); - view_port_outer_->ReleaseContact(DIRECTMANIPULATION_MOUSEFOCUS); +// DirectManipulationHandler +DirectManipulationHandler::DirectManipulationHandler() { + NOTREACHED(); +} + +DirectManipulationHandler::DirectManipulationHandler( + DirectManipulationHelper* helper, + WindowEventTarget* event_target) + : helper_(helper), event_target_(event_target) {} + +DirectManipulationHandler::~DirectManipulationHandler() {} + +void DirectManipulationHandler::TransitionToState(Gesture new_gesture_state) { + if (gesture_state_ == new_gesture_state) + return; + + Gesture previous_gesture_state = gesture_state_; + gesture_state_ = new_gesture_state; + + // End the previous sequence. + switch (previous_gesture_state) { + case Gesture::kScroll: { + // kScroll -> kNone, kPinch, ScrollEnd. + // kScroll -> kFling, we don't want to end the current scroll sequence. + if (new_gesture_state != Gesture::kFling) + event_target_->ApplyPanGestureScrollEnd(); + break; + } + case Gesture::kFling: { + // kFling -> *, FlingEnd. + event_target_->ApplyPanGestureFlingEnd(); + break; + } + case Gesture::kPinch: { + DCHECK_EQ(new_gesture_state, Gesture::kNone); + // kPinch -> kNone, PinchEnd. kPinch should only transition to kNone. + event_target_->ApplyPinchZoomEnd(); + break; + } + case Gesture::kNone: { + // kNone -> *, no cleanup is needed. + break; + } + default: + NOTREACHED(); } + + // Start the new sequence. + switch (new_gesture_state) { + case Gesture::kScroll: { + // kFling, kNone -> kScroll, ScrollBegin. + // ScrollBegin is different phase event with others. It must send within + // the first scroll event. + should_send_scroll_begin_ = true; + break; + } + case Gesture::kFling: { + // Only kScroll can transition to kFling. + DCHECK_EQ(previous_gesture_state, Gesture::kScroll); + event_target_->ApplyPanGestureFlingBegin(); + break; + } + case Gesture::kPinch: { + // * -> kPinch, PinchBegin. + // Pinch gesture may begin with some scroll events. + event_target_->ApplyPinchZoomBegin(); + break; + } + case Gesture::kNone: { + // * -> kNone, only cleanup is needed. + break; + } + default: + NOTREACHED(); + } +} + +HRESULT DirectManipulationHandler::OnViewportStatusChanged( + IDirectManipulationViewport* viewport, + DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) { + // MSDN never mention |viewport| are nullable and we never saw it is null when + // testing. + DCHECK(viewport); + + // The state of our viewport has changed! We'l be in one of three states: + // - ENABLED: initial state + // - READY: the previous gesture has been completed + // - RUNNING: gesture updating + // - INERTIA: finger leave touchpad content still updating by inertia + HRESULT hr = S_OK; + + // Windows should not call this when event_target_ is null since we do not + // pass the DM_POINTERHITTEST to DirectManipulation. + if (!event_target_) + return hr; + + if (current == previous) + return hr; + + if (current == DIRECTMANIPULATION_INERTIA) { + // Fling must lead by Scroll. We can actually hit here when user pinch then + // quickly pan gesture and leave touchpad. In this case, we don't want to + // start a new sequence until the gesture end. The rest events in sequence + // will be ignore since sequence still in pinch and only scale factor + // changes will be applied. + if (previous != DIRECTMANIPULATION_RUNNING || + gesture_state_ != Gesture::kScroll) { + return hr; + } + + TransitionToState(Gesture::kFling); + } + + if (current == DIRECTMANIPULATION_RUNNING) { + // INERTIA -> RUNNING, should start a new sequence. + if (previous == DIRECTMANIPULATION_INERTIA) + TransitionToState(Gesture::kNone); + } + + // Reset the viewport when we're idle, so the content transforms always start + // at identity. + if (current == DIRECTMANIPULATION_READY) { + // Every animation will receive 2 ready message, we should stop request + // compositor animation at the second ready. + first_ready_ = !first_ready_; + hr = helper_->ResetViewport(first_ready_); + last_scale_ = 1.0f; + last_x_offset_ = 0.0f; + last_y_offset_ = 0.0f; + + TransitionToState(Gesture::kNone); + } + + return hr; +} + +HRESULT DirectManipulationHandler::OnViewportUpdated( + IDirectManipulationViewport* viewport) { + // Nothing to do here. + return S_OK; +} + +namespace { + +bool FloatEquals(float f1, float f2) { + // The idea behind this is to use this fraction of the larger of the + // two numbers as the limit of the difference. This breaks down near + // zero, so we reuse this as the minimum absolute size we will use + // for the base of the scale too. + static const float epsilon_scale = 0.00001f; + return fabs(f1 - f2) < + epsilon_scale * + std::fmax(std::fmax(std::fabs(f1), std::fabs(f2)), epsilon_scale); +} + +bool DifferentLessThanOne(int f1, int f2) { + return abs(f1 - f2) < 1; +} + +} // namespace + +HRESULT DirectManipulationHandler::OnContentUpdated( + IDirectManipulationViewport* viewport, + IDirectManipulationContent* content) { + // MSDN never mention these params are nullable and we never saw they are null + // when testing. + DCHECK(viewport); + DCHECK(content); + + HRESULT hr = S_OK; + + // Windows should not call this when event_target_ is null since we do not + // pass the DM_POINTERHITTEST to DirectManipulation. + if (!event_target_) + return hr; + + float xform[6]; + hr = content->GetContentTransform(xform, ARRAYSIZE(xform)); + if (!SUCCEEDED(hr)) + return hr; + + float scale = xform[0]; + int x_offset = xform[4] / device_scale_factor_; + int y_offset = xform[5] / device_scale_factor_; + + // Ignore if Windows pass scale=0 to us. + if (scale == 0.0f) { + LOG(ERROR) << "Windows DirectManipulation API pass scale = 0."; + return hr; + } + + // Ignore the scale factor change less than float point rounding error and + // scroll offset change less than 1. + // TODO(456622) Because we don't fully support fractional scroll, pass float + // scroll offset feels steppy. eg. + // first x_offset is 0.1 ignored, but last_x_offset_ set to 0.1 + // second x_offset is 1 but x_offset - last_x_offset_ is 0.9 ignored. + if (FloatEquals(scale, last_scale_) && + DifferentLessThanOne(x_offset, last_x_offset_) && + DifferentLessThanOne(y_offset, last_y_offset_)) { + return hr; + } + + DCHECK_NE(last_scale_, 0.0f); + + // DirectManipulation will send xy transform move to down-right which is noise + // when pinch zoom. We should consider the gesture either Scroll or Pinch at + // one sequence. But Pinch gesture may begin with some scroll transform since + // DirectManipulation recognition maybe wrong at start if the user pinch with + // slow motion. So we allow kScroll -> kPinch. + + // Consider this is a Scroll when scale factor equals 1.0. + if (FloatEquals(scale, 1.0f)) { + if (gesture_state_ == Gesture::kNone) + TransitionToState(Gesture::kScroll); + } else { + // Pinch gesture may begin with some scroll events. + TransitionToState(Gesture::kPinch); + } + + if (gesture_state_ == Gesture::kScroll) { + if (should_send_scroll_begin_) { + event_target_->ApplyPanGestureScrollBegin(x_offset - last_x_offset_, + y_offset - last_y_offset_); + should_send_scroll_begin_ = false; + } else { + event_target_->ApplyPanGestureScroll(x_offset - last_x_offset_, + y_offset - last_y_offset_); + } + } else if (gesture_state_ == Gesture::kFling) { + event_target_->ApplyPanGestureFling(x_offset - last_x_offset_, + y_offset - last_y_offset_); + } else { + event_target_->ApplyPinchZoomScale(scale / last_scale_); + } + + last_scale_ = scale; + last_x_offset_ = x_offset; + last_y_offset_ = y_offset; + + return hr; +} + +void DirectManipulationHandler::SetWindowEventTarget( + WindowEventTarget* event_target) { + event_target_ = event_target; +} + +void DirectManipulationHandler::SetDeviceScaleFactor( + float device_scale_factor) { + device_scale_factor_ = device_scale_factor; } -} // namespace win. -} // namespace ui. +} // namespace win +} // namespace ui diff --git a/chromium/ui/base/win/direct_manipulation.h b/chromium/ui/base/win/direct_manipulation.h index af7ad807f2e..c0b603a7acf 100644 --- a/chromium/ui/base/win/direct_manipulation.h +++ b/chromium/ui/base/win/direct_manipulation.h @@ -5,79 +5,150 @@ #ifndef UI_WIN_DIRECT_MANIPULATION_H_ #define UI_WIN_DIRECT_MANIPULATION_H_ -#include <directmanipulation.h> -#include <wrl/client.h> +#include <windows.h> +#include <directmanipulation.h> +#include <wrl.h> #include <memory> +#include "base/gtest_prod_util.h" #include "base/macros.h" #include "ui/base/ui_base_export.h" +#include "ui/base/win/window_event_target.h" #include "ui/gfx/geometry/rect.h" +namespace content { +class DirectManipulationBrowserTest; +} // namespace content + namespace ui { namespace win { +class DirectManipulationUnitTest; + +class DirectManipulationHelper; + +// DirectManipulationHandler receives status update and gesture events from +// Direct Manipulation API. +class DirectManipulationHandler + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::Implements< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::FtmBase, + IDirectManipulationViewportEventHandler>> { + public: + explicit DirectManipulationHandler(DirectManipulationHelper* helper, + WindowEventTarget* event_target); + + // WindowEventTarget updates for every DM_POINTERHITTEST in case window + // hierarchy changed. + void SetWindowEventTarget(WindowEventTarget* event_target); + + void SetDeviceScaleFactor(float device_scale_factor); + + private: + friend DirectManipulationUnitTest; + + DirectManipulationHandler(); + ~DirectManipulationHandler() override; + + enum class Gesture { kNone, kScroll, kFling, kPinch }; + + void TransitionToState(Gesture gesture); + + HRESULT STDMETHODCALLTYPE + OnViewportStatusChanged(_In_ IDirectManipulationViewport* viewport, + _In_ DIRECTMANIPULATION_STATUS current, + _In_ DIRECTMANIPULATION_STATUS previous) override; + + HRESULT STDMETHODCALLTYPE + OnViewportUpdated(_In_ IDirectManipulationViewport* viewport) override; + + HRESULT STDMETHODCALLTYPE + OnContentUpdated(_In_ IDirectManipulationViewport* viewport, + _In_ IDirectManipulationContent* content) override; + + DirectManipulationHelper* helper_ = nullptr; + WindowEventTarget* event_target_ = nullptr; + float device_scale_factor_ = 1.0f; + float last_scale_ = 1.0f; + int last_x_offset_ = 0; + int last_y_offset_ = 0; + bool first_ready_ = false; + bool should_send_scroll_begin_ = false; + + // Current recognized gesture from Direct Manipulation. + Gesture gesture_state_ = Gesture::kNone; + + DISALLOW_COPY_AND_ASSIGN(DirectManipulationHandler); +}; + // Windows 10 provides a new API called Direct Manipulation which generates -// smooth scroll events via WM_MOUSEWHEEL messages with predictable deltas -// on high precision touch pads. This basically requires the application window -// to register as a Direct Manipulation consumer. The way mouse wheel messages -// are dispatched is +// smooth scroll and scale factor via IDirectManipulationViewportEventHandler +// on precision touchpad. // 1. The foreground window is checked to see if it is a Direct Manipulation // consumer. -// 2. If it is then Direct Manipulation takes over and sends the following -// messages. WM_POINTERACTIVATE, WM_POINTERDOWN and DM_POINTERHITTEST. -// 3. It then posts WM_MOUSEWHEEL messages with precision deltas which vary -// based on the amount of the scroll. -// 4. If the foreground window is not a Direct Manipulation consumer, it -// then takes a fallback route where it posts WM_MOUSEWHEEL messages -// with precision but varying deltas to the window. There is a also -// a slight delay in receiving the first set of mouse wheel messages. -// This causes scrolling to appear janky and jumpy. -// Our approach for addressing this is to do the absolute minimum to -// register our window as a Direct Manipulation consumer. This class -// provides the necessary functionality to register the passed in HWND as a -// Direct Manipulation consumer. We don't rely on Direct manipulation -// to do the smooth scrolling in the background thread as documented on -// msdn. +// 2. Call SetContact in Direct Manipulation takes over the following scrolling +// when DM_POINTERHITTEST. +// 3. OnViewportStatusChanged will be called when the gesture phase change. +// OnContentUpdated will be called when the gesture update. class UI_BASE_EXPORT DirectManipulationHelper { public: - // Creates an instance of this class if Direct Manipulation is enabled on - // the platform. If not returns NULL. - static std::unique_ptr<DirectManipulationHelper> CreateInstance(); - - // This function instantiates Direct Manipulation and creates a viewport for - // the passed in |window|. - // consumer. Most of the code is boiler plate and is based on the sample. - void Initialize(HWND window); + // Creates and initializes an instance of this class if Direct Manipulation is + // enabled on the platform. Returns nullptr if it disabled or failed on + // initialization. + static std::unique_ptr<DirectManipulationHelper> CreateInstance( + HWND window, + WindowEventTarget* event_target); + + // Creates and initializes an instance for testing. + static std::unique_ptr<DirectManipulationHelper> CreateInstanceForTesting( + WindowEventTarget* event_target, + Microsoft::WRL::ComPtr<IDirectManipulationViewport> viewport); - // Sets the bounds of the fake Direct manipulation viewport to match those - // of the legacy window. - void SetBounds(const gfx::Rect& bounds); + ~DirectManipulationHelper(); // Registers and activates the passed in |window| as a Direct Manipulation // consumer. - void Activate(HWND window); + void Activate(); // Deactivates Direct Manipulation processing on the passed in |window|. - void Deactivate(HWND window); + void Deactivate(); - // Passes the WM_MOUSEWHEEL messages to Direct Manipulation. This is for - // logistics purposes. - void HandleMouseWheel(HWND window, - UINT message, - WPARAM w_param, - LPARAM l_param); + // Reset the fake viewport for gesture end. + HRESULT ResetViewport(bool need_animtation); - ~DirectManipulationHelper(); + // Pass the pointer hit test to Direct Manipulation. Return true indicated we + // need poll for new events every frame from here. + bool OnPointerHitTest(WPARAM w_param, WindowEventTarget* event_target); + + // On each frame poll new Direct Manipulation events. Return true if we still + // need poll for new events on next frame, otherwise stop request need begin + // frame. + bool PollForNextEvent(); private: + friend class content::DirectManipulationBrowserTest; + friend class DirectManipulationUnitTest; + DirectManipulationHelper(); - Microsoft::WRL::ComPtr<IDirectManipulationManager2> manager_; - Microsoft::WRL::ComPtr<IDirectManipulationCompositor> compositor_; + // This function instantiates Direct Manipulation and creates a viewport for + // the passed in |window|. Return false if initialize failed. + bool Initialize(WindowEventTarget* event_target); + + void SetDeviceScaleFactorForTesting(float factor); + + Microsoft::WRL::ComPtr<IDirectManipulationManager> manager_; Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> update_manager_; - Microsoft::WRL::ComPtr<IDirectManipulationFrameInfoProvider> frame_info_; - Microsoft::WRL::ComPtr<IDirectManipulationViewport2> view_port_outer_; + Microsoft::WRL::ComPtr<IDirectManipulationViewport> viewport_; + Microsoft::WRL::ComPtr<DirectManipulationHandler> event_handler_; + HWND window_; + DWORD view_port_handler_cookie_; + bool need_poll_events_ = false; DISALLOW_COPY_AND_ASSIGN(DirectManipulationHelper); }; diff --git a/chromium/ui/base/win/direct_manipulation_unittest.cc b/chromium/ui/base/win/direct_manipulation_unittest.cc new file mode 100644 index 00000000000..79d4bb77502 --- /dev/null +++ b/chromium/ui/base/win/direct_manipulation_unittest.cc @@ -0,0 +1,756 @@ +// 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/win/direct_manipulation.h" + +#include <objbase.h> + +#include "base/macros.h" +#include "base/test/scoped_feature_list.h" +#include "base/win/windows_version.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/ui_base_features.h" + +namespace ui { + +namespace win { + +namespace { + +class MockDirectManipulationViewport + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::Implements< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::FtmBase, + IDirectManipulationViewport>> { + public: + MockDirectManipulationViewport() {} + + ~MockDirectManipulationViewport() override {} + + HRESULT STDMETHODCALLTYPE Enable() override { return S_OK; } + + HRESULT STDMETHODCALLTYPE Disable() override { return S_OK; } + + HRESULT STDMETHODCALLTYPE SetContact(_In_ UINT32 pointerId) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ReleaseContact(_In_ UINT32 pointerId) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ReleaseAllContacts() override { return S_OK; } + + HRESULT STDMETHODCALLTYPE + GetStatus(_Out_ DIRECTMANIPULATION_STATUS* status) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE GetTag(_In_ REFIID riid, + _COM_Outptr_opt_ void** object, + _Out_opt_ UINT32* id) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE SetTag(_In_opt_ IUnknown* object, + _In_ UINT32 id) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE GetViewportRect(_Out_ RECT* viewport) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SetViewportRect(_In_ const RECT* viewport) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ZoomToRect(_In_ const float left, + _In_ const float top, + _In_ const float right, + _In_ const float bottom, + _In_ BOOL animate) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SetViewportTransform(_In_reads_(point_count) const float* matrix, + _In_ DWORD point_count) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SyncDisplayTransform(_In_reads_(point_count) const float* matrix, + _In_ DWORD point_count) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + GetPrimaryContent(_In_ REFIID riid, _COM_Outptr_ void** object) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + AddContent(_In_ IDirectManipulationContent* content) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + RemoveContent(_In_ IDirectManipulationContent* content) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE SetViewportOptions( + _In_ DIRECTMANIPULATION_VIEWPORT_OPTIONS options) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE AddConfiguration( + _In_ DIRECTMANIPULATION_CONFIGURATION configuration) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE RemoveConfiguration( + _In_ DIRECTMANIPULATION_CONFIGURATION configuration) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE ActivateConfiguration( + _In_ DIRECTMANIPULATION_CONFIGURATION configuration) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE SetManualGesture( + _In_ DIRECTMANIPULATION_GESTURE_CONFIGURATION configuration) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SetChaining(_In_ DIRECTMANIPULATION_MOTION_TYPES enabledTypes) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + AddEventHandler(_In_opt_ HWND window, + _In_ IDirectManipulationViewportEventHandler* eventHandler, + _Out_ DWORD* cookie) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE RemoveEventHandler(_In_ DWORD cookie) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SetInputMode(_In_ DIRECTMANIPULATION_INPUT_MODE mode) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SetUpdateMode(_In_ DIRECTMANIPULATION_INPUT_MODE mode) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE Stop() override { return S_OK; } + + HRESULT STDMETHODCALLTYPE Abandon() override { return S_OK; } + + private: + DISALLOW_COPY_AND_ASSIGN(MockDirectManipulationViewport); +}; + +class MockDirectManipulationContent + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::Implements< + Microsoft::WRL::RuntimeClassFlags< + Microsoft::WRL::RuntimeClassType::ClassicCom>, + Microsoft::WRL::FtmBase, + IDirectManipulationContent>> { + public: + MockDirectManipulationContent() {} + + ~MockDirectManipulationContent() override {} + + void SetContentTransform(float scale, float scroll_x, float scroll_y) { + for (int i = 0; i < 6; ++i) + transforms_[i] = 0; + transforms_[0] = scale; + transforms_[4] = scroll_x; + transforms_[5] = scroll_y; + } + + HRESULT STDMETHODCALLTYPE + GetContentTransform(_Out_writes_(point_count) float* transforms, + _In_ DWORD point_count) override { + for (int i = 0; i < 6; ++i) + transforms[i] = transforms_[i]; + return S_OK; + } + + // Other Overrides + HRESULT STDMETHODCALLTYPE GetContentRect(_Out_ RECT* contentSize) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SetContentRect(_In_ const RECT* contentSize) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE GetViewport(_In_ REFIID riid, + _COM_Outptr_ void** object) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE GetTag(_In_ REFIID riid, + _COM_Outptr_opt_ void** object, + _Out_opt_ UINT32* id) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE SetTag(_In_opt_ IUnknown* object, + _In_ UINT32 id) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + GetOutputTransform(_Out_writes_(point_count) float* matrix, + _In_ DWORD point_count) override { + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + SyncContentTransform(_In_reads_(point_count) const float* matrix, + _In_ DWORD point_count) override { + return S_OK; + } + + private: + float transforms_[6]; + + DISALLOW_COPY_AND_ASSIGN(MockDirectManipulationContent); +}; + +enum class EventGesture { + kScrollBegin, + kScroll, + kScrollEnd, + kFlingBegin, + kFling, + kFlingEnd, + kScaleBegin, + kScale, + kScaleEnd, +}; + +struct Event { + explicit Event(float scale) : gesture_(EventGesture::kScale), scale_(scale) {} + + Event(EventGesture gesture, float scroll_x, float scroll_y) + : gesture_(gesture), scroll_x_(scroll_x), scroll_y_(scroll_y) {} + + explicit Event(EventGesture gesture) : gesture_(gesture) {} + + EventGesture gesture_; + float scale_ = 0; + float scroll_x_ = 0; + float scroll_y_ = 0; +}; + +class MockWindowEventTarget : public WindowEventTarget { + public: + MockWindowEventTarget() {} + + ~MockWindowEventTarget() override {} + + void ApplyPinchZoomScale(float scale) override { + events_.push_back(Event(scale)); + } + + void ApplyPinchZoomBegin() override { + events_.push_back(Event(EventGesture::kScaleBegin)); + } + + void ApplyPinchZoomEnd() override { + events_.push_back(Event(EventGesture::kScaleEnd)); + } + + void ApplyPanGestureScroll(int scroll_x, int scroll_y) override { + events_.push_back(Event(EventGesture::kScroll, scroll_x, scroll_y)); + } + + void ApplyPanGestureFling(int scroll_x, int scroll_y) override { + events_.push_back(Event(EventGesture::kFling, scroll_x, scroll_y)); + } + + void ApplyPanGestureScrollBegin(int scroll_x, int scroll_y) override { + events_.push_back(Event(EventGesture::kScrollBegin, scroll_x, scroll_y)); + } + + void ApplyPanGestureFlingBegin() override { + events_.push_back(Event(EventGesture::kFlingBegin)); + } + + void ApplyPanGestureFlingEnd() override { + events_.push_back(Event(EventGesture::kFlingEnd)); + } + + void ApplyPanGestureScrollEnd() override { + events_.push_back(Event(EventGesture::kScrollEnd)); + } + + std::vector<Event> GetEvents() { + std::vector<Event> result = events_; + events_.clear(); + return result; + } + + // Other Overrides + LRESULT HandleMouseMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override { + return S_OK; + } + + LRESULT HandlePointerMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override { + return S_OK; + } + + LRESULT HandleKeyboardMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override { + return S_OK; + } + + LRESULT HandleTouchMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override { + return S_OK; + } + + LRESULT HandleScrollMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override { + return S_OK; + } + + LRESULT HandleNcHitTestMessage(unsigned int message, + WPARAM w_param, + LPARAM l_param, + bool* handled) override { + return S_OK; + } + + void HandleParentChanged() override {} + + private: + std::vector<Event> events_; + + DISALLOW_COPY_AND_ASSIGN(MockWindowEventTarget); +}; + +} // namespace + +class DirectManipulationUnitTest : public testing::Test { + public: + DirectManipulationUnitTest() { + scoped_feature_list_.InitAndEnableFeature(features::kPrecisionTouchpad); + + viewport_ = Microsoft::WRL::Make<MockDirectManipulationViewport>(); + content_ = Microsoft::WRL::Make<MockDirectManipulationContent>(); + direct_manipulation_helper_ = + DirectManipulationHelper::CreateInstanceForTesting(&event_target_, + viewport_); + } + + ~DirectManipulationUnitTest() override {} + + DirectManipulationHelper* GetDirectManipulationHelper() { + return direct_manipulation_helper_.get(); + } + + std::vector<Event> GetEvents() { return event_target_.GetEvents(); } + + void ViewportStatusChanged(DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) { + direct_manipulation_helper_->event_handler_->OnViewportStatusChanged( + viewport_.Get(), current, previous); + } + + void ContentUpdated(float scale, float scroll_x, float scroll_y) { + content_->SetContentTransform(scale, scroll_x, scroll_y); + direct_manipulation_helper_->event_handler_->OnContentUpdated( + viewport_.Get(), content_.Get()); + } + + void SetNeedAnimation(bool need_poll_events) { + direct_manipulation_helper_->need_poll_events_ = need_poll_events; + } + + bool NeedAnimation() { + return direct_manipulation_helper_->need_poll_events_; + } + + void SetDeviceScaleFactor(float factor) { + direct_manipulation_helper_->SetDeviceScaleFactorForTesting(factor); + } + + private: + std::unique_ptr<DirectManipulationHelper> direct_manipulation_helper_; + Microsoft::WRL::ComPtr<MockDirectManipulationViewport> viewport_; + Microsoft::WRL::ComPtr<MockDirectManipulationContent> content_; + MockWindowEventTarget event_target_; + base::test::ScopedFeatureList scoped_feature_list_; + + DISALLOW_COPY_AND_ASSIGN(DirectManipulationUnitTest); +}; + +TEST_F(DirectManipulationUnitTest, HelperShouldCreateForWin10) { + // We should create DirectManipulationHelper instance when win version >= 10. + EXPECT_EQ(GetDirectManipulationHelper() != nullptr, + base::win::GetVersion() >= base::win::VERSION_WIN10); +} + +TEST_F(DirectManipulationUnitTest, ReceiveSimplePanTransform) { + if (!GetDirectManipulationHelper()) + return; + + ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); + ContentUpdated(1, 10, 0); + + std::vector<Event> events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + EXPECT_EQ(10, events[0].scroll_x_); + EXPECT_EQ(0, events[0].scroll_y_); + + // For next update, should only apply the difference. + ContentUpdated(1, 15, 0); + + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScroll, events[0].gesture_); + EXPECT_EQ(5, events[0].scroll_x_); + EXPECT_EQ(0, events[0].scroll_y_); + + ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); + + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollEnd, events[0].gesture_); +} + +TEST_F(DirectManipulationUnitTest, ReceivePanFling) { + if (!GetDirectManipulationHelper()) + return; + + ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); + ContentUpdated(1, 10, 0); + + std::vector<Event> events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + EXPECT_EQ(10, events[0].scroll_x_); + EXPECT_EQ(0, events[0].scroll_y_); + + // For next update, should only apply the difference. + ContentUpdated(1, 15, 0); + + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScroll, events[0].gesture_); + EXPECT_EQ(5, events[0].scroll_x_); + EXPECT_EQ(0, events[0].scroll_y_); + + // Fling Begin. + ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); + + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); + + ContentUpdated(1, 20, 0); + + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFling, events[0].gesture_); + EXPECT_EQ(5, events[0].scroll_x_); + EXPECT_EQ(0, events[0].scroll_y_); + + ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_INERTIA); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); +} + +TEST_F(DirectManipulationUnitTest, ReceiveSimpleScaleTransform) { + if (!GetDirectManipulationHelper()) + return; + + ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); + ContentUpdated(1.1f, 0, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(2u, events.size()); + EXPECT_EQ(EventGesture::kScaleBegin, events[0].gesture_); + EXPECT_EQ(EventGesture::kScale, events[1].gesture_); + EXPECT_EQ(1.1f, events[1].scale_); + + // For next update, should only apply the difference. + ContentUpdated(1.21f, 0, 0); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScale, events[0].gesture_); + EXPECT_EQ(1.1f, events[0].scale_); + + ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScaleEnd, events[0].gesture_); +} + +TEST_F(DirectManipulationUnitTest, ReceiveScrollTransformLessThanOne) { + if (!GetDirectManipulationHelper()) + return; + + // Scroll offset less than 1, should not apply. + ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); + ContentUpdated(1, 0.1f, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(0u, events.size()); + + // Scroll offset less than 1, should not apply. + ContentUpdated(1, 0.2f, 0); + events = GetEvents(); + EXPECT_EQ(0u, events.size()); + + // Scroll offset more than 1, should only apply integer part. + ContentUpdated(1, 1.2f, 0); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + EXPECT_EQ(1, events[0].scroll_x_); + EXPECT_EQ(0, events[0].scroll_y_); + + // Scroll offset difference less than 1, should not apply. + ContentUpdated(1, 1.5f, 0); + events = GetEvents(); + EXPECT_EQ(0u, events.size()); + + // Scroll offset difference more than 1, should only apply integer part. + ContentUpdated(1, 3.0f, 0); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScroll, events[0].gesture_); + EXPECT_EQ(2, events[0].scroll_x_); + EXPECT_EQ(0, events[0].scroll_y_); +} + +TEST_F(DirectManipulationUnitTest, + ReceiveScaleTransformLessThanFloatPointError) { + if (!GetDirectManipulationHelper()) + return; + + // Scale factor less than float point error, ignore. + ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_READY); + ContentUpdated(1.000001f, 0, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(0u, events.size()); + + // Scale factor more than float point error, apply. + ContentUpdated(1.00001f, 0, 0); + events = GetEvents(); + EXPECT_EQ(2u, events.size()); + EXPECT_EQ(EventGesture::kScaleBegin, events[0].gesture_); + EXPECT_EQ(EventGesture::kScale, events[1].gesture_); + EXPECT_EQ(1.00001f, events[1].scale_); + + // Scale factor difference less than float point error, ignore. + ContentUpdated(1.000011f, 0, 0); + events = GetEvents(); + EXPECT_EQ(0u, events.size()); + + // Scale factor difference more than float point error, apply. + ContentUpdated(1.000021f, 0, 0); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScale, events[0].gesture_); + EXPECT_EQ(1.000021f / 1.00001f, events[0].scale_); + + ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScaleEnd, events[0].gesture_); +} + +TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveBothScrollAndScale) { + if (!GetDirectManipulationHelper()) + return; + + // Direct Manipulation maybe give incorrect predictions. In this case, we will + // receive scroll first then scale after. + + // First event is a scroll event. + ContentUpdated(1.0f, 5, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + + // Second event comes with scale factor. Now the scroll offset only noise. + ContentUpdated(1.00001f, 5, 0); + events = GetEvents(); + EXPECT_EQ(3u, events.size()); + EXPECT_EQ(EventGesture::kScrollEnd, events[0].gesture_); + EXPECT_EQ(EventGesture::kScaleBegin, events[1].gesture_); + EXPECT_EQ(EventGesture::kScale, events[2].gesture_); +} + +TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveScaleAfterFling) { + if (!GetDirectManipulationHelper()) + return; + + // Direct Manipulation maybe give pinch event after fling. In this case, we + // should end the current sequence first. + + // First event is a scroll event. + ContentUpdated(1.0f, 5, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + + // Fling Begin. + ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); + + ContentUpdated(1, 10, 0); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFling, events[0].gesture_); + + // Event comes with scale factor. Now the scroll offset only noise. + ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_INERTIA); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); + + ContentUpdated(1.00001f, 10, 0); + events = GetEvents(); + EXPECT_EQ(2u, events.size()); + EXPECT_EQ(EventGesture::kScaleBegin, events[0].gesture_); + EXPECT_EQ(EventGesture::kScale, events[1].gesture_); +} + +TEST_F(DirectManipulationUnitTest, InSameSequenceReceiveScrollAfterFling) { + if (!GetDirectManipulationHelper()) + return; + + // Direct Manipulation maybe give scroll event after fling. In this case, we + // should end the current sequence first. + + // First event is a scroll event. + ContentUpdated(1.0f, 5, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + + // Fling Begin. + ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); + + ContentUpdated(1, 10, 0); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFling, events[0].gesture_); + + // Fling back to Scroll. + ViewportStatusChanged(DIRECTMANIPULATION_RUNNING, DIRECTMANIPULATION_INERTIA); + ContentUpdated(1, 15, 0); + events = GetEvents(); + EXPECT_EQ(2u, events.size()); + EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); + EXPECT_EQ(EventGesture::kScrollBegin, events[1].gesture_); +} + +TEST_F(DirectManipulationUnitTest, + ReceiveScaleAfterFlingWithoutViewportStatusChanged) { + if (!GetDirectManipulationHelper()) + return; + + // We never see this when testing, but still what to test it. + + ContentUpdated(1.0f, 5, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + + // Fling Begin. + ViewportStatusChanged(DIRECTMANIPULATION_INERTIA, DIRECTMANIPULATION_RUNNING); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFlingBegin, events[0].gesture_); + + ContentUpdated(1, 10, 0); + events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kFling, events[0].gesture_); + + // Event comes with scale factor. But no ViewportStatusChanged. + ContentUpdated(1.00001f, 10, 0); + events = GetEvents(); + EXPECT_EQ(3u, events.size()); + EXPECT_EQ(EventGesture::kFlingEnd, events[0].gesture_); + EXPECT_EQ(EventGesture::kScaleBegin, events[1].gesture_); + EXPECT_EQ(EventGesture::kScale, events[2].gesture_); +} + +TEST_F(DirectManipulationUnitTest, + NeedAnimtationShouldBeFalseAfterSecondReset) { + if (!GetDirectManipulationHelper()) + return; + + // Direct Manipulation will set need_poll_events_ true when DM_POINTERTEST + // from touchpad. + SetNeedAnimation(true); + + // Receive first ready when gesture end. + ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); + EXPECT_TRUE(NeedAnimation()); + + // Receive second ready from ZoomToRect. + ViewportStatusChanged(DIRECTMANIPULATION_READY, DIRECTMANIPULATION_RUNNING); + EXPECT_FALSE(NeedAnimation()); +} + +TEST_F(DirectManipulationUnitTest, HiDPIScroll) { + if (!GetDirectManipulationHelper()) + return; + + SetDeviceScaleFactor(10.0); + ContentUpdated(1.0f, 50, 0); + std::vector<Event> events = GetEvents(); + EXPECT_EQ(1u, events.size()); + EXPECT_EQ(EventGesture::kScrollBegin, events[0].gesture_); + EXPECT_EQ(5, events[0].scroll_x_); +} + +} // namespace win + +} // namespace ui
\ No newline at end of file diff --git a/chromium/ui/base/win/on_screen_keyboard_display_manager_tab_tip.h b/chromium/ui/base/win/on_screen_keyboard_display_manager_tab_tip.h deleted file mode 100644 index b5392c4b686..00000000000 --- a/chromium/ui/base/win/on_screen_keyboard_display_manager_tab_tip.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_BASE_WIN_OSK_DISPLAY_MANAGER_H_ -#define UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_ - -#include "base/memory/weak_ptr.h" -#include "base/observer_list.h" -#include "base/strings/string16.h" -#include "ui/base/ui_base_export.h" -#include "ui/gfx/geometry/rect.h" - -namespace ui { - -class OnScreenKeyboardDetector; -class OnScreenKeyboardObserver; - -// This class provides functionality to display the on screen keyboard on -// Windows 8+. It optionally notifies observers that the OSK is displayed, -// hidden, etc. -class UI_BASE_EXPORT OnScreenKeyboardDisplayManager { - public: - static OnScreenKeyboardDisplayManager* GetInstance(); - - ~OnScreenKeyboardDisplayManager(); - - // Functions to display and dismiss the keyboard. - // The optional |observer| parameter allows callers to be notified when the - // keyboard is displayed, dismissed, etc. - bool DisplayVirtualKeyboard(OnScreenKeyboardObserver* observer); - // When the keyboard is dismissed, the registered observer if any is removed - // after notifying it. - bool DismissVirtualKeyboard(); - - // Removes a registered observer. - void RemoveObserver(OnScreenKeyboardObserver* observer); - - // Returns the path of the on screen keyboard exe (TabTip.exe) in the - // |osk_path| parameter. - // Returns true on success. - bool GetOSKPath(base::string16* osk_path); - - // Returns true if the virtual keyboard is currently visible. - bool IsKeyboardVisible() const; - - private: - OnScreenKeyboardDisplayManager(); - - std::unique_ptr<OnScreenKeyboardDetector> keyboard_detector_; - - // The location of TabTip.exe. - base::string16 osk_path_; - - DISALLOW_COPY_AND_ASSIGN(OnScreenKeyboardDisplayManager); -}; - -} // namespace ui - -#endif // UI_BASE_WIN_OSK_DISPLAY_MANAGER_H_ diff --git a/chromium/ui/base/win/osk_display_manager.cc b/chromium/ui/base/win/osk_display_manager.cc deleted file mode 100644 index bbce7bda6eb..00000000000 --- a/chromium/ui/base/win/osk_display_manager.cc +++ /dev/null @@ -1,379 +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/base/win/osk_display_manager.h" - -#include <windows.h> -#include <shellapi.h> -#include <shlobj.h> -#include <shobjidl.h> // Must be before propkey. - -#include "base/bind.h" -#include "base/debug/leak_annotations.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/single_thread_task_runner.h" -#include "base/strings/string_util.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/win/registry.h" -#include "base/win/scoped_co_mem.h" -#include "base/win/win_util.h" -#include "base/win/windows_version.h" -#include "ui/base/win/hidden_window.h" -#include "ui/base/win/osk_display_observer.h" -#include "ui/display/win/dpi.h" -#include "ui/gfx/geometry/dip_util.h" - -namespace { - -constexpr int kCheckOSKDelayMs = 1000; -constexpr int kDismissKeyboardRetryTimeoutMs = 100; -constexpr int kDismissKeyboardMaxRetries = 5; - -constexpr wchar_t kOSKClassName[] = L"IPTip_Main_Window"; - -constexpr wchar_t kWindows8OSKRegPath[] = - L"Software\\Classes\\CLSID\\{054AAE20-4BEA-4347-8A35-64A533254A9D}" - L"\\LocalServer32"; - -} // namespace - -namespace ui { - -// This class provides functionality to detect when the on screen keyboard -// is displayed and move the main window up if it is obscured by the keyboard. -class OnScreenKeyboardDetector { - public: - OnScreenKeyboardDetector(); - ~OnScreenKeyboardDetector(); - - // Schedules a delayed task which detects if the on screen keyboard was - // displayed. - void DetectKeyboard(HWND main_window); - - // Dismisses the on screen keyboard. If a call to display the keyboard was - // made, this function waits for the keyboard to become visible by retrying - // upto a maximum of kDismissKeyboardMaxRetries. - bool DismissKeyboard(); - - // Add/Remove keyboard observers. - // Please note that this class does not track the |observer| destruction. It - // is upto the classes which set up these observers to remove them when they - // are destroyed. - void AddObserver(OnScreenKeyboardObserver* observer); - void RemoveObserver(OnScreenKeyboardObserver* observer); - - // Returns true if the osk is visible. Sets osk bounding rect if non-null - static bool IsKeyboardVisible(gfx::Rect* osk_bounding_rect); - - private: - // Executes as a task and detects if the on screen keyboard is displayed. - // Once the keyboard is displayed it schedules the HideIfNecessary() task to - // detect when the keyboard is or should be hidden. - void CheckIfKeyboardVisible(); - - // Executes as a task and detects if the keyboard was hidden or should be - // hidden. - void HideIfNecessary(); - - // Notifies observers that the keyboard was displayed. - // A recurring task HideIfNecessary() is started to detect when the OSK - // disappears. - void HandleKeyboardVisible(); - - // Notifies observers that the keyboard was hidden. - // The observer list is cleared out after this notification. - void HandleKeyboardHidden(); - - // Removes all observers from the list. - void ClearObservers(); - - // The main window which displays the on screen keyboard. - HWND main_window_ = nullptr; - - // Tracks if the keyboard was displayed. - bool osk_visible_notification_received_ = false; - - // The keyboard dimensions in pixels. - gfx::Rect osk_rect_pixels_; - - // Set to true if a call to DetectKeyboard() was made. - bool keyboard_detect_requested_ = false; - - // Contains the number of attempts made to dismiss the keyboard. Please refer - // to the DismissKeyboard() function for more information. - int keyboard_dismiss_retry_count_ = 0; - - base::ObserverList<OnScreenKeyboardObserver, false> observers_; - - // Should be the last member in the class. Helps ensure that tasks spawned - // by this class instance are canceled when it is destroyed. - base::WeakPtrFactory<OnScreenKeyboardDetector> keyboard_detector_factory_; - - DISALLOW_COPY_AND_ASSIGN(OnScreenKeyboardDetector); -}; - -// OnScreenKeyboardDetector member definitions. -OnScreenKeyboardDetector::OnScreenKeyboardDetector() - : keyboard_detector_factory_(this) {} - -OnScreenKeyboardDetector::~OnScreenKeyboardDetector() { - ClearObservers(); -} - -void OnScreenKeyboardDetector::DetectKeyboard(HWND main_window) { - main_window_ = main_window; - keyboard_detect_requested_ = true; - // The keyboard is displayed by TabTip.exe which is launched via a - // ShellExecute call in the - // OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard() function. We use - // a delayed task to check if the keyboard is visible because of the possible - // delay between the ShellExecute call and the keyboard becoming visible. - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&OnScreenKeyboardDetector::CheckIfKeyboardVisible, - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); -} - -bool OnScreenKeyboardDetector::DismissKeyboard() { - // We dismiss the virtual keyboard by generating the ESC keystroke - // programmatically. - HWND osk = ::FindWindow(kOSKClassName, nullptr); - if (::IsWindow(osk) && ::IsWindowEnabled(osk)) { - keyboard_detect_requested_ = false; - keyboard_dismiss_retry_count_ = 0; - HandleKeyboardHidden(); - PostMessage(osk, WM_SYSCOMMAND, SC_CLOSE, 0); - return true; - } else if (keyboard_detect_requested_) { - if (keyboard_dismiss_retry_count_ < kDismissKeyboardMaxRetries) { - keyboard_dismiss_retry_count_++; - // Please refer to the comments in the DetectKeyboard() function for more - // information as to why we need a delayed task here. - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(base::IgnoreResult( - &OnScreenKeyboardDetector::DismissKeyboard), - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kDismissKeyboardRetryTimeoutMs)); - } else { - keyboard_dismiss_retry_count_ = 0; - } - } - return false; -} - -void OnScreenKeyboardDetector::AddObserver(OnScreenKeyboardObserver* observer) { - observers_.AddObserver(observer); -} - -void OnScreenKeyboardDetector::RemoveObserver( - OnScreenKeyboardObserver* observer) { - observers_.RemoveObserver(observer); -} - -// static -bool OnScreenKeyboardDetector::IsKeyboardVisible(gfx::Rect* osk_bounding_rect) { - HWND osk = ::FindWindow(kOSKClassName, nullptr); - if (!::IsWindow(osk)) - return false; - if (osk_bounding_rect) { - RECT osk_rect = {}; - ::GetWindowRect(osk, &osk_rect); - *osk_bounding_rect = gfx::Rect(osk_rect); - } - return ::IsWindowVisible(osk) && ::IsWindowEnabled(osk); -} - -void OnScreenKeyboardDetector::CheckIfKeyboardVisible() { - if (IsKeyboardVisible(&osk_rect_pixels_)) { - if (!osk_visible_notification_received_) - HandleKeyboardVisible(); - } else { - DVLOG(1) << "OSK did not come up in 1 second. Something wrong."; - } -} - -void OnScreenKeyboardDetector::HideIfNecessary() { - HWND osk = ::FindWindow(kOSKClassName, nullptr); - if (!::IsWindow(osk)) - return; - - // Three cases here. - // 1. OSK was hidden because the user dismissed it. - // 2. We are no longer in the foreground. - // 3. The OSK is still visible. - // In the first case we just have to notify the observers that the OSK was - // hidden. - // In the second case we need to dismiss the OSK which internally will - // notify the observers about the OSK being hidden. - if (!::IsWindowEnabled(osk)) { - if (osk_visible_notification_received_) { - if (main_window_ == ::GetForegroundWindow()) { - DVLOG(1) << "OSK window hidden while we are in the foreground."; - HandleKeyboardHidden(); - } - } - } else if (main_window_ != ::GetForegroundWindow()) { - if (osk_visible_notification_received_) { - DVLOG(1) << "We are no longer in the foreground. Dismising OSK."; - DismissKeyboard(); - } - } else { - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&OnScreenKeyboardDetector::HideIfNecessary, - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); - } -} - -void OnScreenKeyboardDetector::HandleKeyboardVisible() { - DCHECK(!osk_visible_notification_received_); - osk_visible_notification_received_ = true; - - for (OnScreenKeyboardObserver& observer : observers_) - observer.OnKeyboardVisible(osk_rect_pixels_); - - // Now that the keyboard is visible, run the task to detect if it was hidden. - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&OnScreenKeyboardDetector::HideIfNecessary, - keyboard_detector_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kCheckOSKDelayMs)); -} - -void OnScreenKeyboardDetector::HandleKeyboardHidden() { - osk_visible_notification_received_ = false; - for (OnScreenKeyboardObserver& observer : observers_) - observer.OnKeyboardHidden(osk_rect_pixels_); - ClearObservers(); -} - -void OnScreenKeyboardDetector::ClearObservers() { - for (auto& observer : observers_) - RemoveObserver(&observer); -} - -// OnScreenKeyboardDisplayManager member definitions. -OnScreenKeyboardDisplayManager::OnScreenKeyboardDisplayManager() {} - -OnScreenKeyboardDisplayManager::~OnScreenKeyboardDisplayManager() {} - -OnScreenKeyboardDisplayManager* OnScreenKeyboardDisplayManager::GetInstance() { - static OnScreenKeyboardDisplayManager* instance = nullptr; - if (!instance) { - instance = new OnScreenKeyboardDisplayManager; - ANNOTATE_LEAKING_OBJECT_PTR(instance); - } - return instance; -} - -bool OnScreenKeyboardDisplayManager::DisplayVirtualKeyboard( - OnScreenKeyboardObserver* observer) { - if (base::win::GetVersion() < base::win::VERSION_WIN8) - return false; - - if (base::win::IsKeyboardPresentOnSlate(nullptr, ui::GetHiddenWindow())) - return false; - - if (osk_path_.empty() && !GetOSKPath(&osk_path_)) { - DLOG(WARNING) << "Failed to get on screen keyboard path from registry"; - return false; - } - - HINSTANCE ret = ::ShellExecuteW(nullptr, L"", osk_path_.c_str(), nullptr, - nullptr, SW_SHOW); - - bool success = reinterpret_cast<intptr_t>(ret) > 32; - if (success) { - // If multiple calls to DisplayVirtualKeyboard occur one after the other, - // the last observer would be the one to get notifications. - keyboard_detector_.reset(new OnScreenKeyboardDetector); - if (observer) - keyboard_detector_->AddObserver(observer); - keyboard_detector_->DetectKeyboard(::GetForegroundWindow()); - } - return success; -} - -bool OnScreenKeyboardDisplayManager::DismissVirtualKeyboard() { - if (base::win::GetVersion() < base::win::VERSION_WIN8) - return false; - - return keyboard_detector_ ? keyboard_detector_->DismissKeyboard() : false; -} - -void OnScreenKeyboardDisplayManager::RemoveObserver( - OnScreenKeyboardObserver* observer) { - if (keyboard_detector_) - keyboard_detector_->RemoveObserver(observer); -} - -bool OnScreenKeyboardDisplayManager::GetOSKPath(base::string16* osk_path) { - DCHECK(osk_path); - - // We need to launch TabTip.exe from the location specified under the - // LocalServer32 key for the {{054AAE20-4BEA-4347-8A35-64A533254A9D}} - // CLSID. - // TabTip.exe is typically found at - // c:\program files\common files\microsoft shared\ink on English Windows. - // We don't want to launch TabTip.exe from - // c:\program files (x86)\common files\microsoft shared\ink. This path is - // normally found on 64 bit Windows. - base::win::RegKey key(HKEY_LOCAL_MACHINE, kWindows8OSKRegPath, - KEY_READ | KEY_WOW64_64KEY); - DWORD osk_path_length = 1024; - if (key.ReadValue(nullptr, base::WriteInto(osk_path, osk_path_length), - &osk_path_length, nullptr) != ERROR_SUCCESS) { - return false; - } - - osk_path->resize(base::string16::traits_type::length(osk_path->c_str())); - - *osk_path = base::ToLowerASCII(*osk_path); - - size_t common_program_files_offset = osk_path->find(L"%commonprogramfiles%"); - // Typically the path to TabTip.exe read from the registry will start with - // %CommonProgramFiles% which needs to be replaced with the corrsponding - // expanded string. - // If the path does not begin with %CommonProgramFiles% we use it as is. - if (common_program_files_offset != base::string16::npos) { - // Preserve the beginning quote in the path. - osk_path->erase(common_program_files_offset, - wcslen(L"%commonprogramfiles%")); - // The path read from the registry contains the %CommonProgramFiles% - // environment variable prefix. On 64 bit Windows the SHGetKnownFolderPath - // function returns the common program files path with the X86 suffix for - // the FOLDERID_ProgramFilesCommon value. - // To get the correct path to TabTip.exe we first read the environment - // variable CommonProgramW6432 which points to the desired common - // files path. Failing that we fallback to the SHGetKnownFolderPath API. - - // We then replace the %CommonProgramFiles% value with the actual common - // files path found in the process. - base::string16 common_program_files_path; - DWORD buffer_size = - GetEnvironmentVariable(L"CommonProgramW6432", nullptr, 0); - if (buffer_size) { - GetEnvironmentVariable( - L"CommonProgramW6432", - base::WriteInto(&common_program_files_path, buffer_size), - buffer_size); - DCHECK(!common_program_files_path.empty()); - } else { - base::win::ScopedCoMem<wchar_t> common_program_files; - if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, nullptr, - &common_program_files))) { - return false; - } - common_program_files_path = common_program_files; - } - osk_path->insert(common_program_files_offset, common_program_files_path); - } - return !osk_path->empty(); -} - -bool OnScreenKeyboardDisplayManager::IsKeyboardVisible() const { - return OnScreenKeyboardDetector::IsKeyboardVisible(nullptr); -} - -} // namespace ui diff --git a/chromium/ui/base/win/shell.cc b/chromium/ui/base/win/shell.cc index b5afdead174..e25487ec101 100644 --- a/chromium/ui/base/win/shell.cc +++ b/chromium/ui/base/win/shell.cc @@ -56,13 +56,6 @@ bool InvokeShellExecute(const base::string16 path, } // namespace -bool OpenAnyViaShell(const base::string16& full_path, - const base::string16& directory, - const base::string16& args, - DWORD mask) { - return InvokeShellExecute(full_path, directory, args, base::string16(), mask); -} - bool OpenFileViaShell(const base::FilePath& full_path) { // Invoke the default verb on the file with no arguments. return InvokeShellExecute(full_path.value(), full_path.DirName().value(), diff --git a/chromium/ui/base/win/shell.h b/chromium/ui/base/win/shell.h index c47e5ce3f47..46b625c43c3 100644 --- a/chromium/ui/base/win/shell.h +++ b/chromium/ui/base/win/shell.h @@ -34,17 +34,6 @@ UI_BASE_EXPORT bool OpenFolderViaShell(const base::FilePath& full_path); // Note: Must be called on a thread that allows blocking. UI_BASE_EXPORT bool OpenFileViaShell(const base::FilePath& full_path); -// Lower level function that allows opening of non-files like urls or GUIDs -// don't use it if one of the above will do. |mask| is a valid combination -// of SEE_MASK_XXX as stated in MSDN. If there is no default application -// registered for the item, it behaves the same as OpenFileViaShell. -// -// Note: Must be called on a thread that allows blocking. -UI_BASE_EXPORT bool OpenAnyViaShell(const base::string16& full_path, - const base::string16& directory, - const base::string16& args, - DWORD mask); - // Disables the ability of the specified window to be pinned to the taskbar or // the Start menu. This will remove "Pin this program to taskbar" from the // taskbar menu of the specified window. diff --git a/chromium/ui/base/win/window_event_target.h b/chromium/ui/base/win/window_event_target.h index 3e9240ef97e..0d3e9d3b78e 100644 --- a/chromium/ui/base/win/window_event_target.h +++ b/chromium/ui/base/win/window_event_target.h @@ -89,6 +89,36 @@ class UI_BASE_EXPORT WindowEventTarget { // Notification from the forwarder window that its parent changed. virtual void HandleParentChanged() = 0; + // Apply the transform from Direct Manipulation API. + + // Calls ApplyPinchZoomScale() for pinch-zoom gesture. scale is the scale + // factor. + virtual void ApplyPinchZoomScale(float scale) = 0; + + // Pinch gesture phase. The sequencing expected of these events. + // The sequence of calls is ApplyPinchZoomBegin(), any number of calls to + // ApplyPinchZoomScale() and finally ApplyPinchZoomEnd(). + virtual void ApplyPinchZoomBegin() = 0; + virtual void ApplyPinchZoomEnd() = 0; + + // Calls ApplyPanGestureScroll() for pan gesture, scroll_x and scroll_y are + // pixel precison scroll offset. + virtual void ApplyPanGestureScroll(int scroll_x, int scroll_y) = 0; + + // Calls ApplyPanGestureFling() for pan inertia gesture, scroll_x and scroll_y + // are pixel precison scroll offset. + virtual void ApplyPanGestureFling(int scroll_x, int scroll_y) = 0; + + // Pan gesture phase. The sequencing expected of these events. + // The sequence of calls is ApplyPanGestureScrollBegin(), any number of calls + // to ApplyPanGestureScroll(), ApplyPanGestureScrollEnd(), + // ApplyPanGestureFlingBegin(), any number of calls to ApplyPanGestureFling(), + // and finally ApplyPanGestureFlingEnd(). + virtual void ApplyPanGestureScrollBegin(int scroll_x, int scroll_y) = 0; + virtual void ApplyPanGestureScrollEnd() = 0; + virtual void ApplyPanGestureFlingBegin() = 0; + virtual void ApplyPanGestureFlingEnd() = 0; + protected: WindowEventTarget(); virtual ~WindowEventTarget(); diff --git a/chromium/ui/base/x/selection_owner.cc b/chromium/ui/base/x/selection_owner.cc index cefa609ca64..17b750b6e9f 100644 --- a/chromium/ui/base/x/selection_owner.cc +++ b/chromium/ui/base/x/selection_owner.cc @@ -7,7 +7,6 @@ #include <algorithm> #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "ui/base/x/selection_utils.h" #include "ui/base/x/x11_window_event_manager.h" #include "ui/events/platform/x11/x11_event_source.h" diff --git a/chromium/ui/base/x/selection_requestor.h b/chromium/ui/base/x/selection_requestor.h index 65d944a121c..746a535669d 100644 --- a/chromium/ui/base/x/selection_requestor.h +++ b/chromium/ui/base/x/selection_requestor.h @@ -10,12 +10,12 @@ #include <vector> #include "base/callback.h" -#include "base/event_types.h" #include "base/macros.h" #include "base/memory/ref_counted_memory.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "ui/base/ui_base_export.h" +#include "ui/events/platform_event.h" #include "ui/gfx/x/x11_types.h" namespace ui { diff --git a/chromium/ui/base/x/selection_requestor_unittest.cc b/chromium/ui/base/x/selection_requestor_unittest.cc index faac9a1c419..7cf997ec43d 100644 --- a/chromium/ui/base/x/selection_requestor_unittest.cc +++ b/chromium/ui/base/x/selection_requestor_unittest.cc @@ -11,6 +11,7 @@ #include "base/memory/ref_counted_memory.h" #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/x/selection_utils.h" #include "ui/base/x/x11_util.h" @@ -67,8 +68,8 @@ class SelectionRequestorTest : public testing::Test { 0, NULL); - event_source_ = ui::PlatformEventSource::CreateDefault(); - CHECK(ui::PlatformEventSource::GetInstance()); + event_source_ = PlatformEventSource::CreateDefault(); + CHECK(PlatformEventSource::GetInstance()); requestor_.reset(new SelectionRequestor(x_display_, x_window_, NULL)); } @@ -84,7 +85,7 @@ class SelectionRequestorTest : public testing::Test { // |requestor_|'s window. XID x_window_; - std::unique_ptr<ui::PlatformEventSource> event_source_; + std::unique_ptr<PlatformEventSource> event_source_; std::unique_ptr<SelectionRequestor> requestor_; base::MessageLoopForUI message_loop_; @@ -123,16 +124,15 @@ TEST_F(SelectionRequestorTest, NestedRequests) { XAtom target1 = gfx::GetAtom("TARGET1"); XAtom target2 = gfx::GetAtom("TARGET2"); - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - loop->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&PerformBlockingConvertSelection, base::Unretained(requestor_.get()), selection, target2, "Data2")); - loop->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&SelectionRequestorTest::SendSelectionNotify, base::Unretained(this), selection, target1, "Data1")); - loop->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&SelectionRequestorTest::SendSelectionNotify, base::Unretained(this), selection, target2, "Data2")); diff --git a/chromium/ui/base/x/x11_util.cc b/chromium/ui/base/x/x11_util.cc index 704ba93c06b..5be8a01f335 100644 --- a/chromium/ui/base/x/x11_util.cc +++ b/chromium/ui/base/x/x11_util.cc @@ -73,7 +73,7 @@ constexpr int kNetWMStateRemove = 0; int DefaultX11ErrorHandler(XDisplay* d, XErrorEvent* e) { if (base::MessageLoop::current()) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&LogErrorEventDescription, d, *e)); + FROM_HERE, base::BindOnce(&LogErrorEventDescription, d, *e)); } else { LOG(ERROR) << "X error received: " diff --git a/chromium/ui/base/x/x11_util.h b/chromium/ui/base/x/x11_util.h index a769101b0c8..e7942b98090 100644 --- a/chromium/ui/base/x/x11_util.h +++ b/chromium/ui/base/x/x11_util.h @@ -17,12 +17,12 @@ #include <vector> #include "base/containers/flat_set.h" -#include "base/event_types.h" #include "base/macros.h" #include "base/memory/ref_counted_memory.h" #include "ui/base/x/ui_base_x_export.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/events/platform_event.h" #include "ui/gfx/x/x11_types.h" typedef unsigned long XSharedMemoryId; // ShmSeg in the X headers. diff --git a/chromium/ui/chromeos/BUILD.gn b/chromium/ui/chromeos/BUILD.gn index c15c784e377..913671f1bf8 100644 --- a/chromium/ui/chromeos/BUILD.gn +++ b/chromium/ui/chromeos/BUILD.gn @@ -69,7 +69,7 @@ test("ui_chromeos_unittests") { ":chromeos", "//base/test:test_support", "//chromeos", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gtest", "//ui/aura:test_support", diff --git a/chromium/ui/compositor/BUILD.gn b/chromium/ui/compositor/BUILD.gn index 12788fef517..1e9a4724032 100644 --- a/chromium/ui/compositor/BUILD.gn +++ b/chromium/ui/compositor/BUILD.gn @@ -208,7 +208,7 @@ test("compositor_unittests") { "//components/viz/common", "//components/viz/service", "//components/viz/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gmock", "//testing/gtest", diff --git a/chromium/ui/compositor/OWNERS b/chromium/ui/compositor/OWNERS index 8bfe0cdad55..37217a38629 100644 --- a/chromium/ui/compositor/OWNERS +++ b/chromium/ui/compositor/OWNERS @@ -1,5 +1,7 @@ danakj@chromium.org +flackr@chromium.org piman@chromium.org +smcgruer@chromium.org vollick@chromium.org # Animation diff --git a/chromium/ui/compositor/callback_layer_animation_observer_unittest.cc b/chromium/ui/compositor/callback_layer_animation_observer_unittest.cc index 6ea60b7fd37..eff7ee4de18 100644 --- a/chromium/ui/compositor/callback_layer_animation_observer_unittest.cc +++ b/chromium/ui/compositor/callback_layer_animation_observer_unittest.cc @@ -8,7 +8,6 @@ #include "base/bind.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/test/layer_animation_observer_test_api.h" diff --git a/chromium/ui/compositor/compositor.cc b/chromium/ui/compositor/compositor.cc index c843ef6c644..ce16a0a14ee 100644 --- a/chromium/ui/compositor/compositor.cc +++ b/chromium/ui/compositor/compositor.cc @@ -336,9 +336,15 @@ void Compositor::ScheduleRedrawRect(const gfx::Rect& damage_rect) { } void Compositor::DisableSwapUntilResize() { + DCHECK(context_factory_private_); context_factory_private_->ResizeDisplay(this, gfx::Size()); } +void Compositor::ReenableSwap() { + DCHECK(context_factory_private_); + context_factory_private_->ResizeDisplay(this, size_); +} + void Compositor::SetLatencyInfo(const ui::LatencyInfo& latency_info) { std::unique_ptr<cc::SwapPromise> swap_promise( new cc::LatencyInfoSwapPromise(latency_info)); diff --git a/chromium/ui/compositor/compositor.h b/chromium/ui/compositor/compositor.h index 78b36146063..a6a0cb9df02 100644 --- a/chromium/ui/compositor/compositor.h +++ b/chromium/ui/compositor/compositor.h @@ -260,6 +260,7 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, // Finishes all outstanding rendering and disables swapping on this surface // until it is resized. void DisableSwapUntilResize(); + void ReenableSwap(); void SetLatencyInfo(const LatencyInfo& latency_info); diff --git a/chromium/ui/compositor/layer.cc b/chromium/ui/compositor/layer.cc index ffd6f93c307..19a12d26e15 100644 --- a/chromium/ui/compositor/layer.cc +++ b/chromium/ui/compositor/layer.cc @@ -113,7 +113,8 @@ Layer::Layer() device_scale_factor_(1.0f), cache_render_surface_requests_(0), deferred_paint_requests_(0), - trilinear_filtering_request_(0) { + trilinear_filtering_request_(0), + weak_ptr_factory_(this) { CreateCcLayer(); } @@ -140,7 +141,8 @@ Layer::Layer(LayerType type) device_scale_factor_(1.0f), cache_render_surface_requests_(0), deferred_paint_requests_(0), - trilinear_filtering_request_(0) { + trilinear_filtering_request_(0), + weak_ptr_factory_(this) { CreateCcLayer(); } @@ -193,7 +195,8 @@ std::unique_ptr<Layer> Layer::Clone() const { surface_layer_->deadline_in_frames() ? cc::DeadlinePolicy::UseSpecifiedDeadline( *surface_layer_->deadline_in_frames()) - : cc::DeadlinePolicy::UseDefaultDeadline()); + : cc::DeadlinePolicy::UseDefaultDeadline(), + surface_layer_->stretch_content_to_fill_bounds()); } if (surface_layer_->fallback_surface_id().is_valid()) clone->SetFallbackSurfaceId(surface_layer_->fallback_surface_id()); @@ -627,7 +630,7 @@ void Layer::SwitchToLayer(scoped_refptr<cc::Layer> new_layer) { DCHECK(child->cc_layer_); cc_layer_->AddChild(child->cc_layer_); } - cc_layer_->SetLayerClient(this); + cc_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr()); cc_layer_->SetTransformOrigin(gfx::Point3F()); cc_layer_->SetContentsOpaque(fills_bounds_opaquely_); cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN); @@ -706,6 +709,11 @@ void Layer::RemoveTrilinearFilteringRequest() { cc_layer_->SetTrilinearFiltering(false); } +bool Layer::StretchContentToFillBounds() const { + DCHECK(surface_layer_); + return surface_layer_->stretch_content_to_fill_bounds(); +} + void Layer::SetTransferableResource( const viz::TransferableResource& resource, std::unique_ptr<viz::SingleReleaseCallback> release_callback, @@ -713,6 +721,7 @@ void Layer::SetTransferableResource( DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR); DCHECK(!resource.mailbox_holder.mailbox.IsZero()); DCHECK(release_callback); + DCHECK(!resource.is_software); if (!texture_layer_.get()) { scoped_refptr<cc::TextureLayer> new_layer = cc::TextureLayer::CreateForMailbox(this); @@ -752,25 +761,28 @@ bool Layer::TextureFlipped() const { void Layer::SetShowPrimarySurface(const viz::SurfaceId& surface_id, const gfx::Size& frame_size_in_dip, SkColor default_background_color, - const cc::DeadlinePolicy& deadline_policy) { + const cc::DeadlinePolicy& deadline_policy, + bool stretch_content_to_fill_bounds) { DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR); if (!surface_layer_) { scoped_refptr<cc::SurfaceLayer> new_layer = cc::SurfaceLayer::Create(); + new_layer->SetHitTestable(true); SwitchToLayer(new_layer); surface_layer_ = new_layer; } surface_layer_->SetPrimarySurfaceId(surface_id, deadline_policy); surface_layer_->SetBackgroundColor(default_background_color); + surface_layer_->SetStretchContentToFillBounds(stretch_content_to_fill_bounds); frame_size_in_dip_ = frame_size_in_dip; RecomputeDrawsContentAndUVRect(); for (const auto& mirror : mirrors_) { - mirror->dest()->SetShowPrimarySurface(surface_id, frame_size_in_dip, - default_background_color, - deadline_policy); + mirror->dest()->SetShowPrimarySurface( + surface_id, frame_size_in_dip, default_background_color, + deadline_policy, stretch_content_to_fill_bounds); } } @@ -1015,6 +1027,7 @@ size_t Layer::GetApproximateUnsharedMemoryUsage() const { } bool Layer::PrepareTransferableResource( + cc::SharedBitmapIdRegistrar* bitmap_registar, viz::TransferableResource* resource, std::unique_ptr<viz::SingleReleaseCallback>* release_callback) { if (!transfer_release_callback_) @@ -1046,7 +1059,7 @@ Layer::TakeDebugInfo(cc::Layer* layer) { } void Layer::didUpdateMainThreadScrollingReasons() {} -void Layer::didChangeScrollbarsHidden(bool) {} +void Layer::didChangeScrollbarsHiddenIfOverlay(bool) {} void Layer::CollectAnimators( std::vector<scoped_refptr<LayerAnimator>>* animators) { @@ -1132,9 +1145,10 @@ void Layer::SetBoundsFromAnimation(const gfx::Rect& bounds, void Layer::SetTransformFromAnimation(const gfx::Transform& transform, PropertyChangeReason reason) { + const gfx::Transform old_transform = this->transform(); cc_layer_->SetTransform(transform); if (delegate_) - delegate_->OnLayerTransformed(reason); + delegate_->OnLayerTransformed(old_transform, reason); } void Layer::SetOpacityFromAnimation(float opacity, @@ -1254,7 +1268,7 @@ void Layer::CreateCcLayer() { cc_layer_->SetTransformOrigin(gfx::Point3F()); cc_layer_->SetContentsOpaque(true); cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN); - cc_layer_->SetLayerClient(this); + cc_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr()); cc_layer_->SetElementId(cc::ElementId(cc_layer_->id())); RecomputePosition(); } diff --git a/chromium/ui/compositor/layer.h b/chromium/ui/compositor/layer.h index 8200c6d43be..736bd3c2bec 100644 --- a/chromium/ui/compositor/layer.h +++ b/chromium/ui/compositor/layer.h @@ -14,6 +14,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/observer_list.h" #include "cc/base/region.h" @@ -292,8 +293,8 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, const std::string& name() const { return name_; } void set_name(const std::string& name) { name_ = name; } - // Set new TransferableResource for this layer. Note that |resource| may hold - // a handle for a shared memory resource or a gpu texture. + // Set new TransferableResource for this layer. This method only supports + // a gpu-backed |resource|. void SetTransferableResource( const viz::TransferableResource& resource, std::unique_ptr<viz::SingleReleaseCallback> release_callback, @@ -307,7 +308,8 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, void SetShowPrimarySurface(const viz::SurfaceId& surface_id, const gfx::Size& frame_size_in_dip, SkColor default_background_color, - const cc::DeadlinePolicy& deadline_policy); + const cc::DeadlinePolicy& deadline_policy, + bool stretch_content_to_fill_bounds); // In the event that the primary surface is not yet available in the // display compositor, the fallback surface will be used. @@ -394,6 +396,7 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, // TextureLayerClient implementation. bool PrepareTransferableResource( + cc::SharedBitmapIdRegistrar* bitmap_registar, viz::TransferableResource* resource, std::unique_ptr<viz::SingleReleaseCallback>* release_callback) override; @@ -403,7 +406,7 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, std::unique_ptr<base::trace_event::ConvertableToTraceFormat> TakeDebugInfo( cc::Layer* layer) override; void didUpdateMainThreadScrollingReasons() override; - void didChangeScrollbarsHidden(bool) override; + void didChangeScrollbarsHiddenIfOverlay(bool) override; // Triggers a call to SwitchToLayer. void SwitchCCLayerForTest(); @@ -438,6 +441,10 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, // while attached to the main layer before the main layer is deleted. const Layer* layer_mask_back_link() const { return layer_mask_back_link_; } + // If |surface_layer_| exists, return whether the contents should stretch to + // fill the bounds of |this|. Defaults to false. + bool StretchContentToFillBounds() const; + private: friend class LayerOwner; class LayerMirror; @@ -618,6 +625,8 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, // layer. unsigned trilinear_filtering_request_; + base::WeakPtrFactory<Layer> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(Layer); }; diff --git a/chromium/ui/compositor/layer_animation_element.cc b/chromium/ui/compositor/layer_animation_element.cc index 38bd26b8a60..3d965dd000c 100644 --- a/chromium/ui/compositor/layer_animation_element.cc +++ b/chromium/ui/compositor/layer_animation_element.cc @@ -8,7 +8,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "cc/animation/animation_id_provider.h" #include "cc/animation/keyframe_model.h" diff --git a/chromium/ui/compositor/layer_animator_unittest.cc b/chromium/ui/compositor/layer_animator_unittest.cc index 6dc9a0cadf0..389cedcbfdc 100644 --- a/chromium/ui/compositor/layer_animator_unittest.cc +++ b/chromium/ui/compositor/layer_animator_unittest.cc @@ -10,7 +10,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ui/compositor/layer_delegate.cc b/chromium/ui/compositor/layer_delegate.cc index d2f8da54fe8..153c7f5c47e 100644 --- a/chromium/ui/compositor/layer_delegate.cc +++ b/chromium/ui/compositor/layer_delegate.cc @@ -9,7 +9,8 @@ namespace ui { void LayerDelegate::OnLayerBoundsChanged(const gfx::Rect& old_bounds, PropertyChangeReason reason) {} -void LayerDelegate::OnLayerTransformed(PropertyChangeReason reason) {} +void LayerDelegate::OnLayerTransformed(const gfx::Transform& old_transform, + PropertyChangeReason reason) {} void LayerDelegate::OnLayerOpacityChanged(PropertyChangeReason reason) {} diff --git a/chromium/ui/compositor/layer_delegate.h b/chromium/ui/compositor/layer_delegate.h index eb8dc714362..1b3c40b4804 100644 --- a/chromium/ui/compositor/layer_delegate.h +++ b/chromium/ui/compositor/layer_delegate.h @@ -10,6 +10,7 @@ namespace gfx { class Rect; +class Transform; } namespace ui { @@ -35,7 +36,8 @@ class COMPOSITOR_EXPORT LayerDelegate { // the property was set directly or by an animation. This will be called // before the first frame of an animation is rendered and when the animation // ends, but not necessarily at every frame of the animation. - virtual void OnLayerTransformed(PropertyChangeReason reason); + virtual void OnLayerTransformed(const gfx::Transform& old_transform, + PropertyChangeReason reason); virtual void OnLayerOpacityChanged(PropertyChangeReason reason); protected: diff --git a/chromium/ui/compositor/layer_owner.cc b/chromium/ui/compositor/layer_owner.cc index 2806e81b6b0..ed68ad2b989 100644 --- a/chromium/ui/compositor/layer_owner.cc +++ b/chromium/ui/compositor/layer_owner.cc @@ -6,7 +6,6 @@ #include <utility> -#include "base/memory/ptr_util.h" namespace ui { diff --git a/chromium/ui/compositor/layer_owner_unittest.cc b/chromium/ui/compositor/layer_owner_unittest.cc index 3a6b60b7083..2e6fb209019 100644 --- a/chromium/ui/compositor/layer_owner_unittest.cc +++ b/chromium/ui/compositor/layer_owner_unittest.cc @@ -7,7 +7,6 @@ #include <utility> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/test/null_task_runner.h" #include "cc/animation/single_keyframe_effect_animation.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ui/compositor/layer_unittest.cc b/chromium/ui/compositor/layer_unittest.cc index a544b33d938..c56eee9bbfc 100644 --- a/chromium/ui/compositor/layer_unittest.cc +++ b/chromium/ui/compositor/layer_unittest.cc @@ -15,7 +15,6 @@ #include "base/files/file_util.h" #include "base/json/json_reader.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" @@ -33,6 +32,7 @@ #include "components/viz/common/frame_sinks/copy_output_request.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/common/resources/transferable_resource.h" +#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" #include "components/viz/common/surfaces/surface_id.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -314,7 +314,8 @@ class TestLayerDelegate : public LayerDelegate { MOCK_METHOD2(OnLayerBoundsChanged, void(const gfx::Rect&, PropertyChangeReason)); - MOCK_METHOD1(OnLayerTransformed, void(PropertyChangeReason)); + MOCK_METHOD2(OnLayerTransformed, + void(const gfx::Transform&, PropertyChangeReason)); MOCK_METHOD1(OnLayerOpacityChanged, void(PropertyChangeReason)); void reset() { @@ -571,10 +572,11 @@ TEST(LayerStandaloneTest, ReleaseMailboxOnDestruction) { bool callback_run = false; auto resource = viz::TransferableResource::MakeGL( gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken()); - layer->SetTransferableResource(resource, - viz::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, &callback_run)), - gfx::Size(10, 10)); + layer->SetTransferableResource( + resource, + viz::SingleReleaseCallback::Create( + base::BindOnce(ReturnMailbox, &callback_run)), + gfx::Size(10, 10)); EXPECT_FALSE(callback_run); layer.reset(); EXPECT_TRUE(callback_run); @@ -891,6 +893,48 @@ TEST_F(LayerWithDelegateTest, Mirroring) { EXPECT_EQ(new_bounds, mirror->bounds()); } +// Tests for SurfaceLayer cloning and mirroring. This tests certain properties +// are preserved. +TEST_F(LayerWithDelegateTest, SurfaceLayerCloneAndMirror) { + const viz::FrameSinkId arbitrary_frame_sink(1, 1); + viz::ParentLocalSurfaceIdAllocator allocator; + std::unique_ptr<Layer> layer(CreateLayer(LAYER_SOLID_COLOR)); + + viz::LocalSurfaceId local_surface_id = allocator.GenerateId(); + viz::SurfaceId surface_id_one(arbitrary_frame_sink, local_surface_id); + 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()) + ->hit_testable()); + + auto clone = layer->Clone(); + EXPECT_FALSE(clone->StretchContentToFillBounds()); + EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(clone->cc_layer_for_testing()) + ->hit_testable()); + auto mirror = layer->Mirror(); + EXPECT_FALSE(mirror->StretchContentToFillBounds()); + EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(mirror->cc_layer_for_testing()) + ->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()) + ->hit_testable()); + + clone = layer->Clone(); + EXPECT_TRUE(clone->StretchContentToFillBounds()); + EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(clone->cc_layer_for_testing()) + ->hit_testable()); + mirror = layer->Mirror(); + EXPECT_TRUE(mirror->StretchContentToFillBounds()); + EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(mirror->cc_layer_for_testing()) + ->hit_testable()); +} + class LayerWithNullDelegateTest : public LayerWithDelegateTest { public: LayerWithNullDelegateTest() {} @@ -973,8 +1017,8 @@ TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { auto resource = viz::TransferableResource::MakeGL( gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken()); l1->SetTransferableResource(resource, - viz::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, &callback1_run)), + viz::SingleReleaseCallback::Create(base::BindOnce( + ReturnMailbox, &callback1_run)), gfx::Size(10, 10)); EXPECT_NE(before_layer, l1->cc_layer_for_testing()); @@ -990,8 +1034,8 @@ TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { resource = viz::TransferableResource::MakeGL( gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken()); l1->SetTransferableResource(resource, - viz::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, &callback2_run)), + viz::SingleReleaseCallback::Create(base::BindOnce( + ReturnMailbox, &callback2_run)), gfx::Size(10, 10)); EXPECT_TRUE(callback1_run); EXPECT_FALSE(callback2_run); @@ -1012,8 +1056,8 @@ TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { resource = viz::TransferableResource::MakeGL( gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken()); l1->SetTransferableResource(resource, - viz::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, &callback3_run)), + viz::SingleReleaseCallback::Create(base::BindOnce( + ReturnMailbox, &callback3_run)), gfx::Size(10, 10)); EXPECT_NE(before_layer, l1->cc_layer_for_testing()); @@ -1159,10 +1203,10 @@ TEST_F(LayerWithNullDelegateTest, SetBoundsSchedulesPaint) { // Checks that the damage rect for a TextureLayer is empty after a commit. TEST_F(LayerWithNullDelegateTest, EmptyDamagedRect) { base::RunLoop run_loop; - viz::ReleaseCallback callback = - base::Bind([](base::RunLoop* run_loop, const gpu::SyncToken& sync_token, - bool is_lost) { run_loop->Quit(); }, - base::Unretained(&run_loop)); + viz::ReleaseCallback callback = base::BindOnce( + [](base::RunLoop* run_loop, const gpu::SyncToken& sync_token, + bool is_lost) { run_loop->Quit(); }, + base::Unretained(&run_loop)); std::unique_ptr<Layer> root(CreateLayer(LAYER_SOLID_COLOR)); auto resource = viz::TransferableResource::MakeGL( @@ -1851,23 +1895,17 @@ TEST_F(LayerWithDelegateTest, ExternalContent) { before = child->cc_layer_for_testing(); child->SetShowPrimarySurface(viz::SurfaceId(), gfx::Size(10, 10), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), false); scoped_refptr<cc::Layer> after = child->cc_layer_for_testing(); const auto* surface = static_cast<cc::SurfaceLayer*>(after.get()); EXPECT_TRUE(after.get()); EXPECT_NE(before.get(), after.get()); EXPECT_EQ(base::nullopt, surface->deadline_in_frames()); - child->SetShowPrimarySurface(viz::SurfaceId(), gfx::Size(10, 10), - SK_ColorWHITE, - cc::DeadlinePolicy::UseSpecifiedDeadline(4u)); + child->SetShowPrimarySurface( + viz::SurfaceId(), gfx::Size(10, 10), SK_ColorWHITE, + cc::DeadlinePolicy::UseSpecifiedDeadline(4u), false); EXPECT_EQ(4u, surface->deadline_in_frames()); - - // Changing to painted content should change the underlying cc layer. - before = child->cc_layer_for_testing(); - child->SetShowSolidColorContent(); - EXPECT_TRUE(child->cc_layer_for_testing()); - EXPECT_NE(before.get(), child->cc_layer_for_testing()); } TEST_F(LayerWithDelegateTest, ExternalContentMirroring) { @@ -1877,7 +1915,7 @@ TEST_F(LayerWithDelegateTest, ExternalContentMirroring) { viz::FrameSinkId(0, 1), viz::LocalSurfaceId(2, base::UnguessableToken::Create())); layer->SetShowPrimarySurface(surface_id, gfx::Size(10, 10), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), false); const auto mirror = layer->Mirror(); auto* const cc_layer = mirror->cc_layer_for_testing(); @@ -1890,12 +1928,12 @@ TEST_F(LayerWithDelegateTest, ExternalContentMirroring) { viz::SurfaceId(viz::FrameSinkId(1, 2), viz::LocalSurfaceId(3, base::UnguessableToken::Create())); layer->SetShowPrimarySurface(surface_id, gfx::Size(20, 20), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), false); // The mirror should continue to use the same cc_layer. EXPECT_EQ(cc_layer, mirror->cc_layer_for_testing()); layer->SetShowPrimarySurface(surface_id, gfx::Size(20, 20), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), false); // Surface updates propagate to the mirror. EXPECT_EQ(surface_id, surface->primary_surface_id()); @@ -1917,7 +1955,7 @@ TEST_F(LayerWithDelegateTest, LayerFiltersSurvival) { scoped_refptr<cc::Layer> before = layer->cc_layer_for_testing(); layer->SetShowPrimarySurface(viz::SurfaceId(), gfx::Size(10, 10), SK_ColorWHITE, - cc::DeadlinePolicy::UseDefaultDeadline()); + cc::DeadlinePolicy::UseDefaultDeadline(), false); EXPECT_EQ(layer->layer_grayscale(), 0.5f); EXPECT_TRUE(layer->cc_layer_for_testing()); EXPECT_NE(before.get(), layer->cc_layer_for_testing()); @@ -2374,16 +2412,32 @@ TEST(LayerDelegateTest, OnLayerTransformed) { auto layer = std::make_unique<Layer>(LAYER_TEXTURED); testing::StrictMock<TestLayerDelegate> delegate; layer->set_delegate(&delegate); - gfx::Transform target_transform; - target_transform.Skew(10.0f, 5.0f); + gfx::Transform target_transform1; + target_transform1.Skew(10.0f, 5.0f); + { + EXPECT_CALL(delegate, + OnLayerTransformed(gfx::Transform(), + PropertyChangeReason::NOT_FROM_ANIMATION)) + .WillOnce(testing::Invoke( + [&](const gfx::Transform& old_transform, PropertyChangeReason) { + // Verify that |layer->transform()| returns the correct value when + // the delegate is notified. + EXPECT_EQ(target_transform1, layer->transform()); + })); + layer->SetTransform(target_transform1); + } + gfx::Transform target_transform2; + target_transform2.Skew(10.0f, 5.0f); EXPECT_CALL(delegate, - OnLayerTransformed(PropertyChangeReason::NOT_FROM_ANIMATION)) - .WillOnce(testing::Invoke([&](PropertyChangeReason) { - // Verify that |layer->transform()| returns the correct value when the - // delegate is notified. - EXPECT_EQ(layer->transform(), target_transform); - })); - layer->SetTransform(target_transform); + OnLayerTransformed(target_transform1, + PropertyChangeReason::NOT_FROM_ANIMATION)) + .WillOnce(testing::Invoke( + [&](const gfx::Transform& old_transform, PropertyChangeReason) { + // Verify that |layer->transform()| returns the correct value when + // the delegate is notified. + EXPECT_EQ(target_transform2, layer->transform()); + })); + layer->SetTransform(target_transform2); } // Verify that LayerDelegate::OnLayerTransformed() is called at every step of a @@ -2417,8 +2471,10 @@ TEST(LayerDelegateTest, OnLayerTransformedNonThreadedAnimation) { ASSERT_FALSE(element->IsThreaded(layer.get())); LayerAnimationElement* element_raw = element.get(); EXPECT_CALL(delegate, - OnLayerTransformed(PropertyChangeReason::FROM_ANIMATION)) - .WillOnce(testing::Invoke([&](PropertyChangeReason) { + OnLayerTransformed(gfx::Transform(), + PropertyChangeReason::FROM_ANIMATION)) + .WillOnce(testing::Invoke([&](const gfx::Transform& old_transform, + PropertyChangeReason) { // Verify that |layer->transform()| returns the correct value when the // delegate is notified. EXPECT_EQ(layer->transform(), initial_transform); @@ -2430,19 +2486,23 @@ TEST(LayerDelegateTest, OnLayerTransformedNonThreadedAnimation) { // Progress the animation. EXPECT_CALL(delegate, - OnLayerTransformed(PropertyChangeReason::FROM_ANIMATION)) - .WillOnce(testing::Invoke([&](PropertyChangeReason) { - // Verify that |layer->transform()| returns the correct value when the - // delegate is notified. - EXPECT_EQ(layer->transform(), step_transform); - })); + OnLayerTransformed(initial_transform, + PropertyChangeReason::FROM_ANIMATION)) + .WillOnce(testing::Invoke( + [&](const gfx::Transform& old_transform, PropertyChangeReason) { + // Verify that |layer->transform()| returns the correct value when + // the delegate is notified. + EXPECT_EQ(layer->transform(), step_transform); + })); test_controller.Step(element_raw->duration() / 2); testing::Mock::VerifyAndClear(&delegate); // End the animation. - EXPECT_CALL(delegate, - OnLayerTransformed(PropertyChangeReason::FROM_ANIMATION)) - .WillOnce(testing::Invoke([&](PropertyChangeReason) { + EXPECT_CALL( + delegate, + OnLayerTransformed(step_transform, PropertyChangeReason::FROM_ANIMATION)) + .WillOnce(testing::Invoke([&](const gfx::Transform& old_transform, + PropertyChangeReason) { // Verify that |layer->transform()| returns the correct value when the // delegate is notified. EXPECT_EQ(layer->transform(), target_transform); @@ -2477,8 +2537,10 @@ TEST(LayerDelegateTest, OnLayerTransformedThreadedAnimation) { ASSERT_TRUE(element->IsThreaded(layer.get())); LayerAnimationElement* element_raw = element.get(); EXPECT_CALL(delegate, - OnLayerTransformed(PropertyChangeReason::FROM_ANIMATION)) - .WillOnce(testing::Invoke([&](PropertyChangeReason) { + OnLayerTransformed(gfx::Transform(), + PropertyChangeReason::FROM_ANIMATION)) + .WillOnce(testing::Invoke([&](const gfx::Transform& old_transform, + PropertyChangeReason) { // Verify that |layer->transform()| returns the correct value when the // delegate is notified. EXPECT_EQ(layer->transform(), initial_transform); @@ -2491,8 +2553,10 @@ TEST(LayerDelegateTest, OnLayerTransformedThreadedAnimation) { // End the animation. EXPECT_CALL(delegate, - OnLayerTransformed(PropertyChangeReason::FROM_ANIMATION)) - .WillOnce(testing::Invoke([&](PropertyChangeReason) { + OnLayerTransformed(initial_transform, + PropertyChangeReason::FROM_ANIMATION)) + .WillOnce(testing::Invoke([&](const gfx::Transform& old_transform, + PropertyChangeReason) { // Verify that |layer->transform()| returns the correct value when the // delegate is notified. EXPECT_EQ(layer->transform(), target_transform); diff --git a/chromium/ui/compositor/run_all_unittests.cc b/chromium/ui/compositor/run_all_unittests.cc index e2f22adc452..40b96f9542d 100644 --- a/chromium/ui/compositor/run_all_unittests.cc +++ b/chromium/ui/compositor/run_all_unittests.cc @@ -13,6 +13,7 @@ int main(int argc, char** argv) { mojo::edk::Init(); return base::LaunchUnitTests( - argc, argv, base::Bind(&ui::test::CompositorTestSuite::Run, - base::Unretained(&test_suite))); + argc, argv, + base::BindOnce(&ui::test::CompositorTestSuite::Run, + base::Unretained(&test_suite))); } diff --git a/chromium/ui/compositor_extra/BUILD.gn b/chromium/ui/compositor_extra/BUILD.gn new file mode 100644 index 00000000000..ef59ba4b804 --- /dev/null +++ b/chromium/ui/compositor_extra/BUILD.gn @@ -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. + +source_set("compositor_extra") { + sources = [ + "shadow.cc", + "shadow.h", + ] + + deps = [ + "//base", + "//ui/base", + "//ui/compositor", + "//ui/gfx", + "//ui/gfx/geometry", + ] +} diff --git a/chromium/ui/compositor_extra/DEPS b/chromium/ui/compositor_extra/DEPS new file mode 100644 index 00000000000..ed9f0a5c635 --- /dev/null +++ b/chromium/ui/compositor_extra/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+ui/base", + "+ui/compositor", + "+ui/gfx", +] diff --git a/chromium/ui/wm/core/shadow.cc b/chromium/ui/compositor_extra/shadow.cc index 20def6b8976..d14b7e35f15 100644 --- a/chromium/ui/wm/core/shadow.cc +++ b/chromium/ui/compositor_extra/shadow.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/wm/core/shadow.h" +#include "ui/compositor_extra/shadow.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" @@ -10,7 +10,7 @@ #include "ui/gfx/geometry/insets.h" #include "ui/gfx/shadow_util.h" -namespace wm { +namespace ui { namespace { @@ -173,4 +173,4 @@ void Shadow::UpdateLayerBounds() { blur_region.height())); } -} // namespace wm +} // namespace ui diff --git a/chromium/ui/wm/core/shadow.h b/chromium/ui/compositor_extra/shadow.h index 4c224a20b45..fb47899953c 100644 --- a/chromium/ui/wm/core/shadow.h +++ b/chromium/ui/compositor_extra/shadow.h @@ -2,15 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_WM_CORE_SHADOW_H_ -#define UI_WM_CORE_SHADOW_H_ +#ifndef UI_COMPOSITOR_EXTRA_SHADOW_H_ +#define UI_COMPOSITOR_EXTRA_SHADOW_H_ #include <memory> #include "base/macros.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/gfx/geometry/rect.h" -#include "ui/wm/core/wm_core_export.h" namespace gfx { struct ShadowDetails; @@ -18,12 +17,9 @@ struct ShadowDetails; namespace ui { class Layer; -} // namespace ui - -namespace wm { // Simple class that draws a drop shadow around content at given bounds. -class WM_CORE_EXPORT Shadow : public ui::ImplicitAnimationObserver { +class Shadow : public ui::ImplicitAnimationObserver { public: Shadow(); ~Shadow() override; @@ -103,6 +99,6 @@ class WM_CORE_EXPORT Shadow : public ui::ImplicitAnimationObserver { DISALLOW_COPY_AND_ASSIGN(Shadow); }; -} // namespace wm +} // namespace ui -#endif // UI_WM_CORE_SHADOW_H_ +#endif // UI_COMPOSITOR_EXTRA_SHADOW_H_ diff --git a/chromium/ui/wm/core/shadow_unittest.cc b/chromium/ui/compositor_extra/shadow_unittest.cc index 28cef9b62ac..10e6bfec525 100644 --- a/chromium/ui/wm/core/shadow_unittest.cc +++ b/chromium/ui/compositor_extra/shadow_unittest.cc @@ -2,14 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/wm/core/shadow.h" +#include "ui/compositor_extra/shadow.h" #include "base/macros.h" -#include "ui/aura/test/aura_test_base.h" +#include "base/test/test_discardable_memory_allocator.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/gfx/geometry/insets.h" #include "ui/gfx/shadow_util.h" #include "ui/gfx/shadow_value.h" -namespace wm { +namespace ui { namespace { constexpr int kElevationLarge = 24; @@ -28,10 +32,30 @@ gfx::Size NineboxImageSizeForElevationAndCornerRadius(int elevation, return bounds.size(); } -using ShadowTest = aura::test::AuraTestBase; +class ShadowTest : public testing::Test { + protected: + ShadowTest() {} + ~ShadowTest() override {} + + void SetUp() override { + base::DiscardableMemoryAllocator::SetInstance( + &discardable_memory_allocator_); + } + + void TearDown() override { + base::DiscardableMemoryAllocator::SetInstance(nullptr); + } + + private: + base::TestDiscardableMemoryAllocator discardable_memory_allocator_; + + DISALLOW_COPY_AND_ASSIGN(ShadowTest); +}; // Test if the proper content bounds is calculated based on the current style. TEST_F(ShadowTest, SetContentBounds) { + ScopedAnimationDurationScaleMode zero_duration_mode( + ScopedAnimationDurationScaleMode::ZERO_DURATION); // Verify that layer bounds are outset from content bounds. Shadow shadow; { @@ -103,4 +127,4 @@ TEST_F(ShadowTest, AdjustRoundedCornerRadius) { } } // namespace -} // namespace wm +} // namespace ui diff --git a/chromium/ui/content_accelerators/BUILD.gn b/chromium/ui/content_accelerators/BUILD.gn index d464721ac66..74e8747ac69 100644 --- a/chromium/ui/content_accelerators/BUILD.gn +++ b/chromium/ui/content_accelerators/BUILD.gn @@ -9,7 +9,7 @@ source_set("content_accelerators") { ] deps = [ - "//third_party/WebKit/public:blink_headers", + "//third_party/blink/public:blink_headers", "//ui/base", "//ui/events", "//ui/events/blink", diff --git a/chromium/ui/content_accelerators/DEPS b/chromium/ui/content_accelerators/DEPS index 9e6e70d07c3..be517913e71 100644 --- a/chromium/ui/content_accelerators/DEPS +++ b/chromium/ui/content_accelerators/DEPS @@ -2,5 +2,5 @@ include_rules = [ "+content/public", "+ui/base", "+ui/events", - "+third_party/WebKit/public/platform/WebInputEvent.h", + "+third_party/blink/public/platform/web_input_event.h", ] diff --git a/chromium/ui/content_accelerators/accelerator_util.cc b/chromium/ui/content_accelerators/accelerator_util.cc index 6705f807b10..09c3be2f90c 100644 --- a/chromium/ui/content_accelerators/accelerator_util.cc +++ b/chromium/ui/content_accelerators/accelerator_util.cc @@ -5,7 +5,7 @@ #include "ui/content_accelerators/accelerator_util.h" #include "build/build_config.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" diff --git a/chromium/ui/display/BUILD.gn b/chromium/ui/display/BUILD.gn index 381e32449ff..c19111efec9 100644 --- a/chromium/ui/display/BUILD.gn +++ b/chromium/ui/display/BUILD.gn @@ -113,6 +113,8 @@ static_library("test_support") { "test/display_matchers.cc", "test/display_matchers.h", "test/display_test_util.h", + "test/scoped_screen_override.cc", + "test/scoped_screen_override.h", "test/test_screen.cc", "test/test_screen.h", "win/test/screen_util_win.cc", @@ -146,6 +148,7 @@ test("display_unittests") { "manager/chromeos/configure_displays_task_unittest.cc", "manager/chromeos/display_change_observer_unittest.cc", "manager/chromeos/display_configurator_unittest.cc", + "manager/chromeos/display_utils_unittest.cc", "manager/chromeos/query_content_protection_task_unittest.cc", "manager/chromeos/touch_device_manager_unittest.cc", "manager/chromeos/touch_transform_controller_unittest.cc", diff --git a/chromium/ui/display/OWNERS b/chromium/ui/display/OWNERS index e60ce6577fc..0101ef32916 100644 --- a/chromium/ui/display/OWNERS +++ b/chromium/ui/display/OWNERS @@ -1,4 +1,7 @@ +afakhry@chromium.org derat@chromium.org +dnicoara@chromium.org marcheu@chromium.org oshima@chromium.org -afakhry@chromium.org + +# COMPONENT: UI diff --git a/chromium/ui/display/display.cc b/chromium/ui/display/display.cc index 9a10474c49a..9aedc85745d 100644 --- a/chromium/ui/display/display.cc +++ b/chromium/ui/display/display.cc @@ -268,7 +268,7 @@ gfx::Size Display::GetSizeInPixel() const { std::string Display::ToString() const { return base::StringPrintf( - "Display[%lld] bounds=%s, workarea=%s, scale=%g, %s", + "Display[%lld] bounds=[%s], workarea=[%s], scale=%g, %s.", static_cast<long long int>(id_), bounds_.ToString().c_str(), work_area_.ToString().c_str(), device_scale_factor_, IsInternal() ? "internal" : "external"); diff --git a/chromium/ui/display/display.h b/chromium/ui/display/display.h index 49f76438ef3..f900c7e016a 100644 --- a/chromium/ui/display/display.h +++ b/chromium/ui/display/display.h @@ -220,9 +220,7 @@ class DISPLAY_EXPORT Display final { // True if this is a monochrome display (e.g, for accessiblity). Used by media // query APIs. bool is_monochrome() const { return is_monochrome_; } - void set_is_monochrome(bool is_monochrome) { - is_monochrome_ = is_monochrome; - } + void set_is_monochrome(bool is_monochrome) { is_monochrome_ = is_monochrome; } bool operator==(const Display& rhs) const; bool operator!=(const Display& rhs) const { return !(*this == rhs); } diff --git a/chromium/ui/display/display_layout.cc b/chromium/ui/display/display_layout.cc index 814e25255f2..e681213346c 100644 --- a/chromium/ui/display/display_layout.cc +++ b/chromium/ui/display/display_layout.cc @@ -16,6 +16,8 @@ #include "base/values.h" #include "ui/display/display.h" #include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/rect.h" namespace display { namespace { @@ -329,6 +331,23 @@ void DeIntersectDisplays(int64_t primary_id, UpdatePlacementList(display_list, placement_list); } +// Checks if the given point is over the radius vector described by its end +// point |vector|. The point is over a vector if it's on its positive (left) +// side. The method sees a point on the same line as the vector as being over +// the vector. +bool IsPointOverRadiusVector(const gfx::Point& point, + const gfx::Point& vector) { + // |point| is left of |vector| if its radius vector's scalar product with a + // vector orthogonal (and facing the positive side) to |vector| is positive. + // + // An orthogonal vector of (a, b) is (b, -a), as the scalar product of these + // two is 0. + // So, (x, y) is over (a, b) if x * b + y * (-a) >= 0, which is equivalent to + // x * b >= y * a. + return static_cast<int64_t>(point.x()) * static_cast<int64_t>(vector.y()) >= + static_cast<int64_t>(point.y()) * static_cast<int64_t>(vector.x()); +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -634,6 +653,97 @@ DisplayPlacement DisplayLayout::FindPlacementById(int64_t display_id) const { : DisplayPlacement(*iter); } +// Creates a display::DisplayPlacement value for |rectangle| relative to the +// |reference| rectangle. +// The layout consists of two values: +// - position: Whether the rectangle is positioned left, right, over or under +// the reference. +// - offset: The rectangle's offset from the reference origin along the axis +// opposite the position direction (if the rectangle is left or right along +// y-axis, otherwise along x-axis). +// The rectangle's position is calculated by dividing the space in areas defined +// by the |reference|'s diagonals and finding the area |rectangle|'s center +// point belongs. If the |rectangle| in the calculated layout does not share a +// part of the bounds with the |reference|, the |rectangle| position in set to +// the more suitable neighboring position (e.g. if |rectangle| is completely +// over the |reference| top bound, it will be set to TOP) and the layout is +// recalculated with the new position. This is to handle the case where the +// rectangle shares an edge with the reference, but it's center is not in the +// same area as the reference's edge, e.g. +// +// +---------------------+ +// | | +// | REFERENCE | +// | | +// | | +// +---------------------+ +// +-------------------------------------------------+ +// | RECTANGLE x | +// +-------------------------------------------------+ +// +// The rectangle shares an egde with the reference's bottom edge, but it's +// center point is in the left area. + +// static +DisplayPlacement DisplayLayout::CreatePlacementForRectangles( + const gfx::Rect& reference, + const gfx::Rect& rectangle) { + // Translate coordinate system so origin is in the reference's top left point + // (so the reference's down-diagonal vector starts in the (0, 0)) and scale it + // up by two (to avoid division when calculating the rectangle's center + // point). + gfx::Point center(2 * (rectangle.x() - reference.x()) + rectangle.width(), + 2 * (rectangle.y() - reference.y()) + rectangle.height()); + gfx::Point down_diag(2 * reference.width(), 2 * reference.height()); + + bool is_top_right = IsPointOverRadiusVector(center, down_diag); + + // Translate the coordinate system again, so the bottom right point of the + // reference is origin (so the reference's up-diagonal starts at (0, 0)). + // Note that the coordinate system is scaled by 2. + center.Offset(0, -2 * reference.height()); + // Choose the vector orientation so the points on the diagonal are considered + // to be left. + gfx::Point up_diag(-2 * reference.width(), 2 * reference.height()); + + bool is_bottom_right = IsPointOverRadiusVector(center, up_diag); + + DisplayPlacement::Position position; + if (is_top_right) { + position = + is_bottom_right ? DisplayPlacement::RIGHT : DisplayPlacement::TOP; + } else { + position = + is_bottom_right ? DisplayPlacement::BOTTOM : DisplayPlacement::LEFT; + } + + // If the rectangle with the calculated position would not have common side + // with the reference, try to position it so it shares another edge with the + // reference. + if (is_top_right == is_bottom_right) { + if (rectangle.y() > reference.bottom()) { + // The rectangle is left or right, but completely under the reference. + position = DisplayPlacement::BOTTOM; + } else if (rectangle.bottom() < reference.y()) { + // The rectangle is left or right, but completely over the reference. + position = DisplayPlacement::TOP; + } + } else { + if (rectangle.x() > reference.right()) { + // The rectangle is over or under, but completely right of the reference. + position = DisplayPlacement::RIGHT; + } else if (rectangle.right() < reference.x()) { + // The rectangle is over or under, but completely left of the reference. + position = DisplayPlacement::LEFT; + } + } + int offset = (position == DisplayPlacement::LEFT || + position == DisplayPlacement::RIGHT) + ? rectangle.y() + : rectangle.x(); + return DisplayPlacement(position, offset); +} + // static bool DisplayLayout::ApplyDisplayPlacement(const DisplayPlacement& placement, Displays* display_list, diff --git a/chromium/ui/display/display_layout.h b/chromium/ui/display/display_layout.h index e269d589300..3f173780662 100644 --- a/chromium/ui/display/display_layout.h +++ b/chromium/ui/display/display_layout.h @@ -15,6 +15,10 @@ #include "base/strings/string_piece.h" #include "ui/display/display_export.h" +namespace gfx { +class Rect; +} + namespace display { class Display; @@ -123,6 +127,12 @@ class DISPLAY_EXPORT DisplayLayout final { // otherwise returns a DisplayPlacement with an invalid display id. DisplayPlacement FindPlacementById(int64_t display_id) const; + // Creates a display::DisplayPlacement value for |rectangle| relative to the + // |reference| rectangle. + static DisplayPlacement CreatePlacementForRectangles( + const gfx::Rect& reference, + const gfx::Rect& rectangle); + private: // Apply the display placement to |display_list|. // Returns true if the display bounds were updated. diff --git a/chromium/ui/display/display_switches.cc b/chromium/ui/display/display_switches.cc index d06ee064e1e..880fba64afe 100644 --- a/chromium/ui/display/display_switches.cc +++ b/chromium/ui/display/display_switches.cc @@ -65,10 +65,27 @@ const base::Feature kHighDynamicRange{"HighDynamicRange", base::FEATURE_ENABLED_BY_DEFAULT}; #if defined(OS_CHROMEOS) -// Enables using the monitor's provided color space information when rendering. +// Enables using the monitor's provided color space information when +// rendering. // TODO(mcasas): remove this flag http://crbug.com/771345. const base::Feature kUseMonitorColorSpace{"UseMonitorColorSpace", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; +#endif // OS_CHROMEOS + +// Enables the slider in display settings to modify the display zoom/size. +// TODO(malaykeshav): Remove this in M68 when the feature has been in stable for +// atleast one milestone. +constexpr base::Feature kEnableDisplayZoomSetting{ + "EnableDisplayZoomSetting", +#if defined(OS_CHROMEOS) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT #endif +}; + +bool IsDisplayZoomSettingEnabled() { + return base::FeatureList::IsEnabled(kEnableDisplayZoomSetting); +} } // namespace features diff --git a/chromium/ui/display/display_switches.h b/chromium/ui/display/display_switches.h index da146eb5359..f2377204810 100644 --- a/chromium/ui/display/display_switches.h +++ b/chromium/ui/display/display_switches.h @@ -38,6 +38,11 @@ DISPLAY_EXPORT extern const base::Feature kHighDynamicRange; DISPLAY_EXPORT extern const base::Feature kUseMonitorColorSpace; #endif +DISPLAY_EXPORT extern const base::Feature kEnableDisplayZoomSetting; + +// Returns true if experimental display zoom setting is enabled. +DISPLAY_EXPORT bool IsDisplayZoomSettingEnabled(); + } // namespace features #endif // UI_DISPLAY_DISPLAY_SWITCHES_H_ diff --git a/chromium/ui/display/mac/screen_mac.mm b/chromium/ui/display/mac/screen_mac.mm index 4850c795480..be4f343bcd4 100644 --- a/chromium/ui/display/mac/screen_mac.mm +++ b/chromium/ui/display/mac/screen_mac.mm @@ -206,7 +206,7 @@ class ScreenMac : public Screen { Display GetDisplayNearestView(gfx::NativeView view) const override { NSWindow* window = [view window]; if (!window) - window = [NSApp keyWindow]; + return GetPrimaryDisplay(); return GetDisplayNearestWindow(window); } diff --git a/chromium/ui/display/manager/BUILD.gn b/chromium/ui/display/manager/BUILD.gn index f54a2bbf39d..0b7946e9471 100644 --- a/chromium/ui/display/manager/BUILD.gn +++ b/chromium/ui/display/manager/BUILD.gn @@ -36,7 +36,6 @@ jumbo_component("manager") { "display_manager_export.h", "display_manager_utilities.cc", "display_manager_utilities.h", - "display_pref_util.h", "fake_display_delegate.cc", "fake_display_delegate.h", "fake_display_snapshot.cc", @@ -59,6 +58,7 @@ jumbo_component("manager") { "//ui/base", "//ui/display/mojo:interfaces", "//ui/display/util", + "//ui/events:platform_event", "//ui/events/devices", "//ui/strings", ] diff --git a/chromium/ui/display/manager/chromeos/DEPS b/chromium/ui/display/manager/chromeos/DEPS index 11d4683fe1f..c11eb4b186c 100644 --- a/chromium/ui/display/manager/chromeos/DEPS +++ b/chromium/ui/display/manager/chromeos/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+chromeos", + "+ui/events/platform_event.h", # DeviceDataManager is not created in all environments (such as ash when # running in mus/mash). "-ui/events/devices/device_data_manager.h", diff --git a/chromium/ui/display/manager/chromeos/display_change_observer.cc b/chromium/ui/display/manager/chromeos/display_change_observer.cc index 40a9c7b6835..bf9608d8fc1 100644 --- a/chromium/ui/display/manager/chromeos/display_change_observer.cc +++ b/chromium/ui/display/manager/chromeos/display_change_observer.cc @@ -24,6 +24,7 @@ #include "ui/display/types/display_mode.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/util/display_util.h" +#include "ui/display/util/edid_parser.h" #include "ui/events/devices/input_device_manager.h" #include "ui/events/devices/touchscreen_device.h" #include "ui/strings/grit/ui_strings.h" @@ -64,6 +65,12 @@ DisplayChangeObserver::GetInternalManagedDisplayModeList( ui_native_mode->refresh_rate(), ui_native_mode->is_interlaced(), true, 1.0, display_info.device_scale_factor()); + // When display zoom option is available, we cannot change the mode for + // internal displays. + if (features::IsDisplayZoomSettingEnabled()) { + native_mode.set_is_default(true); + return ManagedDisplayInfo::ManagedDisplayModeList{native_mode}; + } return CreateInternalManagedDisplayModeList(native_mode); } @@ -86,13 +93,21 @@ DisplayChangeObserver::GetExternalManagedDisplayModeList( native_mode = display_mode; // Add the display mode if it isn't already present and override interlaced - // display modes with non-interlaced ones. + // display modes with non-interlaced ones. We prioritize having non + // interlaced mode over refresh rate. A mode having lower refresh rate + // but is not interlaced will be picked over a mode having high refresh + // rate but is interlaced. auto display_mode_it = display_mode_map.find(size); - if (display_mode_it == display_mode_map.end()) + if (display_mode_it == display_mode_map.end()) { display_mode_map.insert(std::make_pair(size, display_mode)); - else if (display_mode_it->second.is_interlaced() && - !display_mode.is_interlaced()) + } else if (display_mode_it->second.is_interlaced() && + !display_mode.is_interlaced()) { + display_mode_it->second = std::move(display_mode); + } else if (!display_mode.is_interlaced() && + display_mode_it->second.refresh_rate() < + display_mode.refresh_rate()) { display_mode_it->second = std::move(display_mode); + } } ManagedDisplayInfo::ManagedDisplayModeList display_mode_list; @@ -111,6 +126,11 @@ DisplayChangeObserver::GetExternalManagedDisplayModeList( display_mode_list.push_back(native_mode); } + // If we are using display zoom mode, we no longer have to add additional + // display modes for ultra high resolution displays. + if (features::IsDisplayZoomSettingEnabled()) + return display_mode_list; + if (native_mode.size().width() >= kMinimumWidthFor4K) { for (size_t i = 0; i < arraysize(kAdditionalDeviceScaleFactorsFor4k); ++i) { ManagedDisplayMode mode(native_mode.size(), native_mode.refresh_rate(), @@ -247,21 +267,21 @@ void DisplayChangeObserver::UpdateInternalDisplay( } ManagedDisplayInfo DisplayChangeObserver::CreateManagedDisplayInfo( - const DisplaySnapshot* state, + const DisplaySnapshot* snapshot, const DisplayMode* mode_info) { float device_scale_factor = 1.0f; // Sets dpi only if the screen size is not blacklisted. - float dpi = IsDisplaySizeBlackListed(state->physical_size()) + float dpi = IsDisplaySizeBlackListed(snapshot->physical_size()) ? 0 : kInchInMm * mode_info->size().width() / - state->physical_size().width(); + snapshot->physical_size().width(); - if (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) { + if (snapshot->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) { if (dpi) device_scale_factor = FindDeviceScaleFactor(dpi); } else { ManagedDisplayMode mode; - if (display_manager_->GetSelectedModeForDisplayId(state->display_id(), + if (display_manager_->GetSelectedModeForDisplayId(snapshot->display_id(), &mode)) { device_scale_factor = mode.device_scale_factor(); } else { @@ -270,8 +290,8 @@ ManagedDisplayInfo DisplayChangeObserver::CreateManagedDisplayInfo( // from the value of |k2xThreshouldSizeSquaredFor4KInMm| const int k2xThreshouldSizeSquaredFor4KInMm = (40 * 40 * kInchInMm * kInchInMm) - 100; - gfx::Vector2d size_in_vec(state->physical_size().width(), - state->physical_size().height()); + gfx::Vector2d size_in_vec(snapshot->physical_size().width(), + snapshot->physical_size().height()); if (size_in_vec.LengthSquared() > k2xThreshouldSizeSquaredFor4KInMm && mode_info->size().width() >= kMinimumWidthFor4K) { // Make sure that additional device scale factors table has 2x. @@ -281,35 +301,47 @@ ManagedDisplayInfo DisplayChangeObserver::CreateManagedDisplayInfo( } } - std::string name = (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) + std::string name = (snapshot->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) ? l10n_util::GetStringUTF8(IDS_DISPLAY_NAME_INTERNAL) - : state->display_name(); + : snapshot->display_name(); if (name.empty()) name = l10n_util::GetStringUTF8(IDS_DISPLAY_NAME_UNKNOWN); - const bool has_overscan = state->has_overscan(); - const int64_t id = state->display_id(); + const bool has_overscan = snapshot->has_overscan(); + const int64_t id = snapshot->display_id(); ManagedDisplayInfo new_info = ManagedDisplayInfo(id, name, has_overscan); - new_info.set_sys_path(state->sys_path()); + + if (snapshot->product_code() != DisplaySnapshot::kInvalidProductCode) { + uint16_t manufacturer_id = 0; + uint16_t product_id = 0; + EdidParser::SplitProductCodeInManufacturerIdAndProductId( + snapshot->product_code(), &manufacturer_id, &product_id); + new_info.set_manufacturer_id( + EdidParser::ManufacturerIdToString(manufacturer_id)); + new_info.set_product_id(EdidParser::ProductIdToString(product_id)); + } + new_info.set_year_of_manufacture(snapshot->year_of_manufacture()); + + new_info.set_sys_path(snapshot->sys_path()); new_info.set_device_scale_factor(device_scale_factor); - const gfx::Rect display_bounds(state->origin(), mode_info->size()); + const gfx::Rect display_bounds(snapshot->origin(), mode_info->size()); new_info.SetBounds(display_bounds); new_info.set_native(true); new_info.set_is_aspect_preserving_scaling( - state->is_aspect_preserving_scaling()); + snapshot->is_aspect_preserving_scaling()); if (dpi) new_info.set_device_dpi(dpi); - new_info.set_color_space(state->color_space()); + new_info.set_color_space(snapshot->color_space()); ManagedDisplayInfo::ManagedDisplayModeList display_modes = - (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) - ? GetInternalManagedDisplayModeList(new_info, *state) - : GetExternalManagedDisplayModeList(*state); + (snapshot->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) + ? GetInternalManagedDisplayModeList(new_info, *snapshot) + : GetExternalManagedDisplayModeList(*snapshot); new_info.SetManagedDisplayModes(display_modes); - new_info.set_maximum_cursor_size(state->maximum_cursor_size()); + new_info.set_maximum_cursor_size(snapshot->maximum_cursor_size()); return new_info; } diff --git a/chromium/ui/display/manager/chromeos/display_change_observer.h b/chromium/ui/display/manager/chromeos/display_change_observer.h index 8ad853d4e40..66cae8da8da 100644 --- a/chromium/ui/display/manager/chromeos/display_change_observer.h +++ b/chromium/ui/display/manager/chromeos/display_change_observer.h @@ -64,7 +64,7 @@ class DISPLAY_MANAGER_EXPORT DisplayChangeObserver void UpdateInternalDisplay( const DisplayConfigurator::DisplayStateList& display_states); - ManagedDisplayInfo CreateManagedDisplayInfo(const DisplaySnapshot* state, + ManagedDisplayInfo CreateManagedDisplayInfo(const DisplaySnapshot* snapshot, const DisplayMode* mode_info); // Both |display_configurator_| and |display_manager_| are not owned and must diff --git a/chromium/ui/display/manager/chromeos/display_change_observer_unittest.cc b/chromium/ui/display/manager/chromeos/display_change_observer_unittest.cc index e714181ebd2..7d2922dc9ff 100644 --- a/chromium/ui/display/manager/chromeos/display_change_observer_unittest.cc +++ b/chromium/ui/display/manager/chromeos/display_change_observer_unittest.cc @@ -6,8 +6,9 @@ #include <string> -#include "base/memory/ptr_util.h" +#include "base/test/scoped_feature_list.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/display_switches.h" #include "ui/display/manager/chromeos/display_configurator.h" #include "ui/display/manager/fake_display_snapshot.h" #include "ui/display/manager/managed_display_info.h" @@ -38,7 +39,23 @@ std::unique_ptr<DisplayMode> MakeDisplayMode(int width, } // namespace -TEST(DisplayChangeObserverTest, GetExternalManagedDisplayModeList) { +class DisplayChangeObserverTest : public testing::Test { + public: + DisplayChangeObserverTest() = default; + + void SetUp() override { + scoped_feature_list_.InitAndDisableFeature( + features::kEnableDisplayZoomSetting); + testing::Test::SetUp(); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; + + DISALLOW_COPY_AND_ASSIGN(DisplayChangeObserverTest); +}; + +TEST_F(DisplayChangeObserverTest, GetExternalManagedDisplayModeList) { std::unique_ptr<DisplaySnapshot> display_snapshot = FakeDisplaySnapshot::Builder() .SetId(123) @@ -90,7 +107,7 @@ TEST(DisplayChangeObserverTest, GetExternalManagedDisplayModeList) { EXPECT_EQ(display_modes[5].refresh_rate(), 60); } -TEST(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) { +TEST_F(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) { FakeDisplaySnapshot display_snapshot( 123, gfx::Point(), gfx::Size(), DISPLAY_CONNECTION_TYPE_UNKNOWN, false, false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size()); @@ -101,7 +118,7 @@ TEST(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) { EXPECT_EQ(0u, display_modes.size()); } -TEST(DisplayChangeObserverTest, GetInternalManagedDisplayModeList) { +TEST_F(DisplayChangeObserverTest, GetInternalManagedDisplayModeList) { std::unique_ptr<DisplaySnapshot> display_snapshot = FakeDisplaySnapshot::Builder() .SetId(123) @@ -145,7 +162,7 @@ TEST(DisplayChangeObserverTest, GetInternalManagedDisplayModeList) { EXPECT_EQ(display_modes[4].refresh_rate(), 60); } -TEST(DisplayChangeObserverTest, GetInternalHiDPIManagedDisplayModeList) { +TEST_F(DisplayChangeObserverTest, GetInternalHiDPIManagedDisplayModeList) { // Data picked from peppy. std::unique_ptr<DisplaySnapshot> display_snapshot = FakeDisplaySnapshot::Builder() @@ -204,7 +221,7 @@ TEST(DisplayChangeObserverTest, GetInternalHiDPIManagedDisplayModeList) { EXPECT_EQ(display_modes[7].refresh_rate(), 60); } -TEST(DisplayChangeObserverTest, GetInternalManagedDisplayModeList1_25) { +TEST_F(DisplayChangeObserverTest, GetInternalManagedDisplayModeList1_25) { // Data picked from peppy. std::unique_ptr<DisplaySnapshot> display_snapshot = FakeDisplaySnapshot::Builder() @@ -246,7 +263,7 @@ TEST(DisplayChangeObserverTest, GetInternalManagedDisplayModeList1_25) { EXPECT_EQ(display_modes[4].refresh_rate(), 60); } -TEST(DisplayChangeObserverTest, GetExternalManagedDisplayModeList4K) { +TEST_F(DisplayChangeObserverTest, GetExternalManagedDisplayModeList4K) { std::unique_ptr<DisplaySnapshot> display_snapshot = FakeDisplaySnapshot::Builder() .SetId(123) @@ -320,7 +337,7 @@ TEST(DisplayChangeObserverTest, GetExternalManagedDisplayModeList4K) { EXPECT_EQ(display_modes[8].refresh_rate(), 30); } -TEST(DisplayChangeObserverTest, FindDeviceScaleFactor) { +TEST_F(DisplayChangeObserverTest, FindDeviceScaleFactor) { EXPECT_EQ(1.0f, ComputeDeviceScaleFactor(19.5f, gfx::Rect(1600, 900))); // 21.5" 1920x1080 @@ -353,7 +370,8 @@ TEST(DisplayChangeObserverTest, FindDeviceScaleFactor) { EXPECT_EQ(2.0f, DisplayChangeObserver::FindDeviceScaleFactor(10000.0f)); } -TEST(DisplayChangeObserverTest, FindExternalDisplayNativeModeWhenOverwritten) { +TEST_F(DisplayChangeObserverTest, + FindExternalDisplayNativeModeWhenOverwritten) { std::unique_ptr<DisplaySnapshot> display_snapshot = FakeDisplaySnapshot::Builder() .SetId(123) diff --git a/chromium/ui/display/manager/chromeos/display_configurator.cc b/chromium/ui/display/manager/chromeos/display_configurator.cc index b077a4b25db..e939dbac1f5 100644 --- a/chromium/ui/display/manager/chromeos/display_configurator.cc +++ b/chromium/ui/display/manager/chromeos/display_configurator.cc @@ -11,7 +11,6 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "chromeos/system/devicemode.h" #include "ui/display/display.h" @@ -504,7 +503,6 @@ DisplayConfigurator::DisplayConfigurator() current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID), current_power_state_(chromeos::DISPLAY_POWER_ALL_ON), requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID), - requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON), pending_power_state_(chromeos::DISPLAY_POWER_ALL_ON), has_pending_power_state_(false), pending_power_flags_(kSetDisplayPowerNoFlags), @@ -547,9 +545,25 @@ void DisplayConfigurator::SetDelegateForTesting( void DisplayConfigurator::SetInitialDisplayPower( chromeos::DisplayPowerState power_state) { - DCHECK_EQ(current_display_state_, MULTIPLE_DISPLAY_STATE_INVALID); - requested_power_state_ = current_power_state_ = power_state; - NotifyPowerStateObservers(); + if (requested_power_state_) { + // A new power state has alreday been requested so ignore the initial state. + return; + } + + // Set the initial requested power state. + requested_power_state_ = power_state; + + if (current_display_state_ == MULTIPLE_DISPLAY_STATE_INVALID) { + // DisplayConfigurator::OnConfigured has not been called yet so just set + // the current state and notify observers. + current_power_state_ = power_state; + NotifyPowerStateObservers(); + return; + } + + // DisplayConfigurator::OnConfigured has been called so update the current + // and pending states. + UpdatePowerState(power_state); } void DisplayConfigurator::Init( @@ -591,9 +605,11 @@ void DisplayConfigurator::OnDisplayControlTaken(DisplayControlCallback callback, if (success) { // Force a configuration since the display configuration may have changed. force_configure_ = true; - // Restore the last power state used before releasing control. - SetDisplayPower(requested_power_state_, kSetDisplayPowerNoFlags, - base::DoNothing()); + if (requested_power_state_) { + // Restore the requested power state before releasing control. + SetDisplayPower(*requested_power_state_, kSetDisplayPowerNoFlags, + base::DoNothing()); + } } std::move(callback).Run(success); @@ -668,8 +684,8 @@ void DisplayConfigurator::ForceInitialConfigure() { configuration_task_.reset(new UpdateDisplayConfigurationTask( native_display_delegate_.get(), layout_manager_.get(), - requested_display_state_, requested_power_state_, - kSetDisplayPowerForceProbe, true, + requested_display_state_, GetRequestedPowerState(), + kSetDisplayPowerForceProbe, /*force_configure=*/true, base::Bind(&DisplayConfigurator::OnConfigured, weak_ptr_factory_.GetWeakPtr()))); configuration_task_->Run(); @@ -849,15 +865,29 @@ bool DisplayConfigurator::SetColorCorrection( const std::vector<GammaRampRGBEntry>& degamma_lut, const std::vector<GammaRampRGBEntry>& gamma_lut, const std::vector<float>& correction_matrix) { - for (const DisplaySnapshot* display : cached_displays_) { - if (display->display_id() == display_id) - return native_display_delegate_->SetColorCorrection( - *display, degamma_lut, gamma_lut, correction_matrix); + for (DisplaySnapshot* display : cached_displays_) { + if (display->display_id() != display_id) + continue; + + const bool success = native_display_delegate_->SetColorCorrection( + *display, degamma_lut, gamma_lut, correction_matrix); + // Nullify the |display|s ColorSpace to avoid correcting colors twice, if + // we have successfully configured something. + if (success && (!degamma_lut.empty() || !gamma_lut.empty() || + !correction_matrix.empty())) { + display->reset_color_space(); + } + return success; } return false; } +chromeos::DisplayPowerState DisplayConfigurator::GetRequestedPowerState() + const { + return requested_power_state_.value_or(chromeos::DISPLAY_POWER_ALL_ON); +} + void DisplayConfigurator::PrepareForExit() { configure_display_ = false; } @@ -909,7 +939,7 @@ void DisplayConfigurator::SetDisplayPower( << (configure_timer_.IsRunning() ? "Running" : "Stopped"); requested_power_state_ = power_state; - SetDisplayPowerInternal(requested_power_state_, flags, callback); + SetDisplayPowerInternal(*requested_power_state_, flags, callback); } void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) { @@ -1007,8 +1037,10 @@ void DisplayConfigurator::ResumeDisplays() { // If requested_power_state_ is ALL_OFF due to idle suspend, powerd will turn // the display power on when it enables the backlight. - SetDisplayPower(requested_power_state_, kSetDisplayPowerNoFlags, - base::DoNothing()); + if (requested_power_state_) { + SetDisplayPower(*requested_power_state_, kSetDisplayPowerNoFlags, + base::DoNothing()); + } } void DisplayConfigurator::ConfigureDisplays() { @@ -1063,19 +1095,8 @@ void DisplayConfigurator::OnConfigured( cached_displays_ = displays; if (success) { - chromeos::DisplayPowerState old_power_state = current_power_state_; current_display_state_ = new_display_state; - current_power_state_ = new_power_state; - - // If the pending power state hasn't changed then make sure that value - // gets updated as well since the last requested value may have been - // dependent on certain conditions (ie: if only the internal monitor was - // present). - if (!has_pending_power_state_) - pending_power_state_ = new_power_state; - - if (old_power_state != current_power_state_) - NotifyPowerStateObservers(); + UpdatePowerState(new_power_state); } configuration_task_.reset(); @@ -1095,6 +1116,19 @@ void DisplayConfigurator::OnConfigured( } } +void DisplayConfigurator::UpdatePowerState( + chromeos::DisplayPowerState new_power_state) { + chromeos::DisplayPowerState old_power_state = current_power_state_; + current_power_state_ = new_power_state; + // If the pending power state hasn't changed then make sure that value gets + // updated as well since the last requested value may have been dependent on + // certain conditions (ie: if only the internal monitor was present). + if (!has_pending_power_state_) + pending_power_state_ = new_power_state; + if (old_power_state != current_power_state_) + NotifyPowerStateObservers(); +} + bool DisplayConfigurator::ShouldRunConfigurationTask() const { if (force_configure_) return true; diff --git a/chromium/ui/display/manager/chromeos/display_configurator.h b/chromium/ui/display/manager/chromeos/display_configurator.h index fe91b2ca336..3087d989592 100644 --- a/chromium/ui/display/manager/chromeos/display_configurator.h +++ b/chromium/ui/display/manager/chromeos/display_configurator.h @@ -13,10 +13,10 @@ #include <vector> #include "base/containers/queue.h" -#include "base/event_types.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "base/optional.h" #include "base/timer/timer.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "ui/display/manager/chromeos/query_content_protection_task.h" @@ -24,6 +24,7 @@ #include "ui/display/types/display_constants.h" #include "ui/display/types/native_display_observer.h" #include "ui/display/util/display_util.h" +#include "ui/events/platform_event.h" #include "ui/gfx/geometry/size.h" namespace gfx { @@ -179,9 +180,6 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator ~DisplayConfigurator() override; MultipleDisplayState display_state() const { return current_display_state_; } - chromeos::DisplayPowerState requested_power_state() const { - return requested_power_state_; - } const std::vector<DisplaySnapshot*>& cached_displays() const { return cached_displays_; } @@ -211,7 +209,11 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator void SetDelegateForTesting( std::unique_ptr<NativeDisplayDelegate> display_delegate); - // Sets the initial value of |power_state_|. Must be called before Start(). + // Called asynchronously with the initial |power_state| loaded from prefs. + // This may be called after ForceInitialConfigure triggers a call to + // OnConfigured(), in which case UpdatePowerState() will be called with the + // correct initial value. Does nothing if |requested_power_state_| is set, + // e.g. via SetDisplayPower(). void SetInitialDisplayPower(chromeos::DisplayPowerState power_state); // Initialization, must be called right after constructor. @@ -289,6 +291,9 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator const std::vector<GammaRampRGBEntry>& gamma_lut, const std::vector<float>& correction_matrix); + // Returns the requested power state if set or the default power state. + chromeos::DisplayPowerState GetRequestedPowerState() const; + void set_is_multi_mirroring_enabled_for_test(bool enabled) { is_multi_mirroring_enabled_ = enabled; } @@ -334,6 +339,9 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state); + // Updates the current and pending power state and notifies observers. + void UpdatePowerState(chromeos::DisplayPowerState new_power_state); + // Helps in identifying if a configuration task needs to be scheduled. // Return true if any of the |requested_*| parameters have been updated. False // otherwise. @@ -395,7 +403,7 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator MultipleDisplayState requested_display_state_; // Stores the requested power state. - chromeos::DisplayPowerState requested_power_state_; + base::Optional<chromeos::DisplayPowerState> requested_power_state_; // The power state used by RunPendingConfiguration(). May be // |requested_power_state_| or DISPLAY_POWER_ALL_OFF for suspend. diff --git a/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc b/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc index 987977c2295..623add64ef4 100644 --- a/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc +++ b/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc @@ -9,7 +9,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "chromeos/chromeos_switches.cc" @@ -686,6 +685,12 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { TEST_F(DisplayConfiguratorTest, SuspendAndResume) { InitWithSingleOutput(); + // Set the initial power state to on. + config_waiter_.Reset(); + configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, + DisplayConfigurator::kSetDisplayPowerNoFlags, + config_waiter_.on_configuration_callback()); + // No preparation is needed before suspending when the display is already // on. The configurator should still reprobe on resume in case a display // was connected while suspended. @@ -1406,9 +1411,17 @@ TEST_F(DisplayConfiguratorTest, DontRestoreStalePowerStateAfterResume) { TEST_F(DisplayConfiguratorTest, ExternalControl) { InitWithSingleOutput(); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE); + + // Set the initial power state and verify that it is restored when control is + // taken. + config_waiter_.Reset(); + configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, + DisplayConfigurator::kSetDisplayPowerNoFlags, + config_waiter_.on_configuration_callback()); + configurator_.RelinquishControl( - base::Bind(&DisplayConfiguratorTest::OnDisplayControlUpdated, - base::Unretained(this))); + base::BindOnce(&DisplayConfiguratorTest::OnDisplayControlUpdated, + base::Unretained(this))); EXPECT_EQ(CALLBACK_SUCCESS, PopDisplayControlResult()); EXPECT_EQ( JoinActions( @@ -1416,8 +1429,8 @@ TEST_F(DisplayConfiguratorTest, ExternalControl) { kRelinquishDisplayControl, nullptr), log_->GetActionsAndClear()); configurator_.TakeControl( - base::Bind(&DisplayConfiguratorTest::OnDisplayControlUpdated, - base::Unretained(this))); + base::BindOnce(&DisplayConfiguratorTest::OnDisplayControlUpdated, + base::Unretained(this))); EXPECT_EQ(CALLBACK_SUCCESS, PopDisplayControlResult()); EXPECT_EQ( JoinActions( @@ -1652,6 +1665,13 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { // Tests the suspend and resume behavior when in dual or multi display modes. TEST_F(DisplayConfiguratorTest, SuspendResumeWithMultipleDisplays) { InitWithSingleOutput(); + + // Set the initial power state and verify that it is restored on resume. + config_waiter_.Reset(); + configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, + DisplayConfigurator::kSetDisplayPowerNoFlags, + config_waiter_.on_configuration_callback()); + state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); observer_.Reset(); UpdateOutputs(2, true); diff --git a/chromium/ui/display/manager/chromeos/display_util.cc b/chromium/ui/display/manager/chromeos/display_util.cc index acf2084a8ee..b6e66143748 100644 --- a/chromium/ui/display/manager/chromeos/display_util.cc +++ b/chromium/ui/display/manager/chromeos/display_util.cc @@ -5,13 +5,32 @@ #include "ui/display/manager/chromeos/display_util.h" #include <stddef.h> +#include <algorithm> #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" +#include "ui/display/manager/managed_display_info.h" #include "ui/display/types/display_snapshot.h" namespace display { +namespace { + +// The list of deltas between two consecutive zoom level. Any display must have +// one of these values as the difference between two consecutive zoom level. +constexpr std::array<double, 7> kZoomFactorDeltas = {0.05f, 0.1f, 0.15f, 0.2f, + 0.25f, 0.5f, 1.f}; + +// The maximum logical resolution width allowed when zooming out for a display. +constexpr int kDefaultMaxZoomWidth = 4096; + +// The minimum logical resolution width allowed when zooming in for a display. +constexpr int kDefaultMinZoomWidth = 640; + +// The total number of display zoom factors to enumerate. +constexpr int kNumOfZoomFactors = 9; + +} // namespace std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) { switch (state) { @@ -71,4 +90,61 @@ bool IsPhysicalDisplayType(DisplayConnectionType type) { return !(type & DISPLAY_CONNECTION_TYPE_NETWORK); } +std::vector<double> GetDisplayZoomFactors(const ManagedDisplayMode& mode) { + const int effective_width = std::round( + static_cast<float>(mode.size().width()) / mode.device_scale_factor()); + + // We want to support displays greater than 4K. This is added to ensure the + // zoom does not break in such cases. + const int max_width = std::max(effective_width, kDefaultMaxZoomWidth); + const int min_width = std::min(effective_width, kDefaultMinZoomWidth); + + // The logical resolution will vary from half of the mode resolution to double + // the mode resolution. + int max_effective_width = + std::min(static_cast<int>(std::round(effective_width * 2.f)), max_width); + int min_effective_width = + std::max(static_cast<int>(std::round(effective_width / 2.f)), min_width); + + // If either the maximum width or minimum width was reached in the above step + // and clamping was performed, then update the total range of logical + // resolutions and ensure that everything lies within the maximum and minimum + // resolution range. + const int interval = std::round(static_cast<double>(effective_width) * 1.5f); + if (max_effective_width == max_width) + min_effective_width = std::max(max_effective_width - interval, min_width); + if (min_effective_width == min_width) + max_effective_width = std::min(min_effective_width + interval, max_width); + + double max_zoom = static_cast<double>(effective_width) / + static_cast<double>(min_effective_width); + double min_zoom = static_cast<double>(effective_width) / + static_cast<double>(max_effective_width); + + double delta = + (max_zoom - min_zoom) / static_cast<double>(kNumOfZoomFactors - 1); + + // Number of zoom values above 100% zoom. + const int zoom_in_count = std::round((max_zoom - 1.f) / delta); + + // Number of zoom values below 100% zoom. + const int zoom_out_count = kNumOfZoomFactors - zoom_in_count - 1; + + // Clamp the delta between consecutive zoom factors to a user friendly and UI + // friendly value. + std::size_t delta_index = 0; + while (delta_index < kZoomFactorDeltas.size() && + delta >= kZoomFactorDeltas[delta_index]) { + delta_index++; + } + delta = kZoomFactorDeltas[delta_index - 1]; + + min_zoom = 1.f - delta * zoom_out_count; + + std::vector<double> zoom_values; + for (int i = 0; i < kNumOfZoomFactors; i++) + zoom_values.push_back(min_zoom + i * delta); + return zoom_values; +} + } // namespace display diff --git a/chromium/ui/display/manager/chromeos/display_util.h b/chromium/ui/display/manager/chromeos/display_util.h index 3da69e16416..0f04c816392 100644 --- a/chromium/ui/display/manager/chromeos/display_util.h +++ b/chromium/ui/display/manager/chromeos/display_util.h @@ -15,6 +15,7 @@ namespace display { class DisplaySnapshot; +class ManagedDisplayMode; // Returns a string describing |state|. std::string DisplayPowerStateToString(chromeos::DisplayPowerState state); @@ -35,6 +36,10 @@ GetDisplayPower(const std::vector<DisplaySnapshot*>& displays, // All other types return true. bool IsPhysicalDisplayType(DisplayConnectionType type); +// Returns a list of display zooms supported by the given |mode|. +std::vector<double> DISPLAY_MANAGER_EXPORT +GetDisplayZoomFactors(const ManagedDisplayMode& mode); + } // namespace display #endif // UI_DISPLAY_MANAGER_CHROMEOS_DISPLAY_UTIL_H_ diff --git a/chromium/ui/display/manager/chromeos/display_utils_unittest.cc b/chromium/ui/display/manager/chromeos/display_utils_unittest.cc new file mode 100644 index 00000000000..0eeb386e003 --- /dev/null +++ b/chromium/ui/display/manager/chromeos/display_utils_unittest.cc @@ -0,0 +1,111 @@ +// 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/display/manager/chromeos/display_util.h" + +#include <vector> + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/manager/managed_display_info.h" +#include "ui/gfx/geometry/size.h" + +namespace display { +namespace test { + +namespace { +constexpr std::size_t kNumOfZoomFactors = 9; +constexpr int kDefaultMaxZoomWidth = 4096; +constexpr int kDefaultMinZoomWidth = 640; +} // namespace +using DisplayUtilTest = testing::Test; + +TEST_F(DisplayUtilTest, DisplayZooms) { + // A vector of pairs where each pair is the resolution width correspoinding + // to its list of available display zoom values. + const std::vector<std::pair<int, std::vector<double>>> expected_zoom_values{ + {480, {0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.f}}, + {640, {0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.f}}, + {720, {0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.f, 1.05f}}, + {800, {0.40f, 0.50f, 0.60f, 0.70f, 0.80f, 0.90f, 1.f, 1.10f, 1.20f}}, + {960, {0.60f, 0.70f, 0.80f, 0.90f, 1.f, 1.10f, 1.20f, 1.30f, 1.40f}}, + {1024, {0.60f, 0.70f, 0.80f, 0.90f, 1.f, 1.10f, 1.20f, 1.30f, 1.40f}}, + {1280, {0.55f, 0.70f, 0.85f, 1.f, 1.15f, 1.30f, 1.45f, 1.60f, 1.75f}}, + {1366, {0.55f, 0.70f, 0.85f, 1.f, 1.15f, 1.30f, 1.45f, 1.60f, 1.75f}}, + {1440, {0.55f, 0.70f, 0.85f, 1.f, 1.15f, 1.30f, 1.45f, 1.60f, 1.75f}}, + {1600, {0.55f, 0.70f, 0.85f, 1.f, 1.15f, 1.30f, 1.45f, 1.60f, 1.75f}}, + {1920, {0.55f, 0.70f, 0.85f, 1.f, 1.15f, 1.30f, 1.45f, 1.60f, 1.75f}}, + {2160, {0.60f, 0.80f, 1.f, 1.20f, 1.40f, 1.60f, 1.80f, 2.00f, 2.20f}}, + {2560, {0.75f, 1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f}}, + {2880, {0.75f, 1.f, 1.25f, 1.50f, 1.75f, 2.00f, 2.25f, 2.50f, 2.75f}}, + {3200, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}}, + {3840, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}}, + {4096, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}}, + {5120, {1.f, 1.50f, 2.00f, 2.50f, 3.00f, 3.50f, 4.00f, 4.50f, 5.00f}}, + {7680, {1.f, 2.00f, 3.00f, 4.00f, 5.00f, 6.00f, 7.00f, 8.00f, 9.00f}}, + {8192, {1.f, 2.00f, 3.00f, 4.00f, 5.00f, 6.00f, 7.00f, 8.00f, 9.00f}}, + }; + + for (const auto& pair : expected_zoom_values) { + const int size = pair.first; + ManagedDisplayMode mode(gfx::Size(size, size), 60, false, true, 1.f, 1.f); + const std::vector<double> zoom_values = GetDisplayZoomFactors(mode); + EXPECT_EQ(zoom_values.size(), kNumOfZoomFactors); + for (std::size_t j = 0; j < kNumOfZoomFactors; j++) { + EXPECT_NEAR(zoom_values[j], pair.second[j], 0.001); + + // Display pref stores the zoom value only upto 2 decimal places. This + // check ensures that the expected precision is only upto 2 decimal + // points. Before changing this line please ensure that you have updated + // chromeos/display/display_prefs.cc + int percentage_value = std::round(zoom_values[j] * 100.f); + float fractional_value = static_cast<float>(percentage_value) / 100.f; + EXPECT_NEAR(zoom_values[j], fractional_value, 0.0001f); + } + + const int effective_minimum_width_possible = size / zoom_values.back(); + const int effective_maximum_width_possible = size * zoom_values.front(); + + const int allowed_minimum_width = std::min(kDefaultMinZoomWidth, size); + const int allowed_maximum_width = std::max(kDefaultMaxZoomWidth, size); + + EXPECT_GE(effective_minimum_width_possible, allowed_minimum_width); + EXPECT_LE(effective_maximum_width_possible, allowed_maximum_width); + } +} + +TEST_F(DisplayUtilTest, DisplayZoomsWithInternalDsf) { + const std::vector<int> sizes = {1280, 1366, 1440, 1600, 1920, 2160, 2560, + 2880, 3200, 3840, 4096, 5120, 7680, 8192}; + + const std::vector<float> dsfs = {1.25f, 1.5f, 1.6f, 1.8f, 2.f, 2.25f}; + + for (float dsf : dsfs) { + for (int size : sizes) { + ManagedDisplayMode mode(gfx::Size(size, size), 60, false, true, 1.f, dsf); + const std::vector<double> zoom_values = GetDisplayZoomFactors(mode); + + const int effective_size = std::round(static_cast<float>(size) / dsf); + ManagedDisplayMode expected_mode( + gfx::Size(effective_size, effective_size), 60, false, true, 1.f, 1.f); + const std::vector<double> expected_zoom_values = + GetDisplayZoomFactors(expected_mode); + EXPECT_EQ(zoom_values.size(), kNumOfZoomFactors); + for (std::size_t i = 0; i < kNumOfZoomFactors; i++) + EXPECT_NEAR(zoom_values[i], expected_zoom_values[i], 0.001); + + const int effective_minimum_width_possible = size / zoom_values.back(); + const int effective_maximum_width_possible = size * zoom_values.front(); + + const int allowed_minimum_width = std::min(kDefaultMinZoomWidth, size); + const int allowed_maximum_width = std::max(kDefaultMaxZoomWidth, size); + + EXPECT_GE(effective_minimum_width_possible, allowed_minimum_width); + EXPECT_LE(effective_maximum_width_possible, allowed_maximum_width); + } + } +} + +} // namespace test +} // namespace display diff --git a/chromium/ui/display/manager/chromeos/touch_device_manager.cc b/chromium/ui/display/manager/chromeos/touch_device_manager.cc index 4ffd851a402..656fe1db54e 100644 --- a/chromium/ui/display/manager/chromeos/touch_device_manager.cc +++ b/chromium/ui/display/manager/chromeos/touch_device_manager.cc @@ -5,7 +5,9 @@ #include "ui/display/manager/chromeos/touch_device_manager.h" #include <algorithm> +#include <set> #include <string> +#include <tuple> #include "base/files/file_util.h" #include "base/hash.h" @@ -24,6 +26,7 @@ using ManagedDisplayInfoList = std::vector<ManagedDisplayInfo*>; using DeviceList = std::vector<ui::TouchscreenDevice>; constexpr char kFallbackTouchDeviceName[] = "fallback_touch_device_name"; +constexpr char kFallbackTouchDevicePhys[] = "fallback_touch_device_phys"; // Returns true if |path| is likely a USB device. bool IsDeviceConnectedViaUsb(const base::FilePath& path) { @@ -145,6 +148,23 @@ ManagedDisplayInfo* GetBestMatchForDevice( return display_info; } +// Returns a set of TouchDeviceIdentifiers (sans their port information) that +// are associated with more than 1 touch device from the list |devices|. +std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp> +GetCollisionSet(const DeviceList& devices) { + std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp> + collision_set; + std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp> ids; + for (const ui::TouchscreenDevice& device : devices) { + TouchDeviceIdentifier id = TouchDeviceIdentifier::FromDevice(device); + if (ids.find(id) != ids.end()) + collision_set.insert(id); + else + ids.insert(id); + } + return collision_set; +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -154,7 +174,8 @@ ManagedDisplayInfo* GetBestMatchForDevice( const TouchDeviceIdentifier& TouchDeviceIdentifier::GetFallbackTouchDeviceIdentifier() { static const TouchDeviceIdentifier kFallTouchDeviceIdentifier( - GenerateIdentifier(kFallbackTouchDeviceName, 0, 0)); + GenerateIdentifier(kFallbackTouchDeviceName, 0, 0), + base::PersistentHash(kFallbackTouchDevicePhys)); return kFallTouchDeviceIdentifier; } @@ -170,28 +191,36 @@ uint32_t TouchDeviceIdentifier::GenerateIdentifier(std::string name, // static TouchDeviceIdentifier TouchDeviceIdentifier::FromDevice( const ui::TouchscreenDevice& touch_device) { - return TouchDeviceIdentifier(GenerateIdentifier( - touch_device.name, touch_device.vendor_id, touch_device.product_id)); + return TouchDeviceIdentifier( + GenerateIdentifier(touch_device.name, touch_device.vendor_id, + touch_device.product_id), + base::PersistentHash(touch_device.phys)); } TouchDeviceIdentifier::TouchDeviceIdentifier(uint32_t identifier) - : id_(identifier) {} + : id_(identifier), + secondary_id_(base::PersistentHash(kFallbackTouchDevicePhys)) {} + +TouchDeviceIdentifier::TouchDeviceIdentifier(uint32_t identifier, + uint32_t secondary_id) + : id_(identifier), secondary_id_(secondary_id) {} TouchDeviceIdentifier::TouchDeviceIdentifier(const TouchDeviceIdentifier& other) - : id_(other.id_) {} + : id_(other.id_), secondary_id_(other.secondary_id_) {} TouchDeviceIdentifier& TouchDeviceIdentifier::operator=( TouchDeviceIdentifier other) { id_ = other.id_; + secondary_id_ = other.secondary_id_; return *this; } bool TouchDeviceIdentifier::operator<(const TouchDeviceIdentifier& rhs) const { - return id_ < rhs.id_; + return std::tie(id_, secondary_id_) < std::tie(rhs.id_, rhs.secondary_id_); } bool TouchDeviceIdentifier::operator==(const TouchDeviceIdentifier& rhs) const { - return id_ == rhs.id_; + return id_ == rhs.id_ && secondary_id_ == rhs.secondary_id_; } bool TouchDeviceIdentifier::operator!=(const TouchDeviceIdentifier& rhs) const { @@ -202,6 +231,10 @@ std::string TouchDeviceIdentifier::ToString() const { return base::UintToString(id_); } +std::string TouchDeviceIdentifier::SecondaryIdToString() const { + return base::UintToString(secondary_id_); +} + //////////////////////////////////////////////////////////////////////////////// // TouchCalibrationData @@ -255,6 +288,7 @@ TouchDeviceManager::~TouchDeviceManager() {} void TouchDeviceManager::AssociateTouchscreens( std::vector<ManagedDisplayInfo>* all_displays, const std::vector<ui::TouchscreenDevice>& all_devices) { + active_touch_associations_.clear(); // |displays| and |devices| contain pointers directly to the values stored // inside of |all_displays| and |all_devices|. When a display or input device // has been associated, it is removed from the |displays| or |devices| list. @@ -286,6 +320,7 @@ void TouchDeviceManager::AssociateTouchscreens( } AssociateInternalDevices(&displays, &devices); + AssociateDevicesWithCollision(&displays, &devices); AssociateFromHistoricalData(&displays, &devices); AssociateUdlDevices(&displays, &devices); AssociateSameSizeDevices(&displays, &devices); @@ -341,6 +376,54 @@ void TouchDeviceManager::AssociateInternalDevices( } } +void TouchDeviceManager::AssociateDevicesWithCollision( + ManagedDisplayInfoList* displays, + DeviceList* devices) { + if (!devices->size() || !displays->size()) + return; + + // Get a list of touch devices that have the same primary ids but connected + // via different interfaces. + std::set<TouchDeviceIdentifier, TouchDeviceIdentifier::WeakComp> + collision_set = GetCollisionSet(*devices); + if (collision_set.empty()) + return; + + VLOG(2) << "Trying to match " << devices->size() << " devices " + << "and " << displays->size() << " displays where there is/are " + << collision_set.size() << " collisions with the touch device ids"; + + for (auto device_it = devices->begin(); device_it != devices->end();) { + const auto identifier = TouchDeviceIdentifier::FromDevice(*device_it); + // If this device is not the one that has a collision or if this device is + // the one that has collision but we have no past port mapping information + // associated with it, then we skip. + if (!base::ContainsKey(collision_set, identifier) || + !base::ContainsKey(port_associations_, identifier)) { + device_it++; + continue; + } + + int64_t display_id = port_associations_.at(identifier); + + // Find the display associated with |display_id| from |displays|. + ManagedDisplayInfoList::iterator display_it = + std::find_if(displays->begin(), displays->end(), + [&display_id](ManagedDisplayInfo* info) { + return info->id() == display_id; + }); + + if (display_it != displays->end()) { + VLOG(2) << "=> Matched device " << (*device_it).name << " to display " + << (*display_it)->name(); + Associate(*display_it, *device_it); + device_it = devices->erase(device_it); + } else { + device_it++; + } + } +} + void TouchDeviceManager::AssociateFromHistoricalData( ManagedDisplayInfoList* displays, DeviceList* devices) { @@ -531,6 +614,11 @@ void TouchDeviceManager::AddTouchCalibrationData( info.calibration_data = data; touch_associations_.at(identifier).emplace(display_id, info); } + + // Store the port association information, i.e. the touch device identified by + // |identifier| when connected to port |identifier.secondary_id()| was + // associated with display identified by |display_id|. + port_associations_[identifier] = display_id; } void TouchDeviceManager::ClearTouchCalibrationData( @@ -612,13 +700,16 @@ TouchDeviceManager::GetAssociatedTouchDevicesForDisplay( } void TouchDeviceManager::RegisterTouchAssociations( - const TouchAssociationMap& touch_associations) { + const TouchAssociationMap& touch_associations, + const PortAssociationMap& port_associations) { touch_associations_ = touch_associations; + port_associations_ = port_associations; } std::ostream& operator<<(std::ostream& os, const TouchDeviceIdentifier& identifier) { - return os << identifier.ToString(); + return os << identifier.ToString() << " [" << identifier.SecondaryIdToString() + << "]"; } bool HasExternalTouchscreenDevice() { diff --git a/chromium/ui/display/manager/chromeos/touch_device_manager.h b/chromium/ui/display/manager/chromeos/touch_device_manager.h index 5c42e66cdb9..e64cd187a61 100644 --- a/chromium/ui/display/manager/chromeos/touch_device_manager.h +++ b/chromium/ui/display/manager/chromeos/touch_device_manager.h @@ -28,10 +28,29 @@ namespace test { class TouchDeviceManagerTestApi; } // namespace test -// A unique identifier to identify |ui::TouchscreenDevices|. These identifiers -// are persistent across system restarts. +// A unique identifier to identify |ui::TouchscreenDevices|. The primary id +// reflected by |id_| is persistent across system restarts and hotplugs. The +// secondary id represented by |secondary_id_|, reflects the physical port +// information. This is consistent and safe as long as the device is connected +// to the same port along the same path. class DISPLAY_MANAGER_EXPORT TouchDeviceIdentifier { public: + // A comparator that does not differentiate between duplicate instances of + // the same kind of touch devices, i.e. devices with the same primary id. + // Use this when you are working with different kinds of devices and do not + // care about multiple instances of the same kind of device. + // For example; if you want to store all the calibration information for + // touch devices and display, you do not care about what port the touch device + // is connected via. All touch devices of the same kind will have the same + // calibration data for a given display irrespective of the port they are + // connected to. + struct WeakComp { + bool operator()(const TouchDeviceIdentifier& lhs, + const TouchDeviceIdentifier& rhs) const { + return lhs.id() < rhs.id(); + } + }; + // Returns a touch device identifier used as a default or a fallback option. static const TouchDeviceIdentifier& GetFallbackTouchDeviceIdentifier(); @@ -39,6 +58,7 @@ class DISPLAY_MANAGER_EXPORT TouchDeviceIdentifier { const ui::TouchscreenDevice& touch_device); explicit TouchDeviceIdentifier(uint32_t identifier); + TouchDeviceIdentifier(uint32_t identifier, uint32_t secondary_id); TouchDeviceIdentifier(const TouchDeviceIdentifier& other); ~TouchDeviceIdentifier() = default; @@ -49,12 +69,20 @@ class DISPLAY_MANAGER_EXPORT TouchDeviceIdentifier { bool operator!=(const TouchDeviceIdentifier& other) const; std::string ToString() const; + std::string SecondaryIdToString() const; + + uint32_t id() const { return id_; } private: static uint32_t GenerateIdentifier(std::string name, uint16_t vendor_id, uint16_t product_id); uint32_t id_; + + // Used in case there are multiple devices with the same ID. The secondary id + // is generated based on EVIOCGPHYS which is stable across reboot and hotplug. + // This is not safe across different ports on the device. + uint32_t secondary_id_; }; // A struct that represents all the data required for touch calibration for the @@ -100,8 +128,11 @@ class DISPLAY_MANAGER_EXPORT TouchDeviceManager { }; using AssociationInfoMap = std::map<int64_t, TouchAssociationInfo>; - using TouchAssociationMap = - std::map<TouchDeviceIdentifier, AssociationInfoMap>; + using TouchAssociationMap = std::map<TouchDeviceIdentifier, + AssociationInfoMap, + TouchDeviceIdentifier::WeakComp>; + using ActiveTouchAssociationMap = std::map<TouchDeviceIdentifier, int64_t>; + using PortAssociationMap = ActiveTouchAssociationMap; TouchDeviceManager(); ~TouchDeviceManager(); @@ -158,20 +189,30 @@ class DISPLAY_MANAGER_EXPORT TouchDeviceManager { std::vector<TouchDeviceIdentifier> GetAssociatedTouchDevicesForDisplay( int64_t display_id) const; - // Registers the touch associations retrieved from the persistent store. This - // function is used to initialize the TouchDeviceManager on system start up. - void RegisterTouchAssociations(const TouchAssociationMap& touch_associations); + // Registers the touch associations and port associations retrieved from the + // persistent store. This function is used to initialize the + // TouchDeviceManager on system start up. + void RegisterTouchAssociations(const TouchAssociationMap& touch_associations, + const PortAssociationMap& port_associations); const TouchAssociationMap& touch_associations() const { return touch_associations_; } + const PortAssociationMap& port_associations() const { + return port_associations_; + } + private: friend class test::TouchDeviceManagerTestApi; void AssociateInternalDevices(std::vector<ManagedDisplayInfo*>* displays, std::vector<ui::TouchscreenDevice>* devices); + void AssociateDevicesWithCollision( + std::vector<ManagedDisplayInfo*>* displays, + std::vector<ui::TouchscreenDevice>* devices); + void AssociateFromHistoricalData(std::vector<ManagedDisplayInfo*>* displays, std::vector<ui::TouchscreenDevice>* devices); @@ -197,10 +238,17 @@ class DISPLAY_MANAGER_EXPORT TouchDeviceManager { // association information for this system. TouchAssociationMap touch_associations_; - // A mapping of touch devices identified by their TouchDeviceIdentifier and - // display ids they are currently associated with. This map only contains - // items (displays and touch devices) that are currently active. - std::map<TouchDeviceIdentifier, int64_t> active_touch_associations_; + // A mapping of Touch device and the port it is connected via, to the display. + // This is used when some touch devices cannot be distinguished from one + // another except based on the port they are connected via. We use the + // EVIOCGPHYS information of the touch device to get the port information. + PortAssociationMap port_associations_; + + // A mapping between touch devices(identified by their TouchDeviceIdentifier) + // and display ids of the display that they are currently associated with. + // This map only contains items (displays and touch devices) that are + // currently active. + ActiveTouchAssociationMap active_touch_associations_; DISALLOW_COPY_AND_ASSIGN(TouchDeviceManager); }; diff --git a/chromium/ui/display/manager/chromeos/touch_device_manager_unittest.cc b/chromium/ui/display/manager/chromeos/touch_device_manager_unittest.cc index 472f0bafb2a..fc3d847487b 100644 --- a/chromium/ui/display/manager/chromeos/touch_device_manager_unittest.cc +++ b/chromium/ui/display/manager/chromeos/touch_device_manager_unittest.cc @@ -8,7 +8,6 @@ #include <string> #include <vector> -#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" @@ -406,7 +405,8 @@ class TouchAssociationFromPrefTest : public TouchAssociationTest { touch_associations[TouchDeviceIdentifier::FromDevice(devices_[2])] [displays_[0].id()] = CreateTouchAssociationInfo(3); - touch_device_manager_->RegisterTouchAssociations(touch_associations); + touch_device_manager_->RegisterTouchAssociations( + touch_associations, TouchDeviceManager::PortAssociationMap()); } void TearDown() override { @@ -581,4 +581,264 @@ TEST_F(TouchAssociationFromPrefTest, InternalDisplayIsNotMatched) { EXPECT_TRUE(AreAssociated(displays_[3], devices_[1])); } +class TouchAssociationWithDuplicateDeviceTest : public TouchAssociationTest { + public: + TouchAssociationWithDuplicateDeviceTest() {} + ~TouchAssociationWithDuplicateDeviceTest() override {} + + void SetUp() override { + TouchAssociationTest::SetUp(); + TouchDeviceManager::TouchAssociationMap touch_associations; + TouchDeviceManager::PortAssociationMap port_associations; + + // Create different ports. + const std::vector<std::string> ports = {"port 0", "port 1", "port 2", + "port 3", "port 4"}; + + std::string device_name_1 = "device 1"; + std::string device_name_2 = "device 2"; + + // 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))); + devices_.back().name = device_name_1; + devices_.back().phys = ports[0]; + + int vendor_id = devices_.back().vendor_id; + int product_id = devices_.back().product_id; + + devices_.push_back(CreateTouchscreenDevice( + 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, 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, + // product id and name. + devices_.back().name = device_name_1; + devices_.back().phys = ports[1]; + devices_.back().vendor_id = vendor_id; + devices_.back().product_id = product_id; + + devices_.push_back(CreateTouchscreenDevice( + 3, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1920, 1080))); + devices_.back().name = device_name_1; + devices_.back().phys = ports[2]; + devices_.back().vendor_id = vendor_id; + devices_.back().product_id = product_id; + + devices_.push_back(CreateTouchscreenDevice( + 4, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, gfx::Size(800, 600))); + + devices_.push_back(CreateTouchscreenDevice( + 5, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(4096, 4096))); + devices_.back().name = device_name_2; + devices_.back().phys = ports[3]; + + vendor_id = devices_.back().vendor_id; + product_id = devices_.back().product_id; + + devices_.push_back(CreateTouchscreenDevice( + 6, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(4096, 4096))); + devices_.back().name = device_name_2; + devices_.back().phys = ports[4]; + devices_.back().vendor_id = vendor_id; + devices_.back().product_id = product_id; + + // Create priority list for Device Id = 1 + // - Display Index 0 + // - Display Index 2 + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[0])] = + TouchDeviceManager::AssociationInfoMap(); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[0])] + [displays_[0].id()] = CreateTouchAssociationInfo(1); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[0])] + [displays_[2].id()] = CreateTouchAssociationInfo(2); + + // Create priority list for Device Id = 2 + // - Display Index 3 + // - Display Index 1 + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[1])] = + TouchDeviceManager::AssociationInfoMap(); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[1])] + [displays_[3].id()] = CreateTouchAssociationInfo(1); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[1])] + [displays_[1].id()] = CreateTouchAssociationInfo(2); + + // Craete priority list for Device Id = 3 + // - Display Index 2 + // - Display Index 3 + // - Display Index 0 + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[2])] = + TouchDeviceManager::AssociationInfoMap(); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[2])] + [displays_[2].id()] = CreateTouchAssociationInfo(1); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[2])] + [displays_[3].id()] = CreateTouchAssociationInfo(2); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[2])] + [displays_[0].id()] = CreateTouchAssociationInfo(3); + + // Craete priority list for Device Id = 5 + // - Display Index 3 + // - Display Index 2 + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[4])] = + TouchDeviceManager::AssociationInfoMap(); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[4])] + [displays_[3].id()] = CreateTouchAssociationInfo(1); + touch_associations[TouchDeviceIdentifier::FromDevice(devices_[4])] + [displays_[2].id()] = CreateTouchAssociationInfo(2); + + // Map ports: + // - { Touch Device 1, ports[0] } -> Display Index 2 + // - { Touch Device 2, ports[1] } -> Display Index 3 + // - { Touch Device 3, ports[2] } -> Display Index 2 + // - { Touch Device 5, ports[4] } -> Display Index 0 + port_associations[TouchDeviceIdentifier::FromDevice(devices_[0])] = + displays_[2].id(); + port_associations[TouchDeviceIdentifier::FromDevice(devices_[1])] = + displays_[3].id(); + port_associations[TouchDeviceIdentifier::FromDevice(devices_[2])] = + displays_[2].id(); + port_associations[TouchDeviceIdentifier::FromDevice(devices_[4])] = + displays_[0].id(); + + touch_device_manager_->RegisterTouchAssociations(touch_associations, + port_associations); + } + + void TearDown() override { + TouchAssociationTest::TearDown(); + devices_.clear(); + } + + protected: + std::vector<ui::TouchscreenDevice> devices_; + + private: + DISALLOW_COPY_AND_ASSIGN(TouchAssociationWithDuplicateDeviceTest); +}; + +TEST_F(TouchAssociationWithDuplicateDeviceTest, CorrectMapping) { + test::ScopedSetInternalDisplayId set_internal(display_manager(), + displays_[1].id()); + + touch_device_manager()->AssociateTouchscreens(&displays_, devices_); + + EXPECT_EQ(GetTouchDeviceCount(displays_[0]), 1u); + EXPECT_TRUE(AreAssociated(displays_[0], devices_[4])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[1]), 1u); + EXPECT_TRUE(AreAssociated(displays_[1], devices_[3])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[2]), 2u); + EXPECT_TRUE(AreAssociated(displays_[2], devices_[0])); + EXPECT_TRUE(AreAssociated(displays_[2], devices_[2])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[3]), 2u); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[1])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[5])); +} + +TEST_F(TouchAssociationWithDuplicateDeviceTest, NoDuplicateIds) { + test::ScopedSetInternalDisplayId set_internal(display_manager(), + displays_[1].id()); + + std::vector<ui::TouchscreenDevice> devices; + devices.push_back(devices_[1]); + devices.push_back(devices_[3]); + devices.push_back(devices_[4]); + + touch_device_manager()->AssociateTouchscreens(&displays_, devices); + + EXPECT_EQ(GetTouchDeviceCount(displays_[0]), 0u); + + EXPECT_EQ(GetTouchDeviceCount(displays_[1]), 1u); + EXPECT_TRUE(AreAssociated(displays_[1], devices_[3])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[2]), 1u); + EXPECT_TRUE(AreAssociated(displays_[2], devices_[1])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[3]), 1u); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[4])); +} + +TEST_F(TouchAssociationWithDuplicateDeviceTest, CorrectMappingWithSomeMissing) { + test::ScopedSetInternalDisplayId set_internal(display_manager(), + displays_[1].id()); + DisplayInfoList displays; + displays.push_back(displays_[0]); + displays.push_back(displays_[1]); + displays.push_back(displays_[3]); + + touch_device_manager()->AssociateTouchscreens(&displays, devices_); + + EXPECT_EQ(GetTouchDeviceCount(displays_[0]), 1u); + EXPECT_TRUE(AreAssociated(displays_[0], devices_[4])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[1]), 1u); + EXPECT_TRUE(AreAssociated(displays_[1], devices_[3])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[3]), 4u); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[0])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[1])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[2])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[5])); +} + +TEST_F(TouchAssociationWithDuplicateDeviceTest, UpdatePortBeforeAssociation) { + test::ScopedSetInternalDisplayId set_internal(display_manager(), + displays_[1].id()); + + // Reassociate display at index 3 to touch device at index 2. This will + // bring the display to the top of the priority list and map the port the + // device is connected to, to display 3. + touch_device_manager()->AddTouchCalibrationData( + TouchDeviceIdentifier::FromDevice(devices_[2]), displays_[3].id(), + TouchCalibrationData()); + + touch_device_manager()->AssociateTouchscreens(&displays_, devices_); + + EXPECT_EQ(GetTouchDeviceCount(displays_[0]), 1u); + EXPECT_TRUE(AreAssociated(displays_[0], devices_[4])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[1]), 1u); + EXPECT_TRUE(AreAssociated(displays_[1], devices_[3])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[2]), 1u); + EXPECT_TRUE(AreAssociated(displays_[2], devices_[0])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[3]), 3u); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[1])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[2])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[5])); +} + +TEST_F(TouchAssociationWithDuplicateDeviceTest, ChangeAssociation) { + test::ScopedSetInternalDisplayId set_internal(display_manager(), + displays_[1].id()); + + touch_device_manager()->AssociateTouchscreens(&displays_, devices_); + + // Reassociate display at index 3 to touch device at index 2. This will + // bring the display to the top of the priority list and map the port the + // device is connected to, to display 3. + touch_device_manager()->AddTouchCalibrationData( + TouchDeviceIdentifier::FromDevice(devices_[2]), displays_[3].id(), + TouchCalibrationData()); + + touch_device_manager()->AssociateTouchscreens(&displays_, devices_); + + EXPECT_EQ(GetTouchDeviceCount(displays_[0]), 1u); + EXPECT_TRUE(AreAssociated(displays_[0], devices_[4])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[1]), 1u); + EXPECT_TRUE(AreAssociated(displays_[1], devices_[3])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[2]), 1u); + EXPECT_TRUE(AreAssociated(displays_[2], devices_[0])); + + EXPECT_EQ(GetTouchDeviceCount(displays_[3]), 3u); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[1])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[2])); + EXPECT_TRUE(AreAssociated(displays_[3], devices_[5])); +} + } // namespace display diff --git a/chromium/ui/display/manager/chromeos/touch_transform_controller_unittest.cc b/chromium/ui/display/manager/chromeos/touch_transform_controller_unittest.cc index a9107fdb35a..35b769b93f4 100644 --- a/chromium/ui/display/manager/chromeos/touch_transform_controller_unittest.cc +++ b/chromium/ui/display/manager/chromeos/touch_transform_controller_unittest.cc @@ -8,7 +8,6 @@ #include <string> #include <utility> -#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/display/manager/chromeos/default_touch_transform_setter.h" diff --git a/chromium/ui/display/manager/chromeos/update_display_configuration_task_unittest.cc b/chromium/ui/display/manager/chromeos/update_display_configuration_task_unittest.cc index c1dcc99cf8c..526d70e4e8d 100644 --- a/chromium/ui/display/manager/chromeos/update_display_configuration_task_unittest.cc +++ b/chromium/ui/display/manager/chromeos/update_display_configuration_task_unittest.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ui/display/manager/display_manager.cc b/chromium/ui/display/manager/display_manager.cc index e7fd443794b..d587d229537 100644 --- a/chromium/ui/display/manager/display_manager.cc +++ b/chromium/ui/display/manager/display_manager.cc @@ -17,7 +17,6 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/run_loop.h" #include "base/stl_util.h" @@ -42,6 +41,7 @@ #if defined(OS_CHROMEOS) #include "base/sys_info.h" #include "chromeos/system/devicemode.h" +#include "ui/display/manager/chromeos/display_util.h" #endif #if defined(OS_WIN) @@ -602,6 +602,10 @@ bool DisplayManager::SetDisplayMode(int64_t display_id, // continue to fill |display_info_list|, since we won't be // synchronously updating the displays here. resolution_changed = true; + + // Different resolutions allow different zoom factors to be set in the + // UI. To avoid confusion in the UI, reset the zoom factor to 1.0. + display_info_[display_id].set_zoom_factor(1.f); break; } if (info.device_scale_factor() != display_mode.device_scale_factor()) { @@ -651,11 +655,12 @@ void DisplayManager::RegisterDisplayProperty( Display::RotationSource::USER); display_info_[display_id].SetRotation(rotation, Display::RotationSource::ACTIVE); - // Just in case the preference file was corrupted. - // TODO(mukai): register |display_modes_| here as well, so the lookup for the - // default mode in GetActiveModeForDisplayId() gets much simpler. - if (0.5f <= ui_scale && ui_scale <= 2.0f) + + if (features::IsDisplayZoomSettingEnabled()) + display_info_[display_id].set_zoom_factor(display_zoom_factor); + else if (0.5f <= ui_scale && ui_scale <= 2.0f) display_info_[display_id].set_configured_ui_scale(ui_scale); + if (overscan_insets) display_info_[display_id].SetOverscanInsets(*overscan_insets); @@ -667,8 +672,6 @@ void DisplayManager::RegisterDisplayProperty( device_scale_factor); display_modes_[display_id] = mode; } - - display_zoom_factors_[display_id] = display_zoom_factor; } bool DisplayManager::GetActiveModeForDisplayId(int64_t display_id, @@ -690,7 +693,8 @@ bool DisplayManager::GetActiveModeForDisplayId(int64_t display_id, for (const auto& display_mode : display_modes) { if (GetDisplayIdForUIScaling() == display_id) { - if (info.configured_ui_scale() == display_mode.ui_scale()) { + if (info.configured_ui_scale() == display_mode.ui_scale() || + display_modes.size() == 1) { *mode = display_mode; return true; } @@ -1487,8 +1491,11 @@ void DisplayManager::ClearTouchCalibrationData( void DisplayManager::UpdateZoomFactor(int64_t display_id, float zoom_factor) { DCHECK(zoom_factor > 0); DCHECK_NE(display_id, kInvalidDisplayId); + auto iter = display_info_.find(display_id); + if (iter == display_info_.end()) + return; - display_zoom_factors_[display_id] = zoom_factor; + iter->second.set_zoom_factor(zoom_factor); for (const auto& display : active_display_list_) { if (display.id() == display_id) { @@ -1499,14 +1506,6 @@ void DisplayManager::UpdateZoomFactor(int64_t display_id, float zoom_factor) { } #endif -float DisplayManager::GetZoomFactorForDisplay(int64_t display_id) const { - // If there is no entry for the given display id, then the zoom factor is - // still at its default level of 100% zoom. - if (!base::ContainsKey(display_zoom_factors_, display_id)) - return 1.f; - return display_zoom_factors_.at(display_id); -} - void DisplayManager::SetDefaultMultiDisplayModeForCurrentDisplays( MultiDisplayMode mode) { DCHECK_NE(MIRRORING, mode); @@ -1572,8 +1571,8 @@ void DisplayManager::CreateMirrorWindowAsyncIfAny() { if (software_mirroring_display_list_.empty() || !delegate_) return; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&DisplayManager::CreateMirrorWindowIfAny, - weak_ptr_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&DisplayManager::CreateMirrorWindowIfAny, + weak_ptr_factory_.GetWeakPtr())); } void DisplayManager::UpdateInternalManagedDisplayModeListForTest() { @@ -1585,6 +1584,7 @@ void DisplayManager::UpdateInternalManagedDisplayModeListForTest() { SetInternalManagedDisplayModeList(info); } +// TODO(malaykeshav): Make this work with display zoom. bool DisplayManager::ZoomInternalDisplay(bool up) { int64_t display_id = IsInUnifiedMode() ? kUnifiedDisplayId : GetDisplayIdForUIScaling(); @@ -1605,6 +1605,56 @@ bool DisplayManager::ZoomInternalDisplay(bool up) { return result ? SetDisplayMode(display_id, mode) : false; } +bool DisplayManager::ZoomDisplay(int64_t display_id, bool up) { +#if defined(OS_CHROMEOS) + DCHECK(!IsInUnifiedMode()); + ManagedDisplayMode display_mode; + if (!GetActiveModeForDisplayId(display_id, &display_mode)) + return false; + const std::vector<double> zooms = GetDisplayZoomFactors(display_mode); + auto iter = display_info_.find(display_id); + if (iter == display_info_.end()) + return false; + + const double current_display_zoom = iter->second.zoom_factor(); + + // Find the index of |current_display_zoom| in |zooms|. The nearest value is + // used if the exact match is not found. + std::size_t zoom_idx = 0; + double min_diff = std::abs(zooms[zoom_idx] - current_display_zoom); + for (std::size_t i = 1; i < zooms.size(); i++) { + if (std::abs(current_display_zoom - zooms[i]) < min_diff) { + min_diff = std::abs(current_display_zoom - zooms[i]); + zoom_idx = i; + } + } + // The index of the next zoom value. + const std::size_t next_zoom_idx = zoom_idx + (up ? -1 : 1); + + // If the zoom index is out of bounds, that is, the display is already at + // maximum or minimum zoom then do nothing. + if (next_zoom_idx < 0 || next_zoom_idx >= zooms.size()) + return false; + + iter->second.set_zoom_factor(zooms[next_zoom_idx]); + UpdateDisplays(); + return true; +#else + return false; +#endif // (OS_CHROMEOS) +} + +void DisplayManager::ResetDisplayZoom(int64_t display_id) { + DCHECK(!IsInUnifiedMode()); + auto iter = display_info_.find(display_id); + if (iter == display_info_.end()) + return; + if (std::abs(iter->second.zoom_factor() - 1.f) > 0.001) { + iter->second.set_zoom_factor(1.f); + UpdateDisplays(); + } +} + bool DisplayManager::ResetDisplayToDefaultMode(int64_t id) { if (!IsActiveDisplayId(id) || !Display::IsInternalDisplayId(id)) return false; @@ -1971,9 +2021,6 @@ Display DisplayManager::CreateDisplayFromDisplayInfoById(int64_t id) { gfx::Rect bounds_in_native(display_info.size_in_pixel()); float device_scale_factor = display_info.GetEffectiveDeviceScaleFactor(); - // Apply the zoom factor for the display. - device_scale_factor *= GetZoomFactorForDisplay(id); - // Simply set the origin to (0,0). The primary display's origin is // always (0,0) and the bounds of non-primary display(s) will be updated // in |UpdateNonPrimaryDisplayBoundsForLayout| called in |UpdateDisplay|. diff --git a/chromium/ui/display/manager/display_manager.h b/chromium/ui/display/manager/display_manager.h index 2ff69b5e70c..6de527a6c4f 100644 --- a/chromium/ui/display/manager/display_manager.h +++ b/chromium/ui/display/manager/display_manager.h @@ -445,8 +445,6 @@ class DISPLAY_MANAGER_EXPORT DisplayManager base::Optional<TouchDeviceIdentifier> touch_device_identifier); void UpdateZoomFactor(int64_t display_id, float zoom_factor); #endif - // Returns the zoom foactor for the display identified by |display_id|. - float GetZoomFactorForDisplay(int64_t display_id) const; // Sets/gets default multi display mode. void SetDefaultMultiDisplayModeForCurrentDisplays(MultiDisplayMode mode); @@ -476,9 +474,17 @@ class DISPLAY_MANAGER_EXPORT DisplayManager // Zoom the internal display. bool ZoomInternalDisplay(bool up); + // Zooms the display identified by |display_id| by increasing or decreasing + // its zoom factor value by 1 unit. Zooming in will have no effect on the + // display if it is already at its maximum zoom. Vice versa for zooming out. + bool ZoomDisplay(int64_t display_id, bool up); + // Reset the internal display zoom. void ResetInternalDisplayZoom(); + // Resets the zoom value to 1 for the display identified by |display_id|. + void ResetDisplayZoom(int64_t display_id); + // Notifies observers of display configuration changes. void NotifyMetricsChanged(const Display& display, uint32_t metrics); void NotifyDisplayAdded(const Display& display); @@ -609,9 +615,6 @@ class DISPLAY_MANAGER_EXPORT DisplayManager // Selected display modes for displays. Key is the displays' ID. std::map<int64_t, ManagedDisplayMode> display_modes_; - // Zoom level for each display. - std::map<int64_t, float> display_zoom_factors_; - // When set to true, the host window's resize event updates the display's // size. This is set to true when running on desktop environment (for // debugging) so that resizing the host window will update the display diff --git a/chromium/ui/display/manager/display_pref_util.h b/chromium/ui/display/manager/display_pref_util.h deleted file mode 100644 index dea15cface2..00000000000 --- a/chromium/ui/display/manager/display_pref_util.h +++ /dev/null @@ -1,64 +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_DISPLAY_MANAGER_DISPLAY_PREF_UTIL_H -#define UI_DISPLAY_MANAGER_DISPLAY_PREF_UTIL_H - -#include <map> -#include <string> - -#include "base/strings/string_piece.h" - -namespace display { - -// Utility templates to create enum to string map and -// a function to find an enum value from a string. -template <typename T> -std::map<T, std::string>* CreateToStringMap(T k1, - const std::string& v1, - T k2, - const std::string& v2, - T k3, - const std::string& v3, - T k4, - const std::string& v4) { - std::map<T, std::string>* map = new std::map<T, std::string>(); - (*map)[k1] = v1; - (*map)[k2] = v2; - (*map)[k3] = v3; - (*map)[k4] = v4; - return map; -} - -template <typename T> -std::map<T, std::string>* CreateToStringMap(T k1, - const std::string& v1, - T k2, - const std::string& v2, - T k3, - const std::string& v3) { - std::map<T, std::string>* map = new std::map<T, std::string>(); - (*map)[k1] = v1; - (*map)[k2] = v2; - (*map)[k3] = v3; - return map; -} - -template <typename T> -bool ReverseFind(const std::map<T, std::string>* map, - const base::StringPiece& value, - T* key) { - typename std::map<T, std::string>::const_iterator iter = map->begin(); - for (; iter != map->end(); ++iter) { - if (iter->second == value) { - *key = iter->first; - return true; - } - } - return false; -} - -} // namespace display - -#endif // UI_DISPLAY_MANAGER_DISPLAY_PREF_UTIL_H diff --git a/chromium/ui/display/manager/fake_display_delegate.cc b/chromium/ui/display/manager/fake_display_delegate.cc index 8d47679bb2f..c03a926ce20 100644 --- a/chromium/ui/display/manager/fake_display_delegate.cc +++ b/chromium/ui/display/manager/fake_display_delegate.cc @@ -10,7 +10,6 @@ #include "base/command_line.h" #include "base/hash.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" diff --git a/chromium/ui/display/manager/fake_display_snapshot.cc b/chromium/ui/display/manager/fake_display_snapshot.cc index 3db0e2c84f1..9bdbd1dedd7 100644 --- a/chromium/ui/display/manager/fake_display_snapshot.cc +++ b/chromium/ui/display/manager/fake_display_snapshot.cc @@ -9,7 +9,6 @@ #include <utility> #include <vector> -#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" @@ -163,7 +162,7 @@ std::unique_ptr<FakeDisplaySnapshot> Builder::Build() { return std::make_unique<FakeDisplaySnapshot>( id_, origin_, physical_size, type_, is_aspect_preserving_scaling_, has_overscan_, has_color_correction_matrix_, name_, std::move(modes_), - current_mode_, native_mode_, product_id_, maximum_cursor_size_); + current_mode_, native_mode_, product_code_, maximum_cursor_size_); } Builder& Builder::SetId(int64_t id) { @@ -231,8 +230,8 @@ Builder& Builder::SetName(const std::string& name) { return *this; } -Builder& Builder::SetProductId(int64_t product_id) { - product_id_ = product_id; +Builder& Builder::SetProductCode(int64_t product_code) { + product_code_ = product_code; return *this; } @@ -291,7 +290,7 @@ FakeDisplaySnapshot::FakeDisplaySnapshot(int64_t display_id, DisplayModeList modes, const DisplayMode* current_mode, const DisplayMode* native_mode, - int64_t product_id, + int64_t product_code, const gfx::Size& maximum_cursor_size) : DisplaySnapshot(display_id, origin, @@ -307,7 +306,8 @@ FakeDisplaySnapshot::FakeDisplaySnapshot(int64_t display_id, std::vector<uint8_t>(), current_mode, native_mode, - product_id, + product_code, + 2018 /*year_of_manufacture */, maximum_cursor_size) {} FakeDisplaySnapshot::~FakeDisplaySnapshot() {} diff --git a/chromium/ui/display/manager/fake_display_snapshot.h b/chromium/ui/display/manager/fake_display_snapshot.h index d47abe1343c..8df35ccc3b7 100644 --- a/chromium/ui/display/manager/fake_display_snapshot.h +++ b/chromium/ui/display/manager/fake_display_snapshot.h @@ -59,7 +59,7 @@ class DISPLAY_MANAGER_EXPORT FakeDisplaySnapshot : public DisplaySnapshot { Builder& SetHasOverscan(bool has_overscan); Builder& SetHasColorCorrectionMatrix(bool val); Builder& SetName(const std::string& name); - Builder& SetProductId(int64_t product_id); + Builder& SetProductCode(int64_t product_code); Builder& SetMaximumCursorSize(const gfx::Size& maximum_cursor_size); // Sets physical_size so that the screen has the specified DPI using the // native resolution. @@ -85,7 +85,7 @@ class DISPLAY_MANAGER_EXPORT FakeDisplaySnapshot : public DisplaySnapshot { bool has_overscan_ = false; bool has_color_correction_matrix_ = false; std::string name_; - int64_t product_id_ = DisplaySnapshot::kInvalidProductID; + int64_t product_code_ = DisplaySnapshot::kInvalidProductCode; gfx::Size maximum_cursor_size_ = gfx::Size(64, 64); DisplayModeList modes_; const DisplayMode* current_mode_ = nullptr; @@ -105,7 +105,7 @@ class DISPLAY_MANAGER_EXPORT FakeDisplaySnapshot : public DisplaySnapshot { DisplayModeList modes, const DisplayMode* current_mode, const DisplayMode* native_mode, - int64_t product_id, + int64_t product_code, const gfx::Size& maximum_cursor_size); ~FakeDisplaySnapshot() override; diff --git a/chromium/ui/display/manager/json_converter.cc b/chromium/ui/display/manager/json_converter.cc index 1d4c0a4bb32..363c2da130c 100644 --- a/chromium/ui/display/manager/json_converter.cc +++ b/chromium/ui/display/manager/json_converter.cc @@ -11,7 +11,6 @@ #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "ui/display/display_layout.h" -#include "ui/display/manager/display_pref_util.h" namespace display { diff --git a/chromium/ui/display/manager/managed_display_info.cc b/chromium/ui/display/manager/managed_display_info.cc index c4906316e61..ff3c119c93a 100644 --- a/chromium/ui/display/manager/managed_display_info.cc +++ b/chromium/ui/display/manager/managed_display_info.cc @@ -260,12 +260,14 @@ ManagedDisplayInfo ManagedDisplayInfo::CreateFromSpecWithID( ManagedDisplayInfo::ManagedDisplayInfo() : id_(kInvalidDisplayId), + year_of_manufacture_(kInvalidYearOfManufacture), has_overscan_(false), active_rotation_source_(Display::RotationSource::UNKNOWN), touch_support_(Display::TouchSupport::UNKNOWN), device_scale_factor_(1.0f), device_dpi_(kDpi96), overscan_insets_in_dip_(0, 0, 0, 0), + zoom_factor_(1.f), configured_ui_scale_(1.0f), native_(false), is_aspect_preserving_scaling_(false), @@ -276,12 +278,14 @@ ManagedDisplayInfo::ManagedDisplayInfo(int64_t id, bool has_overscan) : id_(id), name_(name), + year_of_manufacture_(kInvalidYearOfManufacture), has_overscan_(has_overscan), active_rotation_source_(Display::RotationSource::UNKNOWN), touch_support_(Display::TouchSupport::UNKNOWN), device_scale_factor_(1.0f), device_dpi_(kDpi96), overscan_insets_in_dip_(0, 0, 0, 0), + zoom_factor_(1.f), configured_ui_scale_(1.0f), native_(false), is_aspect_preserving_scaling_(false), @@ -312,6 +316,9 @@ Display::Rotation ManagedDisplayInfo::GetRotation( void ManagedDisplayInfo::Copy(const ManagedDisplayInfo& native_info) { DCHECK(id_ == native_info.id_); + manufacturer_id_ = native_info.manufacturer_id_; + product_id_ = native_info.product_id_; + year_of_manufacture_ = native_info.year_of_manufacture_; name_ = native_info.name_; has_overscan_ = native_info.has_overscan_; @@ -340,6 +347,7 @@ void ManagedDisplayInfo::Copy(const ManagedDisplayInfo& native_info) { overscan_insets_in_dip_ = native_info.overscan_insets_in_dip_; rotations_ = native_info.rotations_; + zoom_factor_ = native_info.zoom_factor_; configured_ui_scale_ = native_info.configured_ui_scale_; } @@ -357,10 +365,10 @@ float ManagedDisplayInfo::GetDensityRatio() const { float ManagedDisplayInfo::GetEffectiveDeviceScaleFactor() const { if (Display::IsInternalDisplayId(id_) && device_scale_factor_ == 1.25f) - return (configured_ui_scale_ == 0.8f) ? 1.25f : 1.0f; + return ((configured_ui_scale_ == 0.8f) ? 1.25f : 1.0f) * zoom_factor_; if (device_scale_factor_ == configured_ui_scale_) - return 1.0f; - return device_scale_factor_; + return zoom_factor_; + return device_scale_factor_ * zoom_factor_; } float ManagedDisplayInfo::GetEffectiveUIScale() const { @@ -395,7 +403,7 @@ void ManagedDisplayInfo::SetOverscanInsets(const gfx::Insets& insets_in_dip) { } gfx::Insets ManagedDisplayInfo::GetOverscanInsetsInPixel() const { - return overscan_insets_in_dip_.Scale(device_scale_factor_); + return overscan_insets_in_dip_.Scale(device_scale_factor_ * zoom_factor_); } void ManagedDisplayInfo::SetManagedDisplayModes( @@ -418,9 +426,9 @@ std::string ManagedDisplayInfo::ToString() const { std::string result = base::StringPrintf( "ManagedDisplayInfo[%lld] native bounds=%s, size=%s, device-scale=%g, " - "overscan=%s, rotation=%d, ui-scale=%g, touchscreen=%s, ", + "display-zoom=%g, overscan=%s, rotation=%d, ui-scale=%g, touchscreen=%s", static_cast<long long int>(id_), bounds_in_native_.ToString().c_str(), - size_in_pixel_.ToString().c_str(), device_scale_factor_, + size_in_pixel_.ToString().c_str(), device_scale_factor_, zoom_factor_, overscan_insets_in_dip_.ToString().c_str(), rotation_degree, configured_ui_scale_, touch_support_ == Display::TouchSupport::AVAILABLE diff --git a/chromium/ui/display/manager/managed_display_info.h b/chromium/ui/display/manager/managed_display_info.h index 3f25ae1f5a7..f09dd597d89 100644 --- a/chromium/ui/display/manager/managed_display_info.h +++ b/chromium/ui/display/manager/managed_display_info.h @@ -147,6 +147,9 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo { float device_scale_factor() const { return device_scale_factor_; } void set_device_scale_factor(float scale) { device_scale_factor_ = scale; } + float zoom_factor() const { return zoom_factor_; } + void set_zoom_factor(float zoom_factor) { zoom_factor_ = zoom_factor; } + // Gets/Sets the device DPI of the display. float device_dpi() const { return device_dpi_; } void set_device_dpi(float dpi) { device_dpi_ = dpi; } @@ -253,6 +256,15 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo { maximum_cursor_size_ = size; } + const std::string& manufacturer_id() const { return manufacturer_id_; } + void set_manufacturer_id(const std::string& id) { manufacturer_id_ = id; } + + const std::string& product_id() const { return product_id_; } + void set_product_id(const std::string& id) { product_id_ = id; } + + int32_t year_of_manufacture() const { return year_of_manufacture_; } + void set_year_of_manufacture(int32_t year) { year_of_manufacture_ = year; } + // Returns a string representation of the ManagedDisplayInfo, excluding // display modes. std::string ToString() const; @@ -264,6 +276,9 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo { private: int64_t id_; std::string name_; + std::string manufacturer_id_; + std::string product_id_; + int32_t year_of_manufacture_; base::FilePath sys_path_; bool has_overscan_; std::map<Display::RotationSource, Display::Rotation> rotations_; @@ -285,6 +300,11 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo { gfx::Size size_in_pixel_; gfx::Insets overscan_insets_in_dip_; + // The zoom level currently applied to the display. This value is appended + // multiplicatively to the device scale factor to get the effecting scaling + // for a display. + float zoom_factor_; + // The pixel scale of the display. This is used to simply expand (or shrink) // the desktop over the native display resolution (useful in HighDPI display). // Note that this should not be confused with the device scale factor, which diff --git a/chromium/ui/display/mojo/BUILD.gn b/chromium/ui/display/mojo/BUILD.gn index b38524e92bc..854ee9352ff 100644 --- a/chromium/ui/display/mojo/BUILD.gn +++ b/chromium/ui/display/mojo/BUILD.gn @@ -17,7 +17,7 @@ mojom("interfaces") { ] public_deps = [ - "//mojo/common:common_custom_types", + "//mojo/public/mojom/base", "//ui/gfx/geometry/mojo", "//ui/gfx/mojo", ] diff --git a/chromium/ui/display/mojo/display_snapshot.mojom b/chromium/ui/display/mojo/display_snapshot.mojom index 8f52fe50661..fd6a818c5a0 100644 --- a/chromium/ui/display/mojo/display_snapshot.mojom +++ b/chromium/ui/display/mojo/display_snapshot.mojom @@ -4,7 +4,7 @@ module display.mojom; -import "mojo/common/file_path.mojom"; +import "mojo/public/mojom/base/file_path.mojom"; import "ui/display/mojo/display_constants.mojom"; import "ui/display/mojo/display_mode.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; @@ -21,13 +21,15 @@ struct DisplaySnapshot { bool has_color_correction_matrix; gfx.mojom.ColorSpace color_space; string display_name; - mojo.common.mojom.FilePath sys_path; + mojo_base.mojom.FilePath sys_path; array<display.mojom.DisplayMode> modes; array<uint8> edid; uint64 current_mode_index; bool has_current_mode; uint64 native_mode_index; bool has_native_mode; - int64 product_id; + // |product_code| is a combination of the manufacturer id and the product id. + int64 product_code; + int32 year_of_manufacture; gfx.mojom.Size maximum_cursor_size; }; diff --git a/chromium/ui/display/mojo/display_snapshot_struct_traits.cc b/chromium/ui/display/mojo/display_snapshot_struct_traits.cc index 85f507180c1..165c378e65b 100644 --- a/chromium/ui/display/mojo/display_snapshot_struct_traits.cc +++ b/chromium/ui/display/mojo/display_snapshot_struct_traits.cc @@ -132,7 +132,7 @@ bool StructTraits<display::mojom::DisplaySnapshotDataView, data.is_aspect_preserving_scaling(), data.has_overscan(), data.has_color_correction_matrix(), color_space, display_name, file_path, std::move(modes), std::move(edid), current_mode, native_mode, - data.product_id(), maximum_cursor_size); + data.product_code(), data.year_of_manufacture(), maximum_cursor_size); return true; } diff --git a/chromium/ui/display/mojo/display_snapshot_struct_traits.h b/chromium/ui/display/mojo/display_snapshot_struct_traits.h index 2be5ea00591..e80a273966e 100644 --- a/chromium/ui/display/mojo/display_snapshot_struct_traits.h +++ b/chromium/ui/display/mojo/display_snapshot_struct_traits.h @@ -93,9 +93,14 @@ struct StructTraits<display::mojom::DisplaySnapshotDataView, return snapshot->native_mode() != nullptr; } - static int64_t product_id( + static int64_t product_code( const std::unique_ptr<display::DisplaySnapshot>& snapshot) { - return snapshot->product_id(); + return snapshot->product_code(); + } + + static int32_t year_of_manufacture( + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { + return snapshot->year_of_manufacture(); } static const gfx::Size& maximum_cursor_size( diff --git a/chromium/ui/display/mojo/display_struct_traits_unittest.cc b/chromium/ui/display/mojo/display_struct_traits_unittest.cc index 1dd6cf72c8a..8d956238730 100644 --- a/chromium/ui/display/mojo/display_struct_traits_unittest.cc +++ b/chromium/ui/display/mojo/display_struct_traits_unittest.cc @@ -83,7 +83,7 @@ void CheckDisplaySnapShotMojoEqual(const DisplaySnapshot& input, output.has_color_correction_matrix()); EXPECT_EQ(input.display_name(), output.display_name()); EXPECT_EQ(input.sys_path(), output.sys_path()); - EXPECT_EQ(input.product_id(), output.product_id()); + EXPECT_EQ(input.product_code(), output.product_code()); EXPECT_EQ(input.modes().size(), output.modes().size()); for (size_t i = 0; i < input.modes().size(); i++) @@ -252,7 +252,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentAndNativeModesNull) { const gfx::ColorSpace display_color_space = gfx::ColorSpace::CreateREC709(); const std::string display_name("whatever display_name"); const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("a/cb"); - const int64_t product_id = 19; + const int64_t product_code = 19; + const int32_t year_of_manufacture = 1776; const DisplayMode display_mode(gfx::Size(13, 11), true, 40.0f); @@ -267,7 +268,7 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentAndNativeModesNull) { display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, - product_id, maximum_cursor_size); + product_code, year_of_manufacture, maximum_cursor_size); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -289,7 +290,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentModeNull) { const gfx::ColorSpace display_color_space = gfx::ColorSpace::CreateREC709(); const std::string display_name("whatever display_name"); const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("z/b"); - const int64_t product_id = 9; + const int64_t product_code = 9; + const int32_t year_of_manufacture = 1776; const DisplayMode display_mode(gfx::Size(13, 11), true, 50.0f); @@ -304,7 +306,7 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentModeNull) { display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, - product_id, maximum_cursor_size); + product_code, year_of_manufacture, maximum_cursor_size); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -326,7 +328,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotExternal) { const std::string display_name("HP Z24i"); const gfx::ColorSpace display_color_space = gfx::ColorSpace::CreateSRGB(); const base::FilePath sys_path = base::FilePath::FromUTF8Unsafe("a/cb"); - const int64_t product_id = 139; + const int64_t product_code = 139; + const int32_t year_of_manufacture = 2018; const DisplayMode display_mode(gfx::Size(1024, 768), false, 60.0f); const DisplayMode display_current_mode(gfx::Size(1440, 900), false, 59.89f); @@ -345,7 +348,7 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotExternal) { display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, - product_id, maximum_cursor_size); + product_code, year_of_manufacture, maximum_cursor_size); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); @@ -367,7 +370,8 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotInternal) { gfx::ColorSpace::CreateDisplayP3D65(); const std::string display_name(""); const base::FilePath sys_path; - const int64_t product_id = 139; + const int64_t product_code = 139; + const int32_t year_of_manufacture = 2018; const DisplayMode display_mode(gfx::Size(2560, 1700), false, 95.96f); @@ -382,7 +386,7 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotInternal) { display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, - product_id, maximum_cursor_size); + product_code, year_of_manufacture, maximum_cursor_size); std::unique_ptr<DisplaySnapshot> output; SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); diff --git a/chromium/ui/display/types/display_constants.h b/chromium/ui/display/types/display_constants.h index 7ef274f07b6..75baacb357e 100644 --- a/chromium/ui/display/types/display_constants.h +++ b/chromium/ui/display/types/display_constants.h @@ -16,6 +16,9 @@ constexpr int64_t kInvalidDisplayId = -1; // Display ID for a virtual display assigned to a unified desktop. constexpr int64_t kUnifiedDisplayId = -10; +// Invalid year of manufacture of the display. +constexpr int32_t kInvalidYearOfManufacture = -1; + // Used to describe the state of a multi-display configuration. enum MultipleDisplayState { MULTIPLE_DISPLAY_STATE_INVALID, diff --git a/chromium/ui/display/types/display_snapshot.cc b/chromium/ui/display/types/display_snapshot.cc index 1d919340029..b8586ffae36 100644 --- a/chromium/ui/display/types/display_snapshot.cc +++ b/chromium/ui/display/types/display_snapshot.cc @@ -74,7 +74,8 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id, const std::vector<uint8_t>& edid, const DisplayMode* current_mode, const DisplayMode* native_mode, - int64_t product_id, + int64_t product_code, + int32_t year_of_manufacture, const gfx::Size& maximum_cursor_size) : display_id_(display_id), origin_(origin), @@ -90,7 +91,8 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id, edid_(edid), current_mode_(current_mode), native_mode_(native_mode), - product_id_(product_id), + product_code_(product_code), + year_of_manufacture_(year_of_manufacture), maximum_cursor_size_(maximum_cursor_size) { // We must explicitly clear out the bytes that represent the serial number. const size_t end = @@ -122,25 +124,26 @@ std::unique_ptr<DisplaySnapshot> DisplaySnapshot::Clone() { is_aspect_preserving_scaling_, has_overscan_, has_color_correction_matrix_, color_space_, display_name_, sys_path_, std::move(clone_modes), edid_, cloned_current_mode, cloned_native_mode, - product_id_, maximum_cursor_size_); + product_code_, year_of_manufacture_, maximum_cursor_size_); } std::string DisplaySnapshot::ToString() const { return base::StringPrintf( "id=%" PRId64 " current_mode=%s native_mode=%s origin=%s" - " physical_size=%s, type=%s name=\"%s\" modes=(%s)", + " physical_size=%s, type=%s name=\"%s\" (year:%d) " + "modes=(%s)", display_id_, current_mode_ ? current_mode_->ToString().c_str() : "nullptr", native_mode_ ? native_mode_->ToString().c_str() : "nullptr", origin_.ToString().c_str(), physical_size_.ToString().c_str(), DisplayConnectionTypeString(type_).c_str(), display_name_.c_str(), - ModeListString(modes_).c_str()); + year_of_manufacture_, ModeListString(modes_).c_str()); } // static gfx::BufferFormat DisplaySnapshot::PrimaryFormat() { - return gfx::BufferFormat::BGRX_8888; + return gfx::BufferFormat::BGRA_8888; } } // namespace display diff --git a/chromium/ui/display/types/display_snapshot.h b/chromium/ui/display/types/display_snapshot.h index cbb518fd94a..03b57e58f96 100644 --- a/chromium/ui/display/types/display_snapshot.h +++ b/chromium/ui/display/types/display_snapshot.h @@ -43,7 +43,8 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { const std::vector<uint8_t>& edid, const DisplayMode* current_mode, const DisplayMode* native_mode, - int64_t product_id, + int64_t product_code, + int32_t year_of_manufacture, const gfx::Size& maximum_cursor_size); virtual ~DisplaySnapshot(); @@ -60,6 +61,7 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { return has_color_correction_matrix_; } const gfx::ColorSpace& color_space() const { return color_space_; } + void reset_color_space() { color_space_ = gfx::ColorSpace(); } const std::string& display_name() const { return display_name_; } const base::FilePath& sys_path() const { return sys_path_; } const DisplayModeList& modes() const { return modes_; } @@ -67,7 +69,8 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { const DisplayMode* current_mode() const { return current_mode_; } void set_current_mode(const DisplayMode* mode) { current_mode_ = mode; } const DisplayMode* native_mode() const { return native_mode_; } - int64_t product_id() const { return product_id_; } + int64_t product_code() const { return product_code_; } + int32_t year_of_manufacture() const { return year_of_manufacture_; } const gfx::Size& maximum_cursor_size() const { return maximum_cursor_size_; } void add_mode(const DisplayMode* mode) { modes_.push_back(mode->Clone()); } @@ -78,8 +81,8 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { // Returns a textual representation of this display state. std::string ToString() const; - // Used when no product id known. - static const int64_t kInvalidProductID = -1; + // Used when no |product_code_| known. + static const int64_t kInvalidProductCode = -1; // Returns the buffer format to be used for the primary plane buffer. static gfx::BufferFormat PrimaryFormat(); @@ -102,7 +105,7 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { // Whether this display has advanced color correction available. const bool has_color_correction_matrix_; - const gfx::ColorSpace color_space_; + gfx::ColorSpace color_space_; const std::string display_name_; @@ -120,8 +123,10 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { // "Best" mode supported by the output. const DisplayMode* const native_mode_; - // Combination of manufacturer and product code. - const int64_t product_id_; + // Combination of manufacturer id and product id. + const int64_t product_code_; + + const int32_t year_of_manufacture_; // Maximum supported cursor size on this display. const gfx::Size maximum_cursor_size_; diff --git a/chromium/ui/display/util/OWNERS b/chromium/ui/display/util/OWNERS new file mode 100644 index 00000000000..3fd44a4deeb --- /dev/null +++ b/chromium/ui/display/util/OWNERS @@ -0,0 +1,4 @@ +dcastagna@chromium.org +mcasas@chromium.org + +# COMPONENT: OS>Kernel>Graphics diff --git a/chromium/ui/display/util/edid_parser.cc b/chromium/ui/display/util/edid_parser.cc index 8b310db96a8..9154e2f6cfa 100644 --- a/chromium/ui/display/util/edid_parser.cc +++ b/chromium/ui/display/util/edid_parser.cc @@ -10,100 +10,253 @@ #include "base/hash.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/sys_byteorder.h" #include "third_party/skia/include/core/SkColorSpace.h" +#include "ui/display/types/display_constants.h" #include "ui/display/util/display_util.h" #include "ui/gfx/geometry/size.h" namespace display { -namespace { - -// Returns a 32-bit identifier for this model of display, using -// |manufacturer_id| and |product_code|. -uint32_t GetProductID(uint16_t manufacturer_id, uint16_t product_code) { - return ((static_cast<uint32_t>(manufacturer_id) << 16) | - (static_cast<uint32_t>(product_code))); +EdidParser::EdidParser(const std::vector<uint8_t>& edid_blob) + : manufacturer_id_(0), + product_id_(0), + year_of_manufacture_(display::kInvalidYearOfManufacture), + gamma_(0.0), + bits_per_channel_(-1), + primaries_({0}) { + ParseEdid(edid_blob); } -} // namespace - -bool GetDisplayIdFromEDID(const std::vector<uint8_t>& edid, - uint8_t output_index, - int64_t* display_id_out, - int64_t* product_id_out) { - uint16_t manufacturer_id = 0; - uint16_t product_code = 0; - std::string product_name; +EdidParser::~EdidParser() = default; - // ParseOutputDeviceData fails if it doesn't have product_name. - ParseOutputDeviceData(edid, &manufacturer_id, &product_code, &product_name, - nullptr, nullptr); - - if (manufacturer_id == 0) - return false; +uint32_t EdidParser::GetProductCode() const { + return ((static_cast<uint32_t>(manufacturer_id_) << 16) | + (static_cast<uint32_t>(product_id_))); +} +int64_t EdidParser::GetDisplayId(uint8_t output_index) const { // Generates product specific value from product_name instead of product code. - // See crbug.com/240341 - uint32_t product_code_hash = - product_name.empty() ? 0 : base::Hash(product_name); + // See https://crbug.com/240341 + const uint32_t product_code_hash = + display_name_.empty() ? 0 : base::Hash(display_name_); // An ID based on display's index will be assigned later if this call fails. - *display_id_out = - GenerateDisplayID(manufacturer_id, product_code_hash, output_index); - // |product_id_out| is 64-bit signed so it can store -1 as kInvalidProductID - // and not match a valid product id which will all be in the lowest 32-bits. - if (product_id_out) - *product_id_out = GetProductID(manufacturer_id, product_code); - return true; + return GenerateDisplayID(manufacturer_id_, product_code_hash, output_index); +} + +// static +void EdidParser::SplitProductCodeInManufacturerIdAndProductId( + int64_t product_code, + uint16_t* manufacturer_id, + uint16_t* product_id) { + DCHECK(manufacturer_id); + DCHECK(product_id); + // Undo GetProductCode() packing. + *product_id = product_code & 0xFFFF; + *manufacturer_id = (product_code >> 16) & 0xFFFF; +} + +// static +std::string EdidParser::ManufacturerIdToString(uint16_t manufacturer_id) { + // Constants are taken from "VESA Enhanced EDID Standard" Release A, Revision + // 2, Sep 2006, Sec 3.4.1 "ID Manufacturer Name: 2 Bytes". Essentially these + // are 3 5-bit ASCII characters packed in 2 bytes, where 1 means 'A', etc. + constexpr uint8_t kFiveBitAsciiMask = 0x1F; + constexpr char kFiveBitToAsciiOffset = 'A' - 1; + constexpr size_t kSecondLetterOffset = 5; + constexpr size_t kFirstLetterOffset = 10; + + char out[4] = {}; + out[2] = (manufacturer_id & kFiveBitAsciiMask) + kFiveBitToAsciiOffset; + out[1] = ((manufacturer_id >> kSecondLetterOffset) & kFiveBitAsciiMask) + + kFiveBitToAsciiOffset; + out[0] = ((manufacturer_id >> kFirstLetterOffset) & kFiveBitAsciiMask) + + kFiveBitToAsciiOffset; + return out; +} + +// static +std::string EdidParser::ProductIdToString(uint16_t product_id) { + // From "VESA Enhanced EDID Standard" Release A, Revision 2, Sep 2006, Sec + // 3.4.2 "ID Product Code: 2 Bytes": "The ID product code field, [...] + // contains a 2-byte manufacturer assigned product code. [...] The 2 byte + // number is stored in hex with the least significant byte listed first." + uint8_t lower_char = (product_id >> 8) & 0xFF; + uint8_t upper_char = product_id & 0xFF; + return base::StringPrintf("%02X%02X", upper_char, lower_char); } -bool ParseOutputDeviceData(const std::vector<uint8_t>& edid, - uint16_t* manufacturer_id, - uint16_t* product_code, - std::string* human_readable_name, - gfx::Size* active_pixel_out, - gfx::Size* physical_display_size_out) { +void EdidParser::ParseEdid(const std::vector<uint8_t>& edid) { // See http://en.wikipedia.org/wiki/Extended_display_identification_data // for the details of EDID data format. We use the following data: // bytes 8-9: manufacturer EISA ID, in big-endian // bytes 10-11: manufacturer product code, in little-endian - // bytes 54-125: four descriptors (18-bytes each) which may contain - // the display name. constexpr size_t kManufacturerOffset = 8; constexpr size_t kManufacturerLength = 2; - constexpr size_t kProductCodeOffset = 10; - constexpr size_t kProductCodeLength = 2; - constexpr size_t kDescriptorOffset = 54; - constexpr size_t kNumDescriptors = 4; - constexpr size_t kDescriptorLength = 18; - // The specifier types. - constexpr uint8_t kMonitorNameDescriptor = 0xfc; + constexpr size_t kProductIdOffset = 10; + constexpr size_t kProductIdLength = 2; - if (manufacturer_id) { - if (edid.size() < kManufacturerOffset + kManufacturerLength) { - LOG(ERROR) << "Too short EDID data: manufacturer id"; - return false; - } + if (edid.size() < kManufacturerOffset + kManufacturerLength) { + LOG(ERROR) << "Too short EDID data: manufacturer id"; + // TODO(mcasas): add UMA, https://crbug.com/821393. + return; // Any other fields below are beyond this edid offset. + } + // ICC filename is generated based on these ids. We always read this as big + // endian so that the file name matches bytes 8-11 as they appear in EDID. + manufacturer_id_ = + (edid[kManufacturerOffset] << 8) + edid[kManufacturerOffset + 1]; + + if (edid.size() < kProductIdOffset + kProductIdLength) { + LOG(ERROR) << "Too short EDID data: product id"; + // TODO(mcasas): add UMA, https://crbug.com/821393. + return; // Any other fields below are beyond this edid offset. + } + product_id_ = (edid[kProductIdOffset] << 8) + edid[kProductIdOffset + 1]; - // ICC filename is generated based on these ids. We always read this as big - // endian so that the file name matches bytes 8-11 as they appear in EDID. - *manufacturer_id = - (edid[kManufacturerOffset] << 8) + edid[kManufacturerOffset + 1]; + // Constants are taken from "VESA Enhanced EDID Standard" Release A, Revision + // 2, Sep 2006, Sec 3.4.4 "Week and Year of Manufacture or Model Year: 2 + // Bytes". + constexpr size_t kYearOfManufactureOffset = 17; + constexpr uint32_t kValidValueLowerBound = 0x10; + constexpr int32_t kYearOffset = 1990; + + if (edid.size() < kYearOfManufactureOffset + 1) { + LOG(ERROR) << "Too short EDID data: year of manufacture"; + // TODO(mcasas): add UMA, https://crbug.com/821393. + return; // Any other fields below are beyond this edid offset. } + const uint8_t byte_data = edid[kYearOfManufactureOffset]; + if (byte_data >= kValidValueLowerBound) + year_of_manufacture_ = byte_data + kYearOffset; - if (product_code) { - if (edid.size() < kProductCodeOffset + kProductCodeLength) { - LOG(ERROR) << "Too short EDID data: manufacturer product code"; - return false; - } + // Constants are taken from "VESA Enhanced EDID Standard" Release A, Revision + // 1, Feb 2000, Sec 3.6 "Basic Display Parameters and Features: 5 bytes" + static constexpr int kBitsPerChannelTable[] = {0, 6, 8, 10, 12, 14, 16, 0}; + + constexpr size_t kEDIDRevisionNumberOffset = 19; + constexpr uint8_t kEDIDRevision4Value = 4; + + constexpr size_t kVideoInputDefinitionOffset = 20; + constexpr uint8_t kDigitalInfoMask = 0x80; + constexpr uint8_t kColorBitDepthMask = 0x70; + constexpr uint8_t kColorBitDepthOffset = 4; + + if (edid.size() < kVideoInputDefinitionOffset + 1) { + LOG(ERROR) << "Too short EDID data: bits per channel"; + // TODO(mcasas): add UMA, https://crbug.com/821393. + return; // Any other fields below are beyond this edid offset. + } + if (edid[kEDIDRevisionNumberOffset] >= kEDIDRevision4Value && + (edid[kVideoInputDefinitionOffset] & kDigitalInfoMask)) { + // EDID needs to be revision 4 at least, and kDigitalInfoMask be set for + // the Video Input Definition entry to describe a digital interface. + bits_per_channel_ = kBitsPerChannelTable[( + (edid[kVideoInputDefinitionOffset] & kColorBitDepthMask) >> + kColorBitDepthOffset)]; + } + + // Constants are taken from "VESA Enhanced EDID Standard" Release A, Revision + // 2, Sep 2006, Sec. 3.6.3 "Display Transfer Characteristics (GAMMA ): 1 Byte" + constexpr size_t kGammaOffset = 23; + constexpr double kGammaMultiplier = 100.0; + constexpr double kGammaBias = 100.0; + + if (edid.size() < kGammaOffset + 1) { + LOG(ERROR) << "Too short EDID data: gamma"; + // TODO(mcasas): add UMA, https://crbug.com/821393. + return; // Any other fields below are beyond this edid offset. + } + if (edid[kGammaOffset] != 0xFF) { + // Otherwise the byte at kGammaOffset is 0xFF, gamma is stored elsewhere. + gamma_ = (edid[kGammaOffset] + kGammaBias) / kGammaMultiplier; + } + + // Offsets, lengths, positions and masks are taken from [1] (or [2]). + // [1] http://en.wikipedia.org/wiki/Extended_display_identification_data + // [2] "VESA Enhanced EDID Standard " Release A, Revision 1, Feb 2000, Sec 3.7 + // "Phosphor or Filter Chromaticity: 10 bytes" + constexpr size_t kChromaticityOffset = 25; + constexpr unsigned int kChromaticityLength = 10; - *product_code = - (edid[kProductCodeOffset] << 8) + edid[kProductCodeOffset + 1]; + constexpr size_t kRedGreenLsbOffset = 25; + constexpr uint8_t kRedxLsbPosition = 6; + constexpr uint8_t kRedyLsbPosition = 4; + constexpr uint8_t kGreenxLsbPosition = 3; + constexpr uint8_t kGreenyLsbPosition = 0; + + constexpr size_t kBlueWhiteLsbOffset = 26; + constexpr uint8_t kBluexLsbPosition = 6; + constexpr uint8_t kBlueyLsbPosition = 4; + constexpr uint8_t kWhitexLsbPosition = 3; + constexpr uint8_t kWhiteyLsbPosition = 0; + + // All LSBits parts are 2 bits wide. + constexpr uint8_t kLsbMask = 0x3; + + constexpr size_t kRedxMsbOffset = 27; + constexpr size_t kRedyMsbOffset = 28; + constexpr size_t kGreenxMsbOffset = 29; + constexpr size_t kGreenyMsbOffset = 30; + constexpr size_t kBluexMsbOffset = 31; + constexpr size_t kBlueyMsbOffset = 32; + constexpr size_t kWhitexMsbOffset = 33; + constexpr size_t kWhiteyMsbOffset = 34; + + static_assert( + kChromaticityOffset + kChromaticityLength == kWhiteyMsbOffset + 1, + "EDID Parameter section length error"); + + if (edid.size() < kChromaticityOffset + kChromaticityLength) { + LOG(ERROR) << "Too short EDID data: chromaticity coordinates"; + // TODO(mcasas): add UMA, https://crbug.com/821393. + return; // Any other fields below are beyond this edid offset. } - if (human_readable_name) - human_readable_name->clear(); + const uint8_t red_green_lsbs = edid[kRedGreenLsbOffset]; + const uint8_t blue_white_lsbs = edid[kBlueWhiteLsbOffset]; + + // Recompose the 10b values by appropriately mixing the 8 MSBs and the 2 LSBs, + // then rescale to 1024; + primaries_.fRX = ((edid[kRedxMsbOffset] << 2) + + ((red_green_lsbs >> kRedxLsbPosition) & kLsbMask)) / + 1024.0f; + primaries_.fRY = ((edid[kRedyMsbOffset] << 2) + + ((red_green_lsbs >> kRedyLsbPosition) & kLsbMask)) / + 1024.0f; + primaries_.fGX = ((edid[kGreenxMsbOffset] << 2) + + ((red_green_lsbs >> kGreenxLsbPosition) & kLsbMask)) / + 1024.0f; + primaries_.fGY = ((edid[kGreenyMsbOffset] << 2) + + ((red_green_lsbs >> kGreenyLsbPosition) & kLsbMask)) / + 1024.0f; + primaries_.fBX = ((edid[kBluexMsbOffset] << 2) + + ((blue_white_lsbs >> kBluexLsbPosition) & kLsbMask)) / + 1024.0f; + primaries_.fBY = ((edid[kBlueyMsbOffset] << 2) + + ((blue_white_lsbs >> kBlueyLsbPosition) & kLsbMask)) / + 1024.0f; + primaries_.fWX = ((edid[kWhitexMsbOffset] << 2) + + ((blue_white_lsbs >> kWhitexLsbPosition) & kLsbMask)) / + 1024.0f; + primaries_.fWY = ((edid[kWhiteyMsbOffset] << 2) + + ((blue_white_lsbs >> kWhiteyLsbPosition) & kLsbMask)) / + 1024.0f; + // TODO(mcasas): Up to two additional White Point coordinates can be provided + // in a Display Descriptor. Read them if we are not satisfied with |fWX| or + // |fWy|. https://crbug.com/771345. + // See http://en.wikipedia.org/wiki/Extended_display_identification_data + // for the details of EDID data format. We use the following data: + // bytes 54-125: four descriptors (18-bytes each) which may contain + // the display name. + constexpr size_t kDescriptorOffset = 54; + constexpr size_t kNumDescriptors = 4; + constexpr size_t kDescriptorLength = 18; + // The specifier types. + constexpr uint8_t kMonitorNameDescriptor = 0xfc; + + display_name_.clear(); for (size_t i = 0; i < kNumDescriptors; ++i) { if (edid.size() < kDescriptorOffset + (i + 1) * kDescriptorLength) break; @@ -114,7 +267,9 @@ bool ParseOutputDeviceData(const std::vector<uint8_t>& edid, if (edid[offset] != 0 && edid[offset + 1] != 0) { constexpr int kMaxResolution = 10080; // 8k display. - if (active_pixel_out) { + // EDID may contain multiple DTD. Use the first one, that contains the + // highest resolution. + if (active_pixel_size_.IsEmpty()) { constexpr size_t kHorizontalPixelLsbOffset = 2; constexpr size_t kHorizontalPixelMsbOffset = 4; constexpr size_t kVerticalPixelLsbOffset = 5; @@ -128,25 +283,7 @@ bool ParseOutputDeviceData(const std::vector<uint8_t>& edid, const uint8_t v_msb = edid[offset + kVerticalPixelMsbOffset]; int v_pixel = std::min(v_lsb + ((v_msb & 0xF0) << 4), kMaxResolution); - active_pixel_out->SetSize(h_pixel, v_pixel); - // EDID may contain multiple DTD. Use first one that - // contains the highest resolution. - active_pixel_out = nullptr; - } - - if (physical_display_size_out) { - constexpr size_t kHorizontalSizeLsbOffset = 12; - constexpr size_t kVerticalSizeLsbOffset = 13; - constexpr size_t kSizeMsbOffset = 14; - - const uint8_t h_lsb = edid[offset + kHorizontalSizeLsbOffset]; - const uint8_t v_lsb = edid[offset + kVerticalSizeLsbOffset]; - - const uint8_t msb = edid[offset + kSizeMsbOffset]; - int h_size = h_lsb + ((msb & 0xF0) << 4); - int v_size = v_lsb + ((msb & 0x0F) << 8); - physical_display_size_out->SetSize(h_size, v_size); - physical_display_size_out = nullptr; + active_pixel_size_.SetSize(h_pixel, v_pixel); } continue; } @@ -160,33 +297,24 @@ bool ParseOutputDeviceData(const std::vector<uint8_t>& edid, // we should check bytes 0-2 and 4, since it may have other values in // case that the descriptor contains other type of data. if (edid[offset] == 0 && edid[offset + 1] == 0 && edid[offset + 2] == 0 && - edid[offset + 3] == kMonitorNameDescriptor && edid[offset + 4] == 0 && - human_readable_name) { - std::string found_name(reinterpret_cast<const char*>(&edid[offset + 5]), - kDescriptorLength - 5); - base::TrimWhitespaceASCII( - found_name, base::TRIM_TRAILING, human_readable_name); + edid[offset + 3] == kMonitorNameDescriptor && edid[offset + 4] == 0) { + std::string name(reinterpret_cast<const char*>(&edid[offset + 5]), + kDescriptorLength - 5); + base::TrimWhitespaceASCII(name, base::TRIM_TRAILING, &display_name_); continue; } } - // Verify if the |human_readable_name| consists of printable characters only. + // Verify if the |display_name_| consists of printable characters only. // TODO(oshima|muka): Consider replacing unprintable chars with white space. - if (human_readable_name) { - for (size_t i = 0; i < human_readable_name->size(); ++i) { - char c = (*human_readable_name)[i]; - if (!isascii(c) || !isprint(c)) { - human_readable_name->clear(); - LOG(ERROR) << "invalid EDID: human unreadable char in name"; - return false; - } + for (const char c : display_name_) { + if (!isascii(c) || !isprint(c)) { + display_name_.clear(); + LOG(ERROR) << "invalid EDID: human unreadable char in name"; + // TODO(mcasas): add UMA, https://crbug.com/821393. } } - return true; -} - -bool ParseOutputOverscanFlag(const std::vector<uint8_t>& edid, bool* flag) { // See http://en.wikipedia.org/wiki/Extended_display_identification_data // for the extension format of EDID. Also see EIA/CEA-861 spec for // the format of the extensions and how video capability is encoded. @@ -205,9 +333,11 @@ bool ParseOutputOverscanFlag(const std::vector<uint8_t>& edid, bool* flag) { constexpr uint8_t kITOverscanFlagPosition = 2; constexpr uint8_t kCEOverscanFlagPosition = 0; - if (edid.size() <= kNumExtensionsOffset) - return false; - + if (edid.size() < kNumExtensionsOffset + 1) { + LOG(ERROR) << "Too short EDID data: extensions"; + // TODO(mcasas): add UMA, https://crbug.com/821393. + return; // Any other fields below are beyond this edid offset. + } const uint8_t num_extensions = edid[kNumExtensionsOffset]; for (size_t i = 0; i < num_extensions; ++i) { @@ -242,146 +372,15 @@ bool ParseOutputOverscanFlag(const std::vector<uint8_t>& edid, bool* flag) { continue; } - // The difference between preferred, IT, and CE video formats - // doesn't matter. Set |flag| to true if any of these flags are true. - *flag = (edid[data_offset + 2] & (1 << kPTOverscanFlagPosition)) || - (edid[data_offset + 2] & (1 << kITOverscanFlagPosition)) || - (edid[data_offset + 2] & (1 << kCEOverscanFlagPosition)); - return true; + // The difference between preferred, IT, and CE video formats doesn't + // matter. Set the flag to true if any of these flags are true. + overscan_flag_ = + (edid[data_offset + 2] & (1 << kPTOverscanFlagPosition)) || + (edid[data_offset + 2] & (1 << kITOverscanFlagPosition)) || + (edid[data_offset + 2] & (1 << kCEOverscanFlagPosition)); + break; } } - - return false; -} - -bool ParseChromaticityCoordinates(const std::vector<uint8_t>& edid, - SkColorSpacePrimaries* primaries) { - DCHECK(primaries); - - // Offsets, lengths, positions and masks are taken from [1] (or [2]). - // [1] http://en.wikipedia.org/wiki/Extended_display_identification_data - // [2] "VESA Enhanced EDID Standard " Release A, Revision 1, Feb 2000, Sec 3.7 - // "Phosphor or Filter Chromaticity: 10 bytes" - constexpr size_t kChromaticityOffset = 25; - constexpr unsigned int kChromaticityLength = 10; - - constexpr size_t kRedGreenLsbOffset = 25; - constexpr uint8_t kRedxLsbPosition = 6; - constexpr uint8_t kRedyLsbPosition = 4; - constexpr uint8_t kGreenxLsbPosition = 3; - constexpr uint8_t kGreenyLsbPosition = 0; - - constexpr size_t kBlueWhiteLsbOffset = 26; - constexpr uint8_t kBluexLsbPosition = 6; - constexpr uint8_t kBlueyLsbPosition = 4; - constexpr uint8_t kWhitexLsbPosition = 3; - constexpr uint8_t kWhiteyLsbPosition = 0; - - // All LSBits parts are 2 bits wide. - constexpr uint8_t kLsbMask = 0x3; - - constexpr size_t kRedxMsbOffset = 27; - constexpr size_t kRedyMsbOffset = 28; - constexpr size_t kGreenxMsbOffset = 29; - constexpr size_t kGreenyMsbOffset = 30; - constexpr size_t kBluexMsbOffset = 31; - constexpr size_t kBlueyMsbOffset = 32; - constexpr size_t kWhitexMsbOffset = 33; - constexpr size_t kWhiteyMsbOffset = 34; - - static_assert( - kChromaticityOffset + kChromaticityLength == kWhiteyMsbOffset + 1, - "EDID Parameter section length error"); - - if (edid.size() < kChromaticityOffset + kChromaticityLength) { - LOG(ERROR) << "Too short EDID data: chromaticity coordinates"; - return false; - } - - const uint8_t red_green_lsbs = edid[kRedGreenLsbOffset]; - const uint8_t blue_white_lsbs = edid[kBlueWhiteLsbOffset]; - - // Recompose the 10b values by appropriately mixing the 8 MSBs and the 2 LSBs, - // then rescale to 1024; - primaries->fRX = ((edid[kRedxMsbOffset] << 2) + - ((red_green_lsbs >> kRedxLsbPosition) & kLsbMask)) / - 1024.0f; - primaries->fRY = ((edid[kRedyMsbOffset] << 2) + - ((red_green_lsbs >> kRedyLsbPosition) & kLsbMask)) / - 1024.0f; - primaries->fGX = ((edid[kGreenxMsbOffset] << 2) + - ((red_green_lsbs >> kGreenxLsbPosition) & kLsbMask)) / - 1024.0f; - primaries->fGY = ((edid[kGreenyMsbOffset] << 2) + - ((red_green_lsbs >> kGreenyLsbPosition) & kLsbMask)) / - 1024.0f; - primaries->fBX = ((edid[kBluexMsbOffset] << 2) + - ((blue_white_lsbs >> kBluexLsbPosition) & kLsbMask)) / - 1024.0f; - primaries->fBY = ((edid[kBlueyMsbOffset] << 2) + - ((blue_white_lsbs >> kBlueyLsbPosition) & kLsbMask)) / - 1024.0f; - primaries->fWX = ((edid[kWhitexMsbOffset] << 2) + - ((blue_white_lsbs >> kWhitexLsbPosition) & kLsbMask)) / - 1024.0f; - primaries->fWY = ((edid[kWhiteyMsbOffset] << 2) + - ((blue_white_lsbs >> kWhiteyLsbPosition) & kLsbMask)) / - 1024.0f; - - // TODO(mcasas): Up to two additional White Point coordinates can be provided - // in a Display Descriptor.Read them if we are not satisfied with |fWX| or - // |FWy|. https://crbug.com/771345. - return true; -} - -DISPLAY_UTIL_EXPORT bool ParseGammaValue(const std::vector<uint8_t>& edid, - double* gamma) { - // Constants are taken from "VESA Enhanced EDID Standard" Release A, Revision - // 2, Sep 2006, Sec. 3.6.3 "Display Transfer Characteristics (GAMMA ): 1 Byte" - constexpr size_t kGammaOffset = 23; - constexpr double kGammaMultiplier = 100.0; - constexpr double kGammaBias = 100.0; - - if (edid.size() < kGammaOffset + 1) { - LOG(ERROR) << "Too short EDID data: gamma"; - return false; - } - if (edid[kGammaOffset] == 0xFF) // Gamma is stored elsewhere. - return false; - DCHECK(gamma); - *gamma = (edid[kGammaOffset] + kGammaBias) / kGammaMultiplier; - return true; -} - -DISPLAY_UTIL_EXPORT bool ParseBitsPerChannel(const std::vector<uint8_t>& edid, - int* bits_per_channel) { - // Constants are taken from "VESA Enhanced EDID Standard" Release A, Revision - // 1, Feb 2000, Sec 3.6 "Basic Display Parameters and Features: 5 bytes" - static constexpr int kBitsPerChannelTable[] = {0, 6, 8, 10, 12, 14, 16, 0}; - - constexpr size_t kEDIDRevisionNumberOffset = 19; - constexpr uint8_t kEDIDRevision4Value = 4; - - constexpr size_t kVideoInputDefinitionOffset = 20; - constexpr uint8_t kDigitalInfoMask = 0x80; - constexpr uint8_t kColorBitDepthMask = 0x70; - constexpr uint8_t kColorBitDepthOffset = 4; - - if (edid.size() < kVideoInputDefinitionOffset + 1) { - LOG(ERROR) << "Too short EDID data: gamma"; - return false; - } - // EDID needs to be revision 4 at least, and kDigitalInfoMask be set for - // the Video Input Definition entry to describe a digital interface. - if (edid[kEDIDRevisionNumberOffset] < kEDIDRevision4Value || - !(edid[kVideoInputDefinitionOffset] & kDigitalInfoMask)) { - return false; - } - DCHECK(bits_per_channel); - *bits_per_channel = kBitsPerChannelTable[( - (edid[kVideoInputDefinitionOffset] & kColorBitDepthMask) >> - kColorBitDepthOffset)]; - return true; } } // namespace display diff --git a/chromium/ui/display/util/edid_parser.h b/chromium/ui/display/util/edid_parser.h index 17e67debfe1..5323f527da5 100644 --- a/chromium/ui/display/util/edid_parser.h +++ b/chromium/ui/display/util/edid_parser.h @@ -11,59 +11,68 @@ #include <vector> #include "base/compiler_specific.h" +#include "base/optional.h" +#include "third_party/skia/include/core/SkColorSpace.h" #include "ui/display/util/display_util_export.h" - -namespace gfx { -class Size; -} - -struct SkColorSpacePrimaries; - -// EDID (Extended Display Identification Data) is a format for monitor -// metadata. This provides a parser for the data. +#include "ui/gfx/geometry/size.h" namespace display { -// Generates the display id and product id for the pair of |edid| and |index|, -// and store in |display_id_out| and |product_id_out|. Returns true if the -// display id is successfully generated, or false otherwise. -DISPLAY_UTIL_EXPORT bool GetDisplayIdFromEDID(const std::vector<uint8_t>& edid, - uint8_t index, - int64_t* display_id_out, - int64_t* product_id_out); - -// Parses |edid| as EDID data and stores extracted data into |manufacturer_id|, -// |product_code|, |human_readable_name|, |active_pixel_out| and -// |physical_display_size_out|, then returns true. nullptr can be passed for -// unwanted output parameters. Some devices (especially internal displays) may -// not have the field for |human_readable_name|, and it will return true in -// that case. -DISPLAY_UTIL_EXPORT bool ParseOutputDeviceData( - const std::vector<uint8_t>& edid, - uint16_t* manufacturer_id, - uint16_t* product_code, - std::string* human_readable_name, - gfx::Size* active_pixel_out, - gfx::Size* physical_display_size_out); - -DISPLAY_UTIL_EXPORT bool ParseOutputOverscanFlag( - const std::vector<uint8_t>& edid, - bool* flag); - -// Extracts from |edid| the |primaries| chromaticity coordinates (CIE xy -// coordinates for Red, Green and Blue channels and for the White Point). -DISPLAY_UTIL_EXPORT bool ParseChromaticityCoordinates( - const std::vector<uint8_t>& edid, - SkColorSpacePrimaries* primaries) WARN_UNUSED_RESULT; - -// Extracts the gamma value from |edid| and returns it, or returns 0.0. -DISPLAY_UTIL_EXPORT bool ParseGammaValue(const std::vector<uint8_t>& edid, - double* gamma) WARN_UNUSED_RESULT; - -// Extracts the bits per channel from |edid| and returns it, or returns 0. -DISPLAY_UTIL_EXPORT bool ParseBitsPerChannel(const std::vector<uint8_t>& edid, - int* bits_per_channel) - WARN_UNUSED_RESULT; +// This class parses a EDID (Extended Display Identification Data) binary blob +// passed on constructor, and provides access to the parsed information, plus +// a few utility postprocessings. +class DISPLAY_UTIL_EXPORT EdidParser { + public: + explicit EdidParser(const std::vector<uint8_t>& edid_blob); + ~EdidParser(); + + uint16_t manufacturer_id() const { return manufacturer_id_; } + uint16_t product_id() const { return product_id_; } + const std::string& display_name() const { return display_name_; } + const gfx::Size& active_pixel_size() const { return active_pixel_size_; } + int32_t year_of_manufacture() const { return year_of_manufacture_; } + bool has_overscan_flag() const { return overscan_flag_.has_value(); } + bool overscan_flag() const { return overscan_flag_.value(); } + double gamma() const { return gamma_; } + int32_t bits_per_channel() const { return bits_per_channel_; } + const SkColorSpacePrimaries& primaries() const { return primaries_; } + + // Returns a 32-bit identifier for this display |manufacturer_id_| and + // |product_id_|. + uint32_t GetProductCode() const; + + // Generates a unique display id out of a mix of |manufacturer_id_|, hashed + // |display_name_| if available, and |output_index|. + int64_t GetDisplayId(uint8_t output_index) const; + + // Splits the |product_code| (as returned by GetDisplayId()) into its + // constituents |manufacturer_id| and |product_id|. + static void SplitProductCodeInManufacturerIdAndProductId( + int64_t product_code, + uint16_t* manufacturer_id, + uint16_t* product_id); + // Extracts the three letter Manufacturer ID out of |manufacturer_id|. + static std::string ManufacturerIdToString(uint16_t manufacturer_id); + // Extracts the 2 Byte Product ID as hex out of |product_id|. + static std::string ProductIdToString(uint16_t product_id); + + private: + // Parses |edid_blob|, filling up as many as possible fields below. + void ParseEdid(const std::vector<uint8_t>& edid); + + uint16_t manufacturer_id_; + uint16_t product_id_; + std::string display_name_; + // Active pixel size from the first detailed timing descriptor in the EDID. + gfx::Size active_pixel_size_; + int32_t year_of_manufacture_; + base::Optional<bool> overscan_flag_; + double gamma_; + int bits_per_channel_; + SkColorSpacePrimaries primaries_; + + DISALLOW_COPY_AND_ASSIGN(EdidParser); +}; } // namespace display diff --git a/chromium/ui/display/util/edid_parser_fuzzer.cc b/chromium/ui/display/util/edid_parser_fuzzer.cc index 7ae1b8f46e4..d3d8f0c12e3 100644 --- a/chromium/ui/display/util/edid_parser_fuzzer.cc +++ b/chromium/ui/display/util/edid_parser_fuzzer.cc @@ -23,15 +23,7 @@ Environment* env = new Environment(); extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::vector<uint8_t> edid; edid.assign(data, data + size); - uint16_t manufacturer_id, product_code; - std::string human_readable_name; - gfx::Size active_pixel_size, physical_display_size; - bool overscan; - - display::ParseOutputDeviceData(edid, &manufacturer_id, &product_code, - &human_readable_name, &active_pixel_size, - &physical_display_size); - - display::ParseOutputOverscanFlag(edid, &overscan); + // Ctor already parses |edid|, which is what we want here. + display::EdidParser edid_parser(edid); return 0; } diff --git a/chromium/ui/display/util/edid_parser_unittest.cc b/chromium/ui/display/util/edid_parser_unittest.cc index 7511a13cdab..8d3073361ab 100644 --- a/chromium/ui/display/util/edid_parser_unittest.cc +++ b/chromium/ui/display/util/edid_parser_unittest.cc @@ -12,21 +12,20 @@ #include "base/numerics/ranges.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColorSpace.h" +#include "ui/display/types/display_constants.h" #include "ui/gfx/geometry/size.h" using ::testing::AssertionFailure; using ::testing::AssertionSuccess; +using ::testing::TestWithParam; +using ::testing::ValuesIn; namespace display { namespace { -// Returns the number of characters in the string literal but doesn't count its -// terminator NULL byte. -#define charsize(str) (arraysize(str) - 1) - // Sample EDID data extracted from real devices. -const unsigned char kNormalDisplay[] = +constexpr unsigned char kNormalDisplay[] = "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01" "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25" "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" @@ -35,8 +34,9 @@ const unsigned char kNormalDisplay[] = "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48" "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff" "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71"; +constexpr size_t kNormalDisplayLength = arraysize(kNormalDisplay); -const unsigned char kInternalDisplay[] = +constexpr unsigned char kInternalDisplay[] = "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27" "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" @@ -45,8 +45,9 @@ const unsigned char kInternalDisplay[] = "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53" "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe" "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45"; +constexpr size_t kInternalDisplayLength = arraysize(kInternalDisplay); -const unsigned char kOverscanDisplay[] = +constexpr unsigned char kOverscanDisplay[] = "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00" "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26" "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00" @@ -63,9 +64,10 @@ const unsigned char kOverscanDisplay[] = "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6"; +constexpr size_t kOverscanDisplayLength = arraysize(kOverscanDisplay); // The EDID info misdetecting overscan once. see crbug.com/226318 -const unsigned char kMisdetectedDisplay[] = +constexpr unsigned char kMisdetectedDisplay[] = "\x00\xff\xff\xff\xff\xff\xff\x00\x10\xac\x64\x40\x4c\x30\x30\x32" "\x0c\x15\x01\x03\x80\x40\x28\x78\xea\x8d\x85\xad\x4f\x35\xb1\x25" "\x0e\x50\x54\xa5\x4b\x00\x71\x4f\x81\x00\x81\x80\xd1\x00\xa9\x40" @@ -82,8 +84,9 @@ const unsigned char kMisdetectedDisplay[] = "\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\x81\x91\x21\x00\x00\x1e\x8c" "\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\x81\x91\x21\x00\x00" "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94"; +constexpr size_t kMisdetectedDisplayLength = arraysize(kMisdetectedDisplay); -const unsigned char kLP2565A[] = +constexpr unsigned char kLP2565A[] = "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01" "\x02\x12\x01\x03\x80\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26" "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00" @@ -92,8 +95,9 @@ const unsigned char kLP2565A[] = "\x5E\x11\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48" "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF" "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\xA4"; +constexpr size_t kLP2565ALength = arraysize(kLP2565A); -const unsigned char kLP2565B[] = +constexpr unsigned char kLP2565B[] = "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x26\x01\x01\x01\x01" "\x02\x12\x01\x03\x6E\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26" "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x71\x00\xA9\x00\xA9\x40\xA9\x4F" @@ -102,9 +106,10 @@ const unsigned char kLP2565B[] = "\x5E\x15\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48" "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF" "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\x45"; +constexpr size_t kLP2565BLength = arraysize(kLP2565B); // HP z32x monitor. -const unsigned char kHPz32x[] = +constexpr unsigned char kHPz32x[] = "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x32\x01\x01\x01\x01" "\x1B\x1B\x01\x04\xB5\x46\x27\x78\x3A\x8D\x15\xAC\x51\x32\xB8\x26" "\x0B\x50\x54\x21\x08\x00\xD1\xC0\xA9\xC0\x81\xC0\xD1\x00\xB3\x00" @@ -121,9 +126,10 @@ const unsigned char kHPz32x[] = "\x00\xA0\xA0\x40\x2E\x60\x20\x30\x63\x00\xB9\x88\x21\x00\x00\x1C" "\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20\x36\x00\xB9\x88\x21\x00" "\x00\x1A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3E"; +constexpr size_t kHPz32xLength = arraysize(kHPz32x); // Chromebook Samus internal display. -const unsigned char kSamus[] = +constexpr unsigned char kSamus[] = "\x00\xff\xff\xff\xff\xff\xff\x00\x30\xe4\x2e\x04\x00\x00\x00\x00" "\x00\x18\x01\x04\xa5\x1b\x12\x96\x02\x4f\xd5\xa2\x59\x52\x93\x26" "\x17\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" @@ -132,9 +138,10 @@ const unsigned char kSamus[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\x00\x4c" "\x47\x20\x44\x69\x73\x70\x6c\x61\x79\x0a\x20\x20\x00\x00\x00\xfe" "\x00\x4c\x50\x31\x32\x39\x51\x45\x32\x2d\x53\x50\x41\x31\x00\x6c"; +constexpr size_t kSamusLength = arraysize(kSamus); // Chromebook Eve internal display. -const unsigned char kEve[] = +constexpr unsigned char kEve[] = "\x00\xff\xff\xff\xff\xff\xff\x00\x4d\x10\x8a\x14\x00\x00\x00\x00" "\x16\x1b\x01\x04\xa5\x1a\x11\x78\x06\xde\x50\xa3\x54\x4c\x99\x26" "\x0f\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" @@ -143,13 +150,29 @@ const unsigned char kEve[] = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc" "\x00\x4c\x51\x31\x32\x33\x50\x31\x4a\x58\x33\x32\x0a\x20\x00\xb6"; +constexpr size_t kEveLength = arraysize(kEve); + +constexpr SkColorSpacePrimaries kNormalDisplayPrimaries = { + 0.6777f, 0.3086f, 0.2080f, 0.6923f, 0.1465f, 0.0546f, 0.3125f, 0.3291f}; +constexpr SkColorSpacePrimaries kInternalDisplayPrimaries = { + 0.5849f, 0.3603f, 0.3769f, 0.5654f, 0.1552f, 0.0996f, 0.3125f, 0.3291f}; +constexpr SkColorSpacePrimaries kOverscanDisplayPrimaries = { + 0.6396f, 0.3291f, 0.2978f, 0.5996f, 0.1494f, 0.0595f, 0.3144f, 0.3291f}; +constexpr SkColorSpacePrimaries kMisdetectedDisplayPrimaries = { + 0.6777f, 0.3086f, 0.2080f, 0.6923f, 0.1465f, 0.0546f, 0.3125f, 0.3291f}; +constexpr SkColorSpacePrimaries kLP2565APrimaries = { + 0.6396f, 0.3291f, 0.2978f, 0.6083f, 0.1494f, 0.0595f, 0.3144f, 0.3291f}; +constexpr SkColorSpacePrimaries kLP2565BPrimaries = { + 0.6396f, 0.3291f, 0.2978f, 0.6083f, 0.1494f, 0.0595f, 0.3144f, 0.3291f}; +constexpr SkColorSpacePrimaries kHPz32xPrimaries = { + 0.6738f, 0.3164f, 0.1962f, 0.7197f, 0.1484f, 0.0439f, 0.3144f, 0.3291f}; +constexpr SkColorSpacePrimaries kSamusPrimaries = { + 0.6337f, 0.3476f, 0.3212f, 0.5771f, 0.1513f, 0.0908f, 0.3144f, 0.3291f}; +constexpr SkColorSpacePrimaries kEvePrimaries = { + 0.6396f, 0.3291f, 0.2998f, 0.5996f, 0.1494f, 0.0595f, 0.3144f, 0.3281f}; -void Reset(gfx::Size* pixel, gfx::Size* size) { - pixel->SetSize(0, 0); - size->SetSize(0, 0); -} // Chromaticity primaries in EDID are specified with 10 bits precision. -const static float kPrimariesPrecision = 0.001f; +constexpr static float kPrimariesPrecision = 0.001f; ::testing::AssertionResult SkColorSpacePrimariesEquals( const char* lhs_expr, @@ -177,286 +200,96 @@ const static float kPrimariesPrecision = 0.001f; } // namespace -TEST(EDIDParserTest, ParseOverscanFlag) { - bool flag = false; - std::vector<uint8_t> edid( - kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay)); - EXPECT_FALSE(ParseOutputOverscanFlag(edid, &flag)); - - flag = false; - edid.assign(kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay)); - EXPECT_FALSE(ParseOutputOverscanFlag(edid, &flag)); - - flag = false; - edid.assign(kOverscanDisplay, kOverscanDisplay + charsize(kOverscanDisplay)); - EXPECT_TRUE(ParseOutputOverscanFlag(edid, &flag)); - EXPECT_TRUE(flag); - - flag = false; - edid.assign( - kMisdetectedDisplay, kMisdetectedDisplay + charsize(kMisdetectedDisplay)); - EXPECT_FALSE(ParseOutputOverscanFlag(edid, &flag)); - - flag = false; - // Copy |kOverscanDisplay| and set flags to false in it. The overscan flags - // are embedded at byte 150 in this specific example. Fix here too when the - // contents of kOverscanDisplay is altered. - edid.assign(kOverscanDisplay, kOverscanDisplay + charsize(kOverscanDisplay)); - edid[150] = '\0'; - EXPECT_TRUE(ParseOutputOverscanFlag(edid, &flag)); - EXPECT_FALSE(flag); -} - -TEST(EDIDParserTest, ParseBrokenOverscanData) { - // Do not fill valid data here because it anyway fails to parse the data. - std::vector<uint8_t> data; - bool flag = false; - EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag)); - data.assign(126, '\0'); - EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag)); - - // extending data because ParseOutputOverscanFlag() will access the data. - data.assign(128, '\0'); - // The number of CEA extensions is stored at byte 126. - data[126] = '\x01'; - EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag)); - - data.assign(150, '\0'); - data[126] = '\x01'; - EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag)); -} - -TEST(EDIDParserTest, ParseEDID) { - uint16_t manufacturer_id = 0; - uint16_t product_code = 0; - std::string human_readable_name; - std::vector<uint8_t> edid( - kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay)); - gfx::Size pixel; - gfx::Size size; - EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code, - &human_readable_name, &pixel, &size)); - EXPECT_EQ(0x22f0u, manufacturer_id); - EXPECT_EQ(0x6c28u, product_code); - EXPECT_EQ("HP ZR30w", human_readable_name); - EXPECT_EQ("2560x1600", pixel.ToString()); - EXPECT_EQ("641x400", size.ToString()); - - manufacturer_id = 0; - product_code = 0; - human_readable_name.clear(); - Reset(&pixel, &size); - edid.assign(kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay)); - - EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code, - nullptr, &pixel, &size)); - EXPECT_EQ(0x4ca3u, manufacturer_id); - EXPECT_EQ(0x4231u, product_code); - EXPECT_EQ("", human_readable_name); - EXPECT_EQ("1280x800", pixel.ToString()); - EXPECT_EQ("261x163", size.ToString()); - - // Internal display doesn't have name. - EXPECT_TRUE(ParseOutputDeviceData(edid, nullptr, nullptr, - &human_readable_name, &pixel, &size)); - EXPECT_TRUE(human_readable_name.empty()); - - manufacturer_id = 0; - product_code = 0; - human_readable_name.clear(); - Reset(&pixel, &size); - edid.assign(kOverscanDisplay, kOverscanDisplay + charsize(kOverscanDisplay)); - EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code, - &human_readable_name, &pixel, &size)); - EXPECT_EQ(0x4c2du, manufacturer_id); - EXPECT_EQ(0xfe08u, product_code); - EXPECT_EQ("SAMSUNG", human_readable_name); - EXPECT_EQ("1920x1080", pixel.ToString()); - EXPECT_EQ("160x90", size.ToString()); -} - -TEST(EDIDParserTest, ParseBrokenEDID) { - uint16_t manufacturer_id = 0; - uint16_t product_code = 0; - std::string human_readable_name; - std::vector<uint8_t> edid; - - gfx::Size dummy; - - // length == 0 - EXPECT_FALSE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code, - &human_readable_name, &dummy, &dummy)); - - // name is broken. Copying kNormalDisplay and substitute its name data by - // some control code. - edid.assign(kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay)); - - // display's name data is embedded in byte 95-107 in this specific example. - // Fix here too when the contents of kNormalDisplay is altered. - edid[97] = '\x1b'; - EXPECT_FALSE(ParseOutputDeviceData(edid, &manufacturer_id, nullptr, - &human_readable_name, &dummy, &dummy)); - - // If |human_readable_name| isn't specified, it skips parsing the name. - manufacturer_id = 0; - product_code = 0; - EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, &product_code, - nullptr, &dummy, &dummy)); - EXPECT_EQ(0x22f0u, manufacturer_id); - EXPECT_EQ(0x6c28u, product_code); -} - -TEST(EDIDParserTest, GetDisplayId) { - // EDID of kLP2565A and B are slightly different but actually the same device. - int64_t id1 = -1; - int64_t id2 = -1; - int64_t product_id1 = -1; - int64_t product_id2 = -1; - std::vector<uint8_t> edid(kLP2565A, kLP2565A + charsize(kLP2565A)); - EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id1, &product_id1)); - edid.assign(kLP2565B, kLP2565B + charsize(kLP2565B)); - EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id2, &product_id2)); - EXPECT_EQ(id1, id2); - // The product code in the two EDIDs varies. - EXPECT_NE(product_id1, product_id2); - EXPECT_EQ(0x22f07626, product_id1); - EXPECT_EQ(0x22f07526, product_id2); - EXPECT_NE(-1, id1); - EXPECT_NE(-1, product_id1); +struct TestParams { + uint16_t manufacturer_id; + uint16_t product_id; + std::string display_name; + gfx::Size active_pixel_size; + int32_t year_of_manufacture; + bool overscan_flag; + double gamma; + int bits_per_channel; + SkColorSpacePrimaries primaries; + + uint32_t product_code; + int64_t display_id_zero; + + std::string manufacturer_id_string; + std::string product_id_string; + + const unsigned char* edid_blob; + size_t edid_blob_length; +} kTestCases[] = { + {0x22f0u, 0x6c28u, "HP ZR30w", gfx::Size(2560, 1600), 2012, false, 2.2, 10, + kNormalDisplayPrimaries, 586181672, 9834734971736576, "HWP", "286C", + kNormalDisplay, kNormalDisplayLength}, + {0x4ca3u, 0x4231u, "", gfx::Size(1280, 800), 2011, false, 2.2, -1, + kInternalDisplayPrimaries, 1285767729, 21571318625337344, "SEC", "3142", + kInternalDisplay, kInternalDisplayLength}, + {0x4c2du, 0xfe08u, "SAMSUNG", gfx::Size(1920, 1080), 2011, true, 2.2, -1, + kOverscanDisplayPrimaries, 1278082568, 21442559853606400, "SAM", "08FE", + kOverscanDisplay, kOverscanDisplayLength}, + {0x10ACu, 0x6440u, "DELL U3011", gfx::Size(1920, 1200), 2011, false, 2.2, + -1, kMisdetectedDisplayPrimaries, 279733312, 4692848143772416, "DEL", + "4064", kMisdetectedDisplay, kMisdetectedDisplayLength}, + {0x22f0u, 0x7626u, "HP LP2465", gfx::Size(1920, 1200), 2008, false, 2.2, -1, + kLP2565APrimaries, 586184230, 9834630174887424, "HWP", "2676", kLP2565A, + kLP2565ALength}, + {0x22f0u, 0x7526u, "HP LP2465", gfx::Size(1920, 1200), 2008, false, 2.2, -1, + kLP2565BPrimaries, 586183974, 9834630174887424, "HWP", "2675", kLP2565B, + kLP2565BLength}, + {0x22f0u, 0x7532u, "HP Z32x", gfx::Size(3840, 2160), 2017, false, 2.2, 10, + kHPz32xPrimaries, 586183986, 9834799315992832, "HWP", "3275", kHPz32x, + kHPz32xLength}, + {0x30E4u, 0x2E04u, "", gfx::Size(2560, 1700), 2014, false, 2.5, 8, + kSamusPrimaries, 820260356, 13761487533244416, "LGD", "042E", kSamus, + kSamusLength}, + {0x4D10u, 0x8A14u, "LQ123P1JX32", gfx::Size(2400, 1600), 2017, false, 2.2, + 8, kEvePrimaries, 1292929556, 21692109949126656, "SHP", "148A", kEve, + kEveLength}, + + // Empty Edid, which is tantamount to error. + {0, 0, "", gfx::Size(0, 0), display::kInvalidYearOfManufacture, false, 0.0, + -1, SkColorSpacePrimaries(), 0, 0, "@@@", "0000", nullptr, 0u}, +}; + +class EDIDParserTest : public TestWithParam<TestParams> { + public: + EDIDParserTest() + : parser_(std::vector<uint8_t>( + GetParam().edid_blob, + GetParam().edid_blob + GetParam().edid_blob_length)) {} + + const EdidParser parser_; + + private: + DISALLOW_COPY_AND_ASSIGN(EDIDParserTest); +}; + +TEST_P(EDIDParserTest, ParseEdids) { + EXPECT_EQ(parser_.manufacturer_id(), GetParam().manufacturer_id); + EXPECT_EQ(parser_.product_id(), GetParam().product_id); + EXPECT_EQ(parser_.display_name(), GetParam().display_name); + EXPECT_EQ(parser_.active_pixel_size(), GetParam().active_pixel_size); + EXPECT_EQ(parser_.year_of_manufacture(), GetParam().year_of_manufacture); + EXPECT_EQ(parser_.has_overscan_flag(), GetParam().overscan_flag); + if (parser_.has_overscan_flag()) + EXPECT_EQ(parser_.overscan_flag(), GetParam().overscan_flag); + EXPECT_DOUBLE_EQ(parser_.gamma(), GetParam().gamma); + EXPECT_EQ(parser_.bits_per_channel(), GetParam().bits_per_channel); + EXPECT_PRED_FORMAT2(SkColorSpacePrimariesEquals, parser_.primaries(), + GetParam().primaries); + + EXPECT_EQ(parser_.GetProductCode(), GetParam().product_code); + EXPECT_EQ(parser_.GetDisplayId(0 /* product_index */), + GetParam().display_id_zero); + + EXPECT_EQ(EdidParser::ManufacturerIdToString(parser_.manufacturer_id()), + GetParam().manufacturer_id_string); + EXPECT_EQ(EdidParser::ProductIdToString(parser_.product_id()), + GetParam().product_id_string); } -TEST(EDIDParserTest, GetDisplayIdFromInternal) { - int64_t id = -1; - int64_t product_id = -1; - std::vector<uint8_t> edid( - kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay)); - EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id, &product_id)); - EXPECT_NE(-1, id); - EXPECT_NE(-1, product_id); -} - -TEST(EDIDParserTest, GetDisplayIdFailure) { - int64_t id = -1; - int64_t product_id = -1; - std::vector<uint8_t> edid; - EXPECT_FALSE(GetDisplayIdFromEDID(edid, 0, &id, &product_id)); - EXPECT_EQ(-1, id); - EXPECT_EQ(-1, product_id); -} - -TEST(EDIDParserTest, ParseChromaticityCoordinates) { - const std::vector<uint8_t> edid_normal_display( - kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay)); - SkColorSpacePrimaries primaries_normal_display; - EXPECT_TRUE(ParseChromaticityCoordinates(edid_normal_display, - &primaries_normal_display)); - // Color primaries associated to |kNormalDisplay|; they don't correspond to - // any standard color gamut. - const SkColorSpacePrimaries kNormalDisplayPrimaries = { - 0.6777f, 0.3086f, 0.2080f, 0.6923f, 0.1465f, 0.0546f, 0.3125f, 0.3291f}; - EXPECT_PRED_FORMAT2(SkColorSpacePrimariesEquals, primaries_normal_display, - kNormalDisplayPrimaries); - - const std::vector<uint8_t> edid_internal_display( - kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay)); - SkColorSpacePrimaries primaries_internal_display; - EXPECT_TRUE(ParseChromaticityCoordinates(edid_internal_display, - &primaries_internal_display)); - // Color primaries associated to |kInternalDisplay|; they don't correspond to - // any standard color gamut. - const SkColorSpacePrimaries kInternalDisplayPrimaries = { - 0.5849f, 0.3603f, 0.3769f, 0.5654f, 0.1552f, 0.0996f, 0.3125f, 0.3291f}; - EXPECT_PRED_FORMAT2(SkColorSpacePrimariesEquals, primaries_internal_display, - kInternalDisplayPrimaries); - - const std::vector<uint8_t> edid_hpz32x(kHPz32x, kHPz32x + charsize(kHPz32x)); - SkColorSpacePrimaries primaries_hpz32x; - EXPECT_TRUE(ParseChromaticityCoordinates(edid_hpz32x, &primaries_hpz32x)); - // Color primaries associated to |kHPz32x|; they don't correspond to - // any standard color gamut. - const SkColorSpacePrimaries kHPz32xPrimaries = { - 0.6738f, 0.3164f, 0.1962f, 0.7197f, 0.1484f, 0.0439f, 0.3144f, 0.3291f}; - EXPECT_PRED_FORMAT2(SkColorSpacePrimariesEquals, primaries_hpz32x, - kHPz32xPrimaries); - - const std::vector<uint8_t> edid_samus(kSamus, kSamus + charsize(kSamus)); - SkColorSpacePrimaries primaries_samus; - EXPECT_TRUE(ParseChromaticityCoordinates(edid_samus, &primaries_samus)); - // Color primaries associated to Samus Chromebook, very similar to BT.709. - const SkColorSpacePrimaries kSamusPrimaries = { - 0.6337f, 0.3476f, 0.3212f, 0.5771f, 0.1513f, 0.0908f, 0.3144f, 0.3291f}; - EXPECT_PRED_FORMAT2(SkColorSpacePrimariesEquals, primaries_samus, - kSamusPrimaries); - - const std::vector<uint8_t> edid_eve(kEve, kEve + charsize(kEve)); - SkColorSpacePrimaries primaries_eve; - EXPECT_TRUE(ParseChromaticityCoordinates(edid_eve, &primaries_eve)); - // Color primaries associated to Eve Chromebook, very similar to BT.709. - const SkColorSpacePrimaries kEvePrimaries = { - 0.6396f, 0.3291f, 0.2998f, 0.5996f, 0.1494f, 0.0595f, 0.3144f, 0.3281f}; - EXPECT_PRED_FORMAT2(SkColorSpacePrimariesEquals, primaries_eve, - kEvePrimaries); -} - -TEST(EDIDParserTest, ParseGammaValue) { - const std::vector<uint8_t> edid_normal_display( - kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay)); - double edid_normal_display_gamma = 0.0; - EXPECT_TRUE(ParseGammaValue(edid_normal_display, &edid_normal_display_gamma)); - EXPECT_DOUBLE_EQ(2.2, edid_normal_display_gamma); - - const std::vector<uint8_t> edid_internal_display( - kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay)); - double edid_internal_display_gamma = 0.0; - EXPECT_TRUE( - ParseGammaValue(edid_internal_display, &edid_internal_display_gamma)); - EXPECT_DOUBLE_EQ(2.2, edid_internal_display_gamma); - - const std::vector<uint8_t> edid_hpz32x(kHPz32x, kHPz32x + charsize(kHPz32x)); - double edid_hpz32x_gamma = 0.0; - EXPECT_TRUE(ParseGammaValue(edid_hpz32x, &edid_hpz32x_gamma)); - EXPECT_DOUBLE_EQ(2.2, edid_hpz32x_gamma); - - const std::vector<uint8_t> edid_samus(kSamus, kSamus + charsize(kSamus)); - double edid_samus_gamma = 0.0; - EXPECT_TRUE(ParseGammaValue(edid_samus, &edid_samus_gamma)); - EXPECT_DOUBLE_EQ(2.5, edid_samus_gamma); - - const std::vector<uint8_t> edid_eve(kEve, kEve + charsize(kEve)); - double edid_eve_gamma = 0.0; - EXPECT_TRUE(ParseGammaValue(edid_eve, &edid_eve_gamma)); - EXPECT_DOUBLE_EQ(2.2, edid_eve_gamma); -} - -TEST(EDIDParserTest, ParseBitsPerChannel) { - const std::vector<uint8_t> edid_normal_display( - kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay)); - int edid_normal_display_bits_per_channel = 0; - EXPECT_TRUE(ParseBitsPerChannel(edid_normal_display, - &edid_normal_display_bits_per_channel)); - EXPECT_EQ(10, edid_normal_display_bits_per_channel); - - const std::vector<uint8_t> edid_internal_display( - kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay)); - int edid_internal_display_bits_per_channel = 0; - // |kInternalDisplay| doesn't have bits per channel information. - EXPECT_FALSE(ParseBitsPerChannel(edid_internal_display, - &edid_internal_display_bits_per_channel)); - - const std::vector<uint8_t> edid_hpz32x(kHPz32x, kHPz32x + charsize(kHPz32x)); - int edid_hpz32x_bits_per_channel = 0; - EXPECT_TRUE(ParseBitsPerChannel(edid_hpz32x, &edid_hpz32x_bits_per_channel)); - EXPECT_EQ(10, edid_hpz32x_bits_per_channel); - - const std::vector<uint8_t> edid_samus(kSamus, kSamus + charsize(kSamus)); - int edid_samus_bits_per_channel = 0; - EXPECT_TRUE(ParseBitsPerChannel(edid_samus, &edid_samus_bits_per_channel)); - EXPECT_EQ(8, edid_samus_bits_per_channel); - - const std::vector<uint8_t> edid_eve(kEve, kEve + charsize(kEve)); - int edid_eve_bits_per_channel = 0; - EXPECT_TRUE(ParseBitsPerChannel(edid_eve, &edid_eve_bits_per_channel)); - EXPECT_EQ(8, edid_eve_bits_per_channel); -} +INSTANTIATE_TEST_CASE_P(, EDIDParserTest, ValuesIn(kTestCases)); } // namespace display diff --git a/chromium/ui/display/util/fuzz_corpus/eve b/chromium/ui/display/util/fuzz_corpus/eve Binary files differnew file mode 100644 index 00000000000..73cc2551931 --- /dev/null +++ b/chromium/ui/display/util/fuzz_corpus/eve diff --git a/chromium/ui/display/util/fuzz_corpus/hpz32x b/chromium/ui/display/util/fuzz_corpus/hpz32x Binary files differnew file mode 100644 index 00000000000..b7ee75d764f --- /dev/null +++ b/chromium/ui/display/util/fuzz_corpus/hpz32x diff --git a/chromium/ui/display/util/x11/edid_parser_x11.cc b/chromium/ui/display/util/x11/edid_parser_x11.cc index 06edcf88c80..57baee9200c 100644 --- a/chromium/ui/display/util/x11/edid_parser_x11.cc +++ b/chromium/ui/display/util/x11/edid_parser_x11.cc @@ -73,33 +73,18 @@ bool GetEDIDProperty(XID output, std::vector<uint8_t>* edid) { } // namespace -EDIDParserX11::EDIDParserX11(XID output_id) - : output_id_(output_id) { +EDIDParserX11::EDIDParserX11(XID output_id) : output_id_(output_id) { GetEDIDProperty(output_id_, &edid_); } -EDIDParserX11::~EDIDParserX11() { -} +EDIDParserX11::~EDIDParserX11() {} bool EDIDParserX11::GetDisplayId(uint8_t index, int64_t* out_display_id) const { if (edid_.empty()) return false; - return GetDisplayIdFromEDID(edid_, index, out_display_id, nullptr); -} - -std::string EDIDParserX11::GetDisplayName() const { - std::string display_name; - ParseOutputDeviceData(edid_, nullptr, nullptr, &display_name, nullptr, - nullptr); - return display_name; -} - -bool EDIDParserX11::GetOutputOverscanFlag(bool* out_flag) const { - if (edid_.empty()) - return false; - - return ParseOutputOverscanFlag(edid_, out_flag); + *out_display_id = EdidParser(edid_).GetDisplayId(output_id_); + return true; } } // namespace display diff --git a/chromium/ui/display/util/x11/edid_parser_x11.h b/chromium/ui/display/util/x11/edid_parser_x11.h index 0c49015b99f..383d828916f 100644 --- a/chromium/ui/display/util/x11/edid_parser_x11.h +++ b/chromium/ui/display/util/x11/edid_parser_x11.h @@ -13,6 +13,7 @@ #include "base/macros.h" #include "ui/display/types/display_constants.h" #include "ui/display/util/display_util_export.h" +#include "ui/display/util/edid_parser.h" typedef unsigned long XID; typedef XID RROutput; @@ -31,22 +32,11 @@ class DISPLAY_UTIL_EXPORT EDIDParserX11 { // Returns true if successful, false otherwise. bool GetDisplayId(uint8_t index, int64_t* out_display_id) const; - // Generate the human readable string from EDID obtained from |output|. - // Returns an empty string upon error. - std::string GetDisplayName() const; - - // Gets the overscan flag and stores to |out_flag|. Returns true if the flag - // is found. Otherwise returns false and doesn't touch |out_flag|. The output - // will produce overscan if |out_flag| is set to true, but the output may - // still produce overscan even though it returns true and |out_flag| is set to - // false. - bool GetOutputOverscanFlag(bool* out_flag) const; - XID output_id() const { return output_id_; } const std::vector<uint8_t>& edid() const { return edid_; } private: - XID output_id_; + const XID output_id_; // This will be an empty vector upon failure to get the EDID from the // |output_id_|. diff --git a/chromium/ui/events/BUILD.gn b/chromium/ui/events/BUILD.gn index 96adf2efb72..d5bb88155d6 100644 --- a/chromium/ui/events/BUILD.gn +++ b/chromium/ui/events/BUILD.gn @@ -41,6 +41,12 @@ source_set("event_constants") { ] } +source_set("platform_event") { + sources = [ + "platform_event.h", + ] +} + component("events_base") { sources = [ "android/scroller.cc", @@ -80,6 +86,7 @@ component("events_base") { public_deps = [ ":dom_keycode_converter", ":event_constants", + ":platform_event", "//base", "//ui/events/platform", "//ui/gfx/geometry", @@ -352,13 +359,14 @@ static_library("test_support") { libs = [ "Carbon.framework" ] } - if (use_x11) { + if (use_x11 || ozone_platform_x11) { sources += [ "test/events_test_utils_x11.cc", "test/events_test_utils_x11.h", ] deps += [ "//ui/events/devices/x11", + "//ui/events/keycodes:x11", "//ui/gfx/x", ] } @@ -376,6 +384,8 @@ if (!is_ios) { "blink/fling_booster_unittest.cc", "blink/input_handler_proxy_unittest.cc", "blink/input_scroll_elasticity_controller_unittest.cc", + "blink/snap_fling_controller_unittest.cc", + "blink/snap_fling_curve_unittest.cc", "blink/web_input_event_traits_unittest.cc", "blink/web_input_event_unittest.cc", "cocoa/events_mac_unittest.mm", @@ -384,6 +394,7 @@ if (!is_ios) { "event_dispatcher_unittest.cc", "event_processor_unittest.cc", "event_rewriter_unittest.cc", + "event_target_unittest.cc", "event_unittest.cc", "fraction_of_time_without_user_input_recorder_unittest.cc", "gesture_detection/bitset_32_unittest.cc", @@ -421,7 +432,7 @@ if (!is_ios) { "//skia", "//testing/gmock", "//testing/gtest", - "//third_party/WebKit/public:blink_headers", + "//third_party/blink/public:blink_headers", "//ui/base:test_support", "//ui/display", "//ui/events/blink", diff --git a/chromium/ui/events/base_event_utils.cc b/chromium/ui/events/base_event_utils.cc index 20bbed9d18f..21dc226e9ff 100644 --- a/chromium/ui/events/base_event_utils.cc +++ b/chromium/ui/events/base_event_utils.cc @@ -47,7 +47,7 @@ bool IsSystemKeyModifier(int flags) { (EF_ALTGR_DOWN & flags) == 0; } -base::LazyInstance<base::TickClock*>::Leaky g_tick_clock = +base::LazyInstance<const base::TickClock*>::Leaky g_tick_clock = LAZY_INSTANCE_INITIALIZER; base::TimeTicks EventTimeForNow() { @@ -55,7 +55,7 @@ base::TimeTicks EventTimeForNow() { : base::TimeTicks::Now(); } -void SetEventTickClockForTesting(base::TickClock* tick_clock) { +void SetEventTickClockForTesting(const base::TickClock* tick_clock) { g_tick_clock.Get() = tick_clock; } diff --git a/chromium/ui/events/base_event_utils.h b/chromium/ui/events/base_event_utils.h index d030fd15b8d..c75509fd171 100644 --- a/chromium/ui/events/base_event_utils.h +++ b/chromium/ui/events/base_event_utils.h @@ -30,7 +30,7 @@ EVENTS_BASE_EXPORT base::TimeTicks EventTimeForNow(); // Overrides the clock used by EventTimeForNow for testing. // This doesn't take the ownership of the clock. EVENTS_BASE_EXPORT void SetEventTickClockForTesting( - base::TickClock* tick_clock); + const base::TickClock* tick_clock); // Converts an event timestamp ticks to seconds (floating point representation). // WARNING: This should only be used when interfacing with platform code that diff --git a/chromium/ui/events/blink/BUILD.gn b/chromium/ui/events/blink/BUILD.gn index f22697697d5..0a93dd31201 100644 --- a/chromium/ui/events/blink/BUILD.gn +++ b/chromium/ui/events/blink/BUILD.gn @@ -24,6 +24,10 @@ jumbo_source_set("blink") { "input_handler_proxy_client.h", "input_scroll_elasticity_controller.cc", "input_scroll_elasticity_controller.h", + "snap_fling_controller.cc", + "snap_fling_controller.h", + "snap_fling_curve.cc", + "snap_fling_curve.h", "synchronous_input_handler_proxy.h", "web_input_event.cc", "web_input_event.h", @@ -33,7 +37,7 @@ jumbo_source_set("blink") { deps = [ "//cc:cc", - "//third_party/WebKit/public:blink_headers", + "//third_party/blink/public:blink_headers", "//ui/events", "//ui/events:dom_keycode_converter", "//ui/events:events_base", diff --git a/chromium/ui/events/blink/DEPS b/chromium/ui/events/blink/DEPS index b1228dde788..6df7edf0a39 100644 --- a/chromium/ui/events/blink/DEPS +++ b/chromium/ui/events/blink/DEPS @@ -5,17 +5,17 @@ include_rules = [ "+cc/input/scroll_elasticity_helper.h", "+cc/trees/swap_promise_monitor.h", - "+third_party/WebKit/public/platform/WebGestureCurve.h", - "+third_party/WebKit/public/platform/WebGestureCurveTarget.h", - "+third_party/WebKit/public/platform/WebFloatPoint.h", - "+third_party/WebKit/public/platform/WebFloatSize.h", - "+third_party/WebKit/public/platform/WebGestureEvent.h", - "+third_party/WebKit/public/platform/WebInputEvent.h", - "+third_party/WebKit/public/platform/WebKeyboardEvent.h", - "+third_party/WebKit/public/platform/WebMouseWheelEvent.h", - "+third_party/WebKit/public/platform/WebPoint.h", - "+third_party/WebKit/public/platform/WebTouchEvent.h", - "+third_party/WebKit/public/web/WebActiveFlingParameters.h", + "+third_party/blink/public/platform/web_gesture_curve.h", + "+third_party/blink/public/platform/web_gesture_curve_target.h", + "+third_party/blink/public/platform/web_float_point.h", + "+third_party/blink/public/platform/web_float_size.h", + "+third_party/blink/public/platform/web_gesture_event.h", + "+third_party/blink/public/platform/web_input_event.h", + "+third_party/blink/public/platform/web_keyboard_event.h", + "+third_party/blink/public/platform/web_mouse_wheel_event.h", + "+third_party/blink/public/platform/web_point.h", + "+third_party/blink/public/platform/web_touch_event.h", + "+third_party/blink/public/web/web_active_fling_parameters.h", "+ui/display/win", "+ui/gfx", diff --git a/chromium/ui/events/blink/blink_event_util.cc b/chromium/ui/events/blink/blink_event_util.cc index c8d058216b2..c0b30b60a43 100644 --- a/chromium/ui/events/blink/blink_event_util.cc +++ b/chromium/ui/events/blink/blink_event_util.cc @@ -13,8 +13,8 @@ #include "base/time/time.h" #include "build/build_config.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" #include "ui/events/android/gesture_event_android.h" #include "ui/events/android/gesture_event_type.h" #include "ui/events/base_event_utils.h" @@ -416,7 +416,7 @@ bool CanCoalesce(const WebGestureEvent& event_to_coalesce, const WebGestureEvent& event) { if (event.GetType() != event_to_coalesce.GetType() || event.resending_plugin_id != event_to_coalesce.resending_plugin_id || - event.source_device != event_to_coalesce.source_device || + event.SourceDevice() != event_to_coalesce.SourceDevice() || event.GetModifiers() != event_to_coalesce.GetModifiers()) return false; @@ -426,7 +426,7 @@ bool CanCoalesce(const WebGestureEvent& event_to_coalesce, // GesturePinchUpdate scales can be combined only if they share a focal point, // e.g., with double-tap drag zoom. if (event.GetType() == WebInputEvent::kGesturePinchUpdate && - event.x == event_to_coalesce.x && event.y == event_to_coalesce.y) + event.PositionInWidget() == event_to_coalesce.PositionInWidget()) return true; return false; @@ -462,9 +462,11 @@ gfx::Transform GetTransformForEvent(const WebGestureEvent& gesture_event) { gesture_event.data.scroll_update.delta_y); } else if (gesture_event.GetType() == WebInputEvent::kGesturePinchUpdate) { float scale = gesture_event.data.pinch_update.scale; - gesture_transform.Translate(-gesture_event.x, -gesture_event.y); + gesture_transform.Translate(-gesture_event.PositionInWidget().x, + -gesture_event.PositionInWidget().y); gesture_transform.Scale(scale, scale); - gesture_transform.Translate(gesture_event.x, gesture_event.y); + gesture_transform.Translate(gesture_event.PositionInWidget().x, + gesture_event.PositionInWidget().y); } else { NOTREACHED() << "Invalid event type for transform retrieval: " << WebInputEvent::GetName(gesture_event.GetType()); @@ -545,7 +547,7 @@ bool IsCompatibleScrollorPinch(const WebGestureEvent& new_event, return (event_in_queue.GetType() == WebInputEvent::kGestureScrollUpdate || event_in_queue.GetType() == WebInputEvent::kGesturePinchUpdate) && event_in_queue.GetModifiers() == new_event.GetModifiers() && - event_in_queue.source_device == new_event.source_device; + event_in_queue.SourceDevice() == new_event.SourceDevice(); } std::pair<WebGestureEvent, WebGestureEvent> CoalesceScrollAndPinch( @@ -559,20 +561,17 @@ std::pair<WebGestureEvent, WebGestureEvent> CoalesceScrollAndPinch( DCHECK(!second_last_event || IsCompatibleScrollorPinch(new_event, *second_last_event)); - WebGestureEvent scroll_event(WebInputEvent::kGestureScrollUpdate, - new_event.GetModifiers(), - new_event.TimeStampSeconds()); + WebGestureEvent scroll_event( + WebInputEvent::kGestureScrollUpdate, new_event.GetModifiers(), + new_event.TimeStampSeconds(), new_event.SourceDevice()); WebGestureEvent pinch_event; - scroll_event.source_device = new_event.source_device; scroll_event.primary_pointer_type = new_event.primary_pointer_type; pinch_event = scroll_event; pinch_event.SetType(WebInputEvent::kGesturePinchUpdate); - pinch_event.x = new_event.GetType() == WebInputEvent::kGesturePinchUpdate - ? new_event.x - : last_event.x; - pinch_event.y = new_event.GetType() == WebInputEvent::kGesturePinchUpdate - ? new_event.y - : last_event.y; + pinch_event.SetPositionInWidget(new_event.GetType() == + WebInputEvent::kGesturePinchUpdate + ? new_event.PositionInWidget() + : last_event.PositionInWidget()); gfx::Transform combined_scroll_pinch = GetTransformForEvent(last_event); if (second_last_event) { @@ -588,11 +587,13 @@ std::pair<WebGestureEvent, WebGestureEvent> CoalesceScrollAndPinch( float combined_scroll_pinch_y = SkMScalarToFloat(combined_scroll_pinch.matrix().get(1, 3)); scroll_event.data.scroll_update.delta_x = - (combined_scroll_pinch_x + pinch_event.x) / combined_scale - - pinch_event.x; + (combined_scroll_pinch_x + pinch_event.PositionInWidget().x) / + combined_scale - + pinch_event.PositionInWidget().x; scroll_event.data.scroll_update.delta_y = - (combined_scroll_pinch_y + pinch_event.y) / combined_scale - - pinch_event.y; + (combined_scroll_pinch_y + pinch_event.PositionInWidget().y) / + combined_scale - + pinch_event.PositionInWidget().y; pinch_event.data.pinch_update.scale = combined_scale; return std::make_pair(scroll_event, pinch_event); @@ -675,26 +676,24 @@ WebGestureEvent CreateWebGestureEvent(const GestureEventDetails& details, const gfx::PointF& raw_location, int flags, uint32_t unique_touch_event_id) { - WebGestureEvent gesture(WebInputEvent::kUndefined, - EventFlagsToWebEventModifiers(flags), - ui::EventTimeStampToSeconds(timestamp)); - gesture.x = gfx::ToFlooredInt(location.x()); - gesture.y = gfx::ToFlooredInt(location.y()); - gesture.global_x = gfx::ToFlooredInt(raw_location.x()); - gesture.global_y = gfx::ToFlooredInt(raw_location.y()); - + blink::WebGestureDevice source_device = blink::kWebGestureDeviceUninitialized; switch (details.device_type()) { case GestureDeviceType::DEVICE_TOUCHSCREEN: - gesture.source_device = blink::kWebGestureDeviceTouchscreen; + source_device = blink::kWebGestureDeviceTouchscreen; break; case GestureDeviceType::DEVICE_TOUCHPAD: - gesture.source_device = blink::kWebGestureDeviceTouchpad; + source_device = blink::kWebGestureDeviceTouchpad; break; case GestureDeviceType::DEVICE_UNKNOWN: NOTREACHED() << "Unknown device type is not allowed"; - gesture.source_device = blink::kWebGestureDeviceUninitialized; break; } + WebGestureEvent gesture( + WebInputEvent::kUndefined, EventFlagsToWebEventModifiers(flags), + ui::EventTimeStampToSeconds(timestamp), source_device); + + gesture.SetPositionInWidget(location); + gesture.SetPositionInScreen(raw_location); gesture.is_source_touch_event_set_non_blocking = details.is_source_touch_event_set_non_blocking(); @@ -866,10 +865,9 @@ std::unique_ptr<blink::WebInputEvent> TranslateAndScaleWebInputEvent( blink::WebGestureEvent* gesture_event = new blink::WebGestureEvent; scaled_event.reset(gesture_event); *gesture_event = static_cast<const blink::WebGestureEvent&>(event); - gesture_event->x += delta.x(); - gesture_event->y += delta.y(); - gesture_event->x *= scale; - gesture_event->y *= scale; + gesture_event->SetPositionInWidget(blink::WebFloatPoint( + (gesture_event->PositionInWidget().x + delta.x()) * scale, + (gesture_event->PositionInWidget().y + delta.y()) * scale)); switch (gesture_event->GetType()) { case blink::WebInputEvent::kGestureScrollUpdate: if (gesture_event->data.scroll_update.delta_units != @@ -1179,12 +1177,21 @@ std::unique_ptr<WebGestureEvent> CreateWebGestureEventFromGestureEventAndroid( case GESTURE_EVENT_TYPE_SCROLL_START: event_type = WebInputEvent::kGestureScrollBegin; break; + case GESTURE_EVENT_TYPE_SCROLL_BY: + event_type = WebInputEvent::kGestureScrollUpdate; + break; + case GESTURE_EVENT_TYPE_SCROLL_END: + event_type = WebInputEvent::kGestureScrollEnd; + break; case GESTURE_EVENT_TYPE_FLING_START: event_type = WebInputEvent::kGestureFlingStart; break; case GESTURE_EVENT_TYPE_FLING_CANCEL: event_type = WebInputEvent::kGestureFlingCancel; break; + case GESTURE_EVENT_TYPE_DOUBLE_TAP: + event_type = WebInputEvent::kGestureDoubleTap; + break; default: NOTREACHED() << "Unknown gesture event type"; return std::make_unique<WebGestureEvent>(); @@ -1194,19 +1201,20 @@ std::unique_ptr<WebGestureEvent> CreateWebGestureEventFromGestureEventAndroid( // NOTE: Source gesture events are synthetic ones that simulate // gesture from keyboard (zoom in/out) for now. Should populate Blink // event's fields better when extended to handle more cases. - web_event->x = event.location().x(); - web_event->y = event.location().y(); - web_event->global_x = event.screen_location().x(); - web_event->global_x = event.screen_location().y(); - web_event->source_device = blink::kWebGestureDeviceTouchscreen; + web_event->SetPositionInWidget(event.location()); + web_event->SetPositionInScreen(event.screen_location()); + web_event->SetSourceDevice(blink::kWebGestureDeviceTouchscreen); if (event.synthetic_scroll()) - web_event->source_device = blink::kWebGestureDeviceSyntheticAutoscroll; + web_event->SetSourceDevice(blink::kWebGestureDeviceSyntheticAutoscroll); if (event_type == WebInputEvent::kGesturePinchUpdate) { web_event->data.pinch_update.scale = event.scale(); } else if (event_type == WebInputEvent::kGestureScrollBegin) { web_event->data.scroll_begin.delta_x_hint = event.delta_x(); web_event->data.scroll_begin.delta_y_hint = event.delta_y(); web_event->data.scroll_begin.target_viewport = event.target_viewport(); + } else if (event_type == WebInputEvent::kGestureScrollUpdate) { + web_event->data.scroll_update.delta_x = event.delta_x(); + web_event->data.scroll_update.delta_y = event.delta_y(); } else if (event_type == WebInputEvent::kGestureFlingStart) { web_event->data.fling_start.velocity_x = event.velocity_x(); web_event->data.fling_start.velocity_y = event.velocity_y(); @@ -1215,6 +1223,11 @@ std::unique_ptr<WebGestureEvent> CreateWebGestureEventFromGestureEventAndroid( web_event->data.fling_cancel.prevent_boosting = true; if (event.synthetic_scroll()) web_event->data.fling_cancel.target_viewport = true; + } else if (event_type == WebInputEvent::kGestureDoubleTap) { + // Set the tap count to 1 even for DoubleTap, in order to be consistent with + // double tap behavior on a mobile viewport. See https://crbug.com/234986 + // for context. + web_event->data.tap.tap_count = 1; } return web_event; diff --git a/chromium/ui/events/blink/blink_event_util.h b/chromium/ui/events/blink/blink_event_util.h index 24b85de5204..6678fcfb481 100644 --- a/chromium/ui/events/blink/blink_event_util.h +++ b/chromium/ui/events/blink/blink_event_util.h @@ -8,9 +8,9 @@ #include <memory> #include "build/build_config.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebTouchEvent.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_touch_event.h" #include "ui/events/event_constants.h" #include "ui/events/gesture_detection/motion_event.h" diff --git a/chromium/ui/events/blink/blink_event_util_unittest.cc b/chromium/ui/events/blink/blink_event_util_unittest.cc index b8cb09a9f16..99c87370eed 100644 --- a/chromium/ui/events/blink/blink_event_util_unittest.cc +++ b/chromium/ui/events/blink/blink_event_util_unittest.cc @@ -6,9 +6,9 @@ #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.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_mouse_wheel_event.h" #include "ui/events/gesture_event_details.h" namespace ui { diff --git a/chromium/ui/events/blink/compositor_thread_event_queue.cc b/chromium/ui/events/blink/compositor_thread_event_queue.cc index 63c2386a045..d2796793b49 100644 --- a/chromium/ui/events/blink/compositor_thread_event_queue.cc +++ b/chromium/ui/events/blink/compositor_thread_event_queue.cc @@ -4,7 +4,6 @@ #include "ui/events/blink/compositor_thread_event_queue.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/blink/web_input_event_traits.h" diff --git a/chromium/ui/events/blink/event_with_callback.cc b/chromium/ui/events/blink/event_with_callback.cc index a3e0b210be7..1e23768b0d3 100644 --- a/chromium/ui/events/blink/event_with_callback.cc +++ b/chromium/ui/events/blink/event_with_callback.cc @@ -4,7 +4,6 @@ #include "ui/events/blink/event_with_callback.h" -#include "base/memory/ptr_util.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/blink/did_overscroll_params.h" #include "ui/events/blink/web_input_event_traits.h" diff --git a/chromium/ui/events/blink/fling_booster.cc b/chromium/ui/events/blink/fling_booster.cc index 233d7204c4d..c1f621c3837 100644 --- a/chromium/ui/events/blink/fling_booster.cc +++ b/chromium/ui/events/blink/fling_booster.cc @@ -59,7 +59,7 @@ bool FlingBooster::FilterGestureEventForFlingBoosting( return false; // Gestures from a different source should immediately interrupt the fling. - if (gesture_event.source_device != source_device_) { + if (gesture_event.SourceDevice() != source_device_) { *out_cancel_current_fling = true; return false; } @@ -100,7 +100,7 @@ bool FlingBooster::FilterGestureEventForFlingBoosting( return true; case WebInputEvent::kGestureFlingStart: { - DCHECK_EQ(source_device_, gesture_event.source_device); + DCHECK_EQ(source_device_, gesture_event.SourceDevice()); gfx::Vector2dF new_fling_velocity( gesture_event.data.fling_start.velocity_x, gesture_event.data.fling_start.velocity_y); diff --git a/chromium/ui/events/blink/fling_booster.h b/chromium/ui/events/blink/fling_booster.h index 50e5c2b41f8..f1bbf959971 100644 --- a/chromium/ui/events/blink/fling_booster.h +++ b/chromium/ui/events/blink/fling_booster.h @@ -5,7 +5,7 @@ #ifndef UI_EVENTS_BLINK_FLING_BOOSTER_H_ #define UI_EVENTS_BLINK_FLING_BOOSTER_H_ -#include "third_party/WebKit/public/platform/WebGestureEvent.h" +#include "third_party/blink/public/platform/web_gesture_event.h" namespace ui { diff --git a/chromium/ui/events/blink/fling_booster_unittest.cc b/chromium/ui/events/blink/fling_booster_unittest.cc index 075363552fb..9a70b41234a 100644 --- a/chromium/ui/events/blink/fling_booster_unittest.cc +++ b/chromium/ui/events/blink/fling_booster_unittest.cc @@ -21,7 +21,7 @@ namespace test { class FlingBoosterTest : public testing::Test { public: FlingBoosterTest() : delta_time_(base::TimeDelta::FromMilliseconds(10)) { - gesture_scroll_event_.source_device = blink::kWebGestureDeviceTouchscreen; + gesture_scroll_event_.SetSourceDevice(blink::kWebGestureDeviceTouchscreen); } WebGestureEvent CreateFlingStart(base::TimeTicks timestamp, @@ -29,8 +29,8 @@ class FlingBoosterTest : public testing::Test { const gfx::Vector2dF& velocity, int modifiers) { WebGestureEvent fling_start(WebInputEvent::kGestureFlingStart, modifiers, - EventTimeStampToSeconds(timestamp)); - fling_start.source_device = source_device; + EventTimeStampToSeconds(timestamp), + source_device); fling_start.data.fling_start.velocity_x = velocity.x(); fling_start.data.fling_start.velocity_y = velocity.y(); return fling_start; @@ -39,8 +39,8 @@ class FlingBoosterTest : public testing::Test { WebGestureEvent CreateFlingCancel(base::TimeTicks timestamp, WebGestureDevice source_device) { WebGestureEvent fling_cancel(WebInputEvent::kGestureFlingCancel, 0, - EventTimeStampToSeconds(timestamp)); - fling_cancel.source_device = source_device; + EventTimeStampToSeconds(timestamp), + source_device); return fling_cancel; } diff --git a/chromium/ui/events/blink/input_handler_proxy.cc b/chromium/ui/events/blink/input_handler_proxy.cc index 6d77910839f..0ae50266b88 100644 --- a/chromium/ui/events/blink/input_handler_proxy.cc +++ b/chromium/ui/events/blink/input_handler_proxy.cc @@ -12,16 +12,15 @@ #include "base/command_line.h" #include "base/location.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_tick_clock.h" #include "base/trace_event/trace_event.h" #include "cc/input/main_thread_scrolling_reason.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" -#include "third_party/WebKit/public/platform/WebTouchEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" +#include "third_party/blink/public/platform/web_touch_event.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/blink/compositor_thread_event_queue.h" #include "ui/events/blink/did_overscroll_params.h" @@ -78,8 +77,8 @@ cc::ScrollState CreateScrollStateForGesture(const WebGestureEvent& event) { cc::ScrollStateData scroll_state_data; switch (event.GetType()) { case WebInputEvent::kGestureScrollBegin: - scroll_state_data.position_x = event.x; - scroll_state_data.position_y = event.y; + scroll_state_data.position_x = event.PositionInWidget().x; + scroll_state_data.position_y = event.PositionInWidget().y; scroll_state_data.delta_x_hint = -event.data.scroll_begin.delta_x_hint; scroll_state_data.delta_y_hint = -event.data.scroll_begin.delta_y_hint; scroll_state_data.is_beginning = true; @@ -114,6 +113,21 @@ cc::ScrollState CreateScrollStateForGesture(const WebGestureEvent& event) { return cc::ScrollState(scroll_state_data); } +cc::ScrollState CreateScrollStateForInertialEnd() { + cc::ScrollStateData scroll_state_data; + scroll_state_data.is_ending = true; + return cc::ScrollState(scroll_state_data); +} + +cc::ScrollState CreateScrollStateForInertialUpdate( + const gfx::Vector2dF& delta) { + cc::ScrollStateData scroll_state_data; + scroll_state_data.delta_x = delta.x(); + scroll_state_data.delta_y = delta.y(); + scroll_state_data.is_in_inertial_phase = true; + return cc::ScrollState(scroll_state_data); +} + cc::InputHandler::ScrollInputType GestureScrollInputType( blink::WebGestureDevice device) { return device == blink::kWebGestureDeviceTouchpad @@ -161,7 +175,8 @@ InputHandlerProxy::InputHandlerProxy( current_overscroll_params_(nullptr), has_ongoing_compositor_scroll_fling_pinch_(false), is_first_gesture_scroll_update_(false), - tick_clock_(base::DefaultTickClock::GetInstance()) { + tick_clock_(base::DefaultTickClock::GetInstance()), + snap_fling_controller_(std::make_unique<SnapFlingController>(this)) { DCHECK(client); input_handler_->BindToClient(this, touchpad_and_wheel_scroll_latching_enabled_); @@ -214,13 +229,13 @@ void InputHandlerProxy::HandleInputEventWithLatencyInfo( if (has_ongoing_compositor_scroll_fling_pinch_) { const auto& gesture_event = ToWebGestureEvent(event_with_callback->event()); bool is_from_set_non_blocking_touch = - gesture_event.source_device == blink::kWebGestureDeviceTouchscreen && + gesture_event.SourceDevice() == blink::kWebGestureDeviceTouchscreen && gesture_event.is_source_touch_event_set_non_blocking; bool is_scroll_end_from_wheel = - gesture_event.source_device == blink::kWebGestureDeviceTouchpad && + gesture_event.SourceDevice() == blink::kWebGestureDeviceTouchpad && gesture_event.GetType() == blink::WebGestureEvent::kGestureScrollEnd; bool scroll_update_has_blocking_wheel_source = - gesture_event.source_device == blink::kWebGestureDeviceTouchpad && + gesture_event.SourceDevice() == blink::kWebGestureDeviceTouchpad && gesture_event.GetType() == blink::WebGestureEvent::kGestureScrollUpdate && (!async_wheel_events_enabled_ || is_first_gesture_scroll_update_); @@ -364,8 +379,8 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( DCHECK(fling_booster_->fling_cancellation_is_deferred()); TRACE_EVENT_INSTANT0("input", "InputHandlerProxy::FlingBoostStart", TRACE_EVENT_SCOPE_THREAD); - } else if (WebInputEvent::kGestureScrollBegin || - WebInputEvent::kGestureScrollUpdate) { + } else if (event.GetType() == WebInputEvent::kGestureScrollBegin || + event.GetType() == WebInputEvent::kGestureScrollUpdate) { TRACE_EVENT_INSTANT0("input", "InputHandlerProxy::ExtendBoostedFlingTimeout", TRACE_EVENT_SCOPE_THREAD); @@ -375,6 +390,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( } } + if (snap_fling_controller_->FilterEventForSnap(event)) + return DROP_EVENT; + switch (event.GetType()) { case WebInputEvent::kMouseWheel: return HandleMouseWheel(static_cast<const WebMouseWheelEvent&>(event)); @@ -394,7 +412,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( DCHECK(!gesture_pinch_on_impl_thread_); const WebGestureEvent& gesture_event = static_cast<const WebGestureEvent&>(event); - if (gesture_event.source_device == blink::kWebGestureDeviceTouchpad && + if (gesture_event.SourceDevice() == blink::kWebGestureDeviceTouchpad && input_handler_->GetEventListenerProperties( cc::EventListenerClass::kMouseWheel) != cc::EventListenerProperties::kNone) { @@ -412,8 +430,8 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( const WebGestureEvent& gesture_event = static_cast<const WebGestureEvent&>(event); input_handler_->PinchGestureEnd( - gfx::Point(gesture_event.x, gesture_event.y), - gesture_event.source_device == blink::kWebGestureDeviceTouchpad); + gfx::ToFlooredPoint(gesture_event.PositionInWidget()), + gesture_event.SourceDevice() == blink::kWebGestureDeviceTouchpad); return DID_HANDLE; } else { return DID_NOT_HANDLE; @@ -427,7 +445,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( return DROP_EVENT; input_handler_->PinchGestureUpdate( gesture_event.data.pinch_update.scale, - gfx::Point(gesture_event.x, gesture_event.y)); + gfx::ToFlooredPoint(gesture_event.PositionInWidget())); return DID_HANDLE; } else { return DID_NOT_HANDLE; @@ -686,29 +704,33 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin( cc::MainThreadScrollingReason::kContinuingMainThreadScroll; } else if (gesture_event.data.scroll_begin.target_viewport) { scroll_status = input_handler_->RootScrollBegin( - &scroll_state, GestureScrollInputType(gesture_event.source_device)); + &scroll_state, GestureScrollInputType(gesture_event.SourceDevice())); } else if (ShouldAnimate(gesture_event.data.scroll_begin.delta_hint_units != blink::WebGestureEvent::ScrollUnits::kPixels)) { DCHECK(!scroll_state.is_in_inertial_phase()); scroll_status = input_handler_->ScrollAnimatedBegin(&scroll_state); } else { scroll_status = input_handler_->ScrollBegin( - &scroll_state, GestureScrollInputType(gesture_event.source_device)); + &scroll_state, GestureScrollInputType(gesture_event.SourceDevice())); } - RecordMainThreadScrollingReasons(gesture_event.source_device, + RecordMainThreadScrollingReasons(gesture_event.SourceDevice(), scroll_status.main_thread_scrolling_reasons); - RecordScrollingThreadStatus(gesture_event.source_device, + RecordScrollingThreadStatus(gesture_event.SourceDevice(), scroll_status.main_thread_scrolling_reasons); InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE; scroll_sequence_ignored_ = false; + in_inertial_scrolling_ = false; switch (scroll_status.thread) { case cc::InputHandler::SCROLL_ON_IMPL_THREAD: TRACE_EVENT_INSTANT0("input", "InputHandlerProxy::handle_input gesture scroll", TRACE_EVENT_SCOPE_THREAD); gesture_scroll_on_impl_thread_ = true; + if (input_handler_->IsCurrentlyScrollingViewport()) + client_->DidStartScrollingViewport(); + if (scroll_status.bubble) result = DID_HANDLE_SHOULD_BUBBLE; else @@ -750,7 +772,8 @@ InputHandlerProxy::HandleGestureScrollUpdate( return DID_NOT_HANDLE; cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event); - gfx::Point scroll_point(gesture_event.x, gesture_event.y); + in_inertial_scrolling_ = scroll_state.is_in_inertial_phase(); + gfx::PointF scroll_point(gesture_event.PositionInWidget()); if (ShouldAnimate(gesture_event.data.scroll_update.delta_units != blink::WebGestureEvent::ScrollUnits::kPixels)) { @@ -759,7 +782,9 @@ InputHandlerProxy::HandleGestureScrollUpdate( base::TimeTicks() + base::TimeDelta::FromSecondsD(gesture_event.TimeStampSeconds()); base::TimeDelta delay = base::TimeTicks::Now() - event_time; - switch (input_handler_->ScrollAnimated(scroll_point, scroll_delta, delay) + switch (input_handler_ + ->ScrollAnimated(gfx::ToFlooredPoint(scroll_point), + scroll_delta, delay) .thread) { case cc::InputHandler::SCROLL_ON_IMPL_THREAD: return DID_HANDLE; @@ -774,14 +799,23 @@ InputHandlerProxy::HandleGestureScrollUpdate( return DID_NOT_HANDLE; } } + + if (snap_fling_controller_->HandleGestureScrollUpdate(gesture_event)) { +#ifndef NDEBUG + expect_scroll_update_end_ = false; +#endif + gesture_scroll_on_impl_thread_ = false; + return DROP_EVENT; + } + cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollBy(&scroll_state); if (!scroll_result.did_scroll && input_handler_->ScrollingShouldSwitchtoMainThread() && - ((gesture_event.source_device == blink::kWebGestureDeviceTouchpad && + ((gesture_event.SourceDevice() == blink::kWebGestureDeviceTouchpad && touchpad_and_wheel_scroll_latching_enabled_) || - gesture_event.source_device == blink::kWebGestureDeviceTouchscreen)) { + gesture_event.SourceDevice() == blink::kWebGestureDeviceTouchscreen)) { gesture_scroll_on_impl_thread_ = false; client_->GenerateScrollBeginAndSendToMainThread(gesture_event); @@ -828,8 +862,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd( InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( const WebGestureEvent& gesture_event) { - // Touchpad flings are handled on browser. - DCHECK(!(gesture_event.source_device == blink::kWebGestureDeviceTouchpad)); + // Touchpad and touchscreen flings are handled on browser. + DCHECK_NE(blink::kWebGestureDeviceTouchpad, gesture_event.SourceDevice()); + DCHECK_NE(blink::kWebGestureDeviceTouchscreen, gesture_event.SourceDevice()); #ifndef NDEBUG expect_scroll_update_end_ = false; #endif @@ -841,8 +876,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( cc::InputHandler::ScrollStatus scroll_status; scroll_status.main_thread_scrolling_reasons = cc::MainThreadScrollingReason::kNotScrollingOnMain; - switch (gesture_event.source_device) { - case blink::kWebGestureDeviceTouchscreen: + switch (gesture_event.SourceDevice()) { case blink::kWebGestureDeviceSyntheticAutoscroll: if (!gesture_scroll_on_impl_thread_) { scroll_status.thread = cc::InputHandler::SCROLL_ON_MAIN_THREAD; @@ -852,6 +886,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( scroll_status = input_handler_->FlingScrollBegin(); } break; + case blink::kWebGestureDeviceTouchscreen: case blink::kWebGestureDeviceTouchpad: case blink::kWebGestureDeviceUninitialized: case blink::kWebGestureDeviceCount: @@ -989,9 +1024,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart( result = DID_HANDLE_NON_BLOCKING; } - bool is_flinging_on_impl = - fling_curve_ && !fling_may_be_active_on_main_thread_; - if (is_flinging_on_impl && is_touching_scrolling_layer) + bool is_in_inertial_scrolling_on_impl = + in_inertial_scrolling_ && gesture_scroll_on_impl_thread_; + if (is_in_inertial_scrolling_on_impl && is_touching_scrolling_layer) result = DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING; client_->SetWhiteListedTouchAction(white_listed_touch_action, @@ -1033,6 +1068,8 @@ void InputHandlerProxy::Animate(base::TimeTicks time) { if (scroll_elasticity_controller_) scroll_elasticity_controller_->Animate(time); + snap_fling_controller_->Animate(time); + if (!fling_curve_) return; @@ -1138,8 +1175,36 @@ void InputHandlerProxy::SynchronouslyZoomBy(float magnify_delta, input_handler_->PinchGestureEnd(anchor, false); } +bool InputHandlerProxy::GetSnapFlingInfo( + const gfx::Vector2dF& natural_displacement, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset) const { + return input_handler_->GetSnapFlingInfo(natural_displacement, initial_offset, + target_offset); +} + +gfx::Vector2dF InputHandlerProxy::ScrollByForSnapFling( + const gfx::Vector2dF& delta) { + cc::ScrollState scroll_state = CreateScrollStateForInertialUpdate(delta); + // TODO(sunyunjia): We should consider moving the scroll to main, handling + // overscroll, and handling elastic scroll after ScrollBy(). + // https://crbug.com/819855 + cc::InputHandlerScrollResult scroll_result = + input_handler_->ScrollBy(&scroll_state); + return scroll_result.current_offset; +} + +void InputHandlerProxy::ScrollEndForSnapFling() { + cc::ScrollState scroll_state = CreateScrollStateForInertialEnd(); + input_handler_->ScrollEnd(&scroll_state, false); +} + +void InputHandlerProxy::RequestAnimationForSnapFling() { + RequestAnimation(); +} + void InputHandlerProxy::HandleOverscroll( - const gfx::Point& causal_event_viewport_point, + const gfx::PointF& causal_event_viewport_point, const cc::InputHandlerScrollResult& scroll_result, bool bundle_overscroll_params_with_ack) { DCHECK(client_); @@ -1173,7 +1238,7 @@ void InputHandlerProxy::HandleOverscroll( current_overscroll_params_->current_fling_velocity = ToClientScrollIncrement(current_fling_velocity_); current_overscroll_params_->causal_event_viewport_point = - gfx::PointF(causal_event_viewport_point); + causal_event_viewport_point; current_overscroll_params_->overscroll_behavior = scroll_result.overscroll_behavior; return; @@ -1182,7 +1247,7 @@ void InputHandlerProxy::HandleOverscroll( client_->DidOverscroll(scroll_result.accumulated_root_overscroll, scroll_result.unused_scroll_delta, ToClientScrollIncrement(current_fling_velocity_), - gfx::PointF(causal_event_viewport_point), + causal_event_viewport_point, scroll_result.overscroll_behavior); } @@ -1283,7 +1348,6 @@ bool InputHandlerProxy::ScrollBy(const WebFloatSize& increment, bool did_scroll = false; switch (fling_parameters_.source_device) { - case blink::kWebGestureDeviceTouchscreen: case blink::kWebGestureDeviceSyntheticAutoscroll: { clipped_increment = ToClientScrollIncrement(clipped_increment); cc::ScrollStateData scroll_state_data; @@ -1298,6 +1362,7 @@ bool InputHandlerProxy::ScrollBy(const WebFloatSize& increment, HandleOverscroll(fling_parameters_.point, scroll_result, false); did_scroll = scroll_result.did_scroll; } break; + case blink::kWebGestureDeviceTouchscreen: case blink::kWebGestureDeviceTouchpad: case blink::kWebGestureDeviceUninitialized: case blink::kWebGestureDeviceCount: @@ -1330,12 +1395,14 @@ void InputHandlerProxy::HandleScrollElasticityOverscroll( // impl thread event handling paths. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&InputScrollElasticityController::ObserveGestureEventAndResult, - scroll_elasticity_controller_->GetWeakPtr(), gesture_event, - scroll_result)); + base::BindOnce( + &InputScrollElasticityController::ObserveGestureEventAndResult, + scroll_elasticity_controller_->GetWeakPtr(), gesture_event, + scroll_result)); } -void InputHandlerProxy::SetTickClockForTesting(base::TickClock* tick_clock) { +void InputHandlerProxy::SetTickClockForTesting( + const base::TickClock* tick_clock) { tick_clock_ = tick_clock; } @@ -1343,23 +1410,22 @@ void InputHandlerProxy::UpdateCurrentFlingState( const WebGestureEvent& fling_start_event, const gfx::Vector2dF& velocity) { DCHECK_EQ(WebInputEvent::kGestureFlingStart, fling_start_event.GetType()); - // When wheel scroll latching is enabled touchpad flings are handled on - // browser. - DCHECK(fling_start_event.source_device != blink::kWebGestureDeviceTouchpad || - !touchpad_and_wheel_scroll_latching_enabled_); + // Flings with touchpad and touchscreen source devices are handled on browser. + DCHECK(fling_start_event.SourceDevice() != blink::kWebGestureDeviceTouchpad && + fling_start_event.SourceDevice() != + blink::kWebGestureDeviceTouchscreen); current_fling_velocity_ = velocity; fling_curve_ = client_->CreateFlingAnimationCurve( - fling_start_event.source_device, + fling_start_event.SourceDevice(), WebFloatPoint(velocity.x(), velocity.y()), blink::WebSize()); disallow_horizontal_fling_scroll_ = !velocity.x(); disallow_vertical_fling_scroll_ = !velocity.y(); fling_parameters_.start_time = fling_start_event.TimeStampSeconds(); fling_parameters_.delta = WebFloatPoint(velocity.x(), velocity.y()); - fling_parameters_.point = WebPoint(fling_start_event.x, fling_start_event.y); - fling_parameters_.global_point = - WebPoint(fling_start_event.global_x, fling_start_event.global_y); + fling_parameters_.point = fling_start_event.PositionInWidget(); + fling_parameters_.global_point = fling_start_event.PositionInScreen(); fling_parameters_.modifiers = fling_start_event.GetModifiers(); - fling_parameters_.source_device = fling_start_event.source_device; + fling_parameters_.source_device = fling_start_event.SourceDevice(); } } // namespace ui diff --git a/chromium/ui/events/blink/input_handler_proxy.h b/chromium/ui/events/blink/input_handler_proxy.h index feeea9d1c65..0670562fa1f 100644 --- a/chromium/ui/events/blink/input_handler_proxy.h +++ b/chromium/ui/events/blink/input_handler_proxy.h @@ -10,12 +10,13 @@ #include "base/containers/hash_tables.h" #include "base/macros.h" #include "cc/input/input_handler.h" -#include "third_party/WebKit/public/platform/WebGestureCurve.h" -#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" -#include "third_party/WebKit/public/web/WebActiveFlingParameters.h" +#include "third_party/blink/public/platform/web_gesture_curve.h" +#include "third_party/blink/public/platform/web_gesture_curve_target.h" +#include "third_party/blink/public/platform/web_gesture_event.h" +#include "third_party/blink/public/web/web_active_fling_parameters.h" #include "ui/events/blink/blink_features.h" #include "ui/events/blink/input_scroll_elasticity_controller.h" +#include "ui/events/blink/snap_fling_controller.h" #include "ui/events/blink/synchronous_input_handler_proxy.h" #include "ui/events/blink/web_input_event_traits.h" @@ -51,7 +52,8 @@ struct DidOverscrollParams; // events intended for a specific WebWidget. class InputHandlerProxy : public cc::InputHandlerClient, public SynchronousInputHandlerProxy, - public blink::WebGestureCurveTarget { + public blink::WebGestureCurveTarget, + public SnapFlingClient { public: InputHandlerProxy(cc::InputHandler* input_handler, InputHandlerProxyClient* client, @@ -114,6 +116,14 @@ class InputHandlerProxy : public cc::InputHandlerClient, bool ScrollBy(const blink::WebFloatSize& offset, const blink::WebFloatSize& velocity) override; + // SnapFlingClient implementation. + bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset) const override; + gfx::Vector2dF ScrollByForSnapFling(const gfx::Vector2dF& delta) override; + void ScrollEndForSnapFling() override; + void RequestAnimationForSnapFling() override; + bool gesture_scroll_on_impl_thread_for_testing() const { return gesture_scroll_on_impl_thread_; } @@ -164,7 +174,7 @@ class InputHandlerProxy : public cc::InputHandlerClient, // Used to send overscroll messages to the browser. // |bundle_overscroll_params_with_ack| means overscroll message should be // bundled with triggering event response, and won't fire |DidOverscroll|. - void HandleOverscroll(const gfx::Point& causal_event_viewport_point, + void HandleOverscroll(const gfx::PointF& causal_event_viewport_point, const cc::InputHandlerScrollResult& scroll_result, bool bundle_overscroll_params_with_ack); @@ -179,7 +189,7 @@ class InputHandlerProxy : public cc::InputHandlerClient, // Overrides the internal clock for testing. // This doesn't take the ownership of the clock. |tick_clock| must outlive the // InputHandlerProxy instance. - void SetTickClockForTesting(base::TickClock* tick_clock); + void SetTickClockForTesting(const base::TickClock* tick_clock); // |is_touching_scrolling_layer| indicates if one of the points that has // been touched hits a currently scrolling layer. @@ -213,6 +223,7 @@ class InputHandlerProxy : public cc::InputHandlerClient, #endif bool gesture_scroll_on_impl_thread_; bool gesture_pinch_on_impl_thread_; + bool in_inertial_scrolling_ = false; bool scroll_sequence_ignored_; // This is always false when there are no flings on the main thread, but // conservative in the sense that we might not be actually flinging when it is @@ -260,10 +271,12 @@ class InputHandlerProxy : public cc::InputHandlerClient, bool has_ongoing_compositor_scroll_fling_pinch_; bool is_first_gesture_scroll_update_; - base::TickClock* tick_clock_; + const base::TickClock* tick_clock_; std::unique_ptr<FlingBooster> fling_booster_; + std::unique_ptr<SnapFlingController> snap_fling_controller_; + DISALLOW_COPY_AND_ASSIGN(InputHandlerProxy); }; diff --git a/chromium/ui/events/blink/input_handler_proxy_client.h b/chromium/ui/events/blink/input_handler_proxy_client.h index 3d992cd12fb..d6d44c93df1 100644 --- a/chromium/ui/events/blink/input_handler_proxy_client.h +++ b/chromium/ui/events/blink/input_handler_proxy_client.h @@ -49,6 +49,8 @@ class InputHandlerProxyClient { virtual void DidAnimateForInput() = 0; + virtual void DidStartScrollingViewport() = 0; + // Used to send a GSB to the main thread when the wheel scroll latching is // enabled and the scrolling should switch to the main thread. virtual void GenerateScrollBeginAndSendToMainThread( diff --git a/chromium/ui/events/blink/input_handler_proxy_unittest.cc b/chromium/ui/events/blink/input_handler_proxy_unittest.cc index 2f1830b4735..0d890a51654 100644 --- a/chromium/ui/events/blink/input_handler_proxy_unittest.cc +++ b/chromium/ui/events/blink/input_handler_proxy_unittest.cc @@ -18,14 +18,14 @@ #include "cc/trees/swap_promise_monitor.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebFloatPoint.h" -#include "third_party/WebKit/public/platform/WebFloatSize.h" -#include "third_party/WebKit/public/platform/WebGestureCurve.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebKeyboardEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" -#include "third_party/WebKit/public/platform/WebPoint.h" -#include "third_party/WebKit/public/platform/WebTouchEvent.h" +#include "third_party/blink/public/platform/web_float_point.h" +#include "third_party/blink/public/platform/web_float_size.h" +#include "third_party/blink/public/platform/web_gesture_curve.h" +#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/platform/web_keyboard_event.h" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" +#include "third_party/blink/public/platform/web_point.h" +#include "third_party/blink/public/platform/web_touch_event.h" #include "ui/events/blink/blink_event_util.h" #include "ui/events/blink/compositor_thread_event_queue.h" #include "ui/events/blink/did_overscroll_params.h" @@ -85,32 +85,21 @@ MATCHER_P(WheelEventsMatch, expected, "") { WebGestureEvent CreateFling(base::TimeTicks timestamp, WebGestureDevice source_device, WebFloatPoint velocity, - WebPoint point, - WebPoint global_point, + WebFloatPoint point, + WebFloatPoint global_point, int modifiers) { WebGestureEvent fling(WebInputEvent::kGestureFlingStart, modifiers, - (timestamp - base::TimeTicks()).InSecondsF()); + (timestamp - base::TimeTicks()).InSecondsF(), + source_device); // Touchpad fling is handled on broswer. DCHECK(source_device != blink::kWebGestureDeviceTouchpad); - fling.source_device = source_device; fling.data.fling_start.velocity_x = velocity.x; fling.data.fling_start.velocity_y = velocity.y; - fling.x = point.x; - fling.y = point.y; - fling.global_x = global_point.x; - fling.global_y = global_point.y; + fling.SetPositionInWidget(point); + fling.SetPositionInScreen(global_point); return fling; } -WebGestureEvent CreateFling(WebGestureDevice source_device, - WebFloatPoint velocity, - WebPoint point, - WebPoint global_point, - int modifiers) { - return CreateFling(base::TimeTicks(), source_device, velocity, point, - global_point, modifiers); -} - WebScopedInputEvent CreateGestureScrollFlingPinch( WebInputEvent::Type type, WebGestureDevice source_device, @@ -118,8 +107,8 @@ WebScopedInputEvent CreateGestureScrollFlingPinch( int x = 0, int y = 0) { WebGestureEvent gesture(type, WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests()); - gesture.source_device = source_device; + WebInputEvent::GetStaticTimeStampForTests(), + source_device); if (type == WebInputEvent::kGestureScrollUpdate) { gesture.data.scroll_update.delta_y = delta_y_or_scale; } else if (type == WebInputEvent::kGestureFlingStart) { @@ -128,8 +117,7 @@ WebScopedInputEvent CreateGestureScrollFlingPinch( gesture.data.fling_start.velocity_y = delta_y_or_scale; } else if (type == WebInputEvent::kGesturePinchUpdate) { gesture.data.pinch_update.scale = delta_y_or_scale; - gesture.x = x; - gesture.y = y; + gesture.SetPositionInWidget(gfx::PointF(x, y)); } return WebInputEventTraits::Clone(gesture); } @@ -208,6 +196,11 @@ class MockInputHandler : public cc::InputHandler { } void set_is_scrolling_root(bool is) { is_scrolling_root_ = is; } + MOCK_CONST_METHOD3(GetSnapFlingInfo, + bool(const gfx::Vector2dF& natural_displacement, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset)); + private: bool is_scrolling_root_ = true; DISALLOW_COPY_AND_ASSIGN(MockInputHandler); @@ -293,6 +286,7 @@ class MockInputHandlerProxyClient const cc::OverscrollBehavior& overscroll_behavior)); void DidStopFlinging() override {} void DidAnimateForInput() override {} + void DidStartScrollingViewport() override {} MOCK_METHOD3(SetWhiteListedTouchAction, void(cc::TouchAction touch_action, uint32_t unique_touch_event_id, @@ -393,7 +387,7 @@ class InputHandlerProxyTest mock_input_handler_.set_is_scrolling_root(synchronous_root_scroll_); // Set a default device so tests don't always have to set this. - gesture_.source_device = blink::kWebGestureDeviceTouchpad; + gesture_.SetSourceDevice(blink::kWebGestureDeviceTouchpad); } virtual ~InputHandlerProxyTest() { input_handler_.reset(); } @@ -436,14 +430,14 @@ class InputHandlerProxyTest void StartFling(base::TimeTicks timestamp, WebGestureDevice source_device, WebFloatPoint velocity, - WebPoint position) { + WebFloatPoint position) { expected_disposition_ = InputHandlerProxy::DID_HANDLE; VERIFY_AND_RESET_MOCKS(); EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) .WillOnce(testing::Return(kImplThreadScrollState)); gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = source_device; + gesture_.SetSourceDevice(source_device); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -482,6 +476,7 @@ class InputHandlerProxyTest void GestureScrollStarted(); void ScrollHandlingSwitchedToMainThread(); void GestureScrollIgnored(); + void FlingAndSnap(); const bool synchronous_root_scroll_; const bool install_synchronous_handler_; @@ -545,7 +540,7 @@ class InputHandlerProxyEventQueueTest : public testing::TestWithParam<bool> { CreateGestureScrollFlingPinch(type, source_device, delta_y_or_scale, x, y), latency, - base::Bind( + base::BindOnce( &InputHandlerProxyEventQueueTest::DidHandleInputEventAndOverscroll, weak_ptr_factory_.GetWeakPtr())); } @@ -563,7 +558,8 @@ class InputHandlerProxyEventQueueTest : public testing::TestWithParam<bool> { return input_handler_proxy_->compositor_event_queue_->queue_; } - void SetInputHandlerProxyTickClockForTesting(base::TickClock* tick_clock) { + void SetInputHandlerProxyTickClockForTesting( + const base::TickClock* tick_clock) { input_handler_proxy_->SetTickClockForTesting(tick_clock); } @@ -821,6 +817,59 @@ TEST_P(InputHandlerProxyTest, GestureScrollBeginThatTargetViewport) { VERIFY_AND_RESET_MOCKS(); } +void InputHandlerProxyTest::FlingAndSnap() { + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + + gesture_.SetType(WebInputEvent::kGestureScrollBegin); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + // The event should be dropped if InputHandler decides to snap. + expected_disposition_ = InputHandlerProxy::DROP_EVENT; + VERIFY_AND_RESET_MOCKS(); + + gesture_.SetType(WebInputEvent::kGestureScrollUpdate); + gesture_.data.scroll_update.delta_y = + -40; // -Y means scroll down - i.e. in the +Y direction. + gesture_.data.scroll_update.inertial_phase = + blink::WebGestureEvent::kMomentumPhase; + EXPECT_CALL(mock_input_handler_, + GetSnapFlingInfo(testing::_, testing::_, testing::_)) + .WillOnce(DoAll(testing::SetArgPointee<1>(gfx::Vector2dF(0, 0)), + testing::SetArgPointee<2>(gfx::Vector2dF(0, 100)), + testing::Return(true))); + EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(1); + EXPECT_SET_NEEDS_ANIMATE_INPUT(1); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + VERIFY_AND_RESET_MOCKS(); +} + +TEST_P(InputHandlerProxyTest, SnapFlingIgnoresFollowingGSUAndGSE) { + FlingAndSnap(); + // The next GestureScrollUpdate should also be ignored, and will not ask for + // snap position. + expected_disposition_ = InputHandlerProxy::DROP_EVENT; + + EXPECT_CALL(mock_input_handler_, + GetSnapFlingInfo(testing::_, testing::_, testing::_)) + .Times(0); + EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + VERIFY_AND_RESET_MOCKS(); + + // The GestureScrollEnd should also be ignored. + expected_disposition_ = InputHandlerProxy::DROP_EVENT; + gesture_.SetType(WebInputEvent::kGestureScrollEnd); + gesture_.data.scroll_end.inertial_phase = + blink::WebGestureEvent::kMomentumPhase; + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, testing::_)).Times(0); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + VERIFY_AND_RESET_MOCKS(); +} + TEST_P(InputHandlerProxyTest, GesturePinch) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; @@ -837,8 +886,7 @@ TEST_P(InputHandlerProxyTest, GesturePinch) { gesture_.SetType(WebInputEvent::kGesturePinchUpdate); gesture_.data.pinch_update.scale = 1.5; - gesture_.x = 7; - gesture_.y = 13; + gesture_.SetPositionInWidget(gfx::PointF(7, 13)); EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(1.5, gfx::Point(7, 13))); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -847,8 +895,7 @@ TEST_P(InputHandlerProxyTest, GesturePinch) { gesture_.SetType(WebInputEvent::kGesturePinchUpdate); gesture_.data.pinch_update.scale = 0.5; gesture_.data.pinch_update.zoom_disabled = true; - gesture_.x = 9; - gesture_.y = 6; + gesture_.SetPositionInWidget(gfx::PointF(9, 6)); EXPECT_EQ(InputHandlerProxy::DROP_EVENT, input_handler_->HandleInputEvent(gesture_)); gesture_.data.pinch_update.zoom_disabled = false; @@ -857,8 +904,7 @@ TEST_P(InputHandlerProxyTest, GesturePinch) { gesture_.SetType(WebInputEvent::kGesturePinchUpdate); gesture_.data.pinch_update.scale = 0.5; - gesture_.x = 9; - gesture_.y = 6; + gesture_.SetPositionInWidget(gfx::PointF(9, 6)); EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(.5, gfx::Point(9, 6))); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -886,16 +932,14 @@ TEST_P(InputHandlerProxyTest, GesturePinchWithWheelHandler) { gesture_.SetType(WebInputEvent::kGesturePinchUpdate); gesture_.data.pinch_update.scale = 1.5; - gesture_.x = 7; - gesture_.y = 13; + gesture_.SetPositionInWidget(gfx::PointF(7, 13)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); gesture_.SetType(WebInputEvent::kGesturePinchUpdate); gesture_.data.pinch_update.scale = 0.5; - gesture_.x = 9; - gesture_.y = 6; + gesture_.SetPositionInWidget(gfx::PointF(9, 6)); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); @@ -938,8 +982,7 @@ TEST_P(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) { gesture_.SetType(WebInputEvent::kGesturePinchUpdate); gesture_.data.pinch_update.scale = 1.5; - gesture_.x = 7; - gesture_.y = 13; + gesture_.SetPositionInWidget(gfx::PointF(7, 13)); EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(1.5, gfx::Point(7, 13))); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -958,8 +1001,7 @@ TEST_P(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) { gesture_.SetType(WebInputEvent::kGesturePinchUpdate); gesture_.data.pinch_update.scale = 0.5; - gesture_.x = 9; - gesture_.y = 6; + gesture_.SetPositionInWidget(gfx::PointF(9, 6)); EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(.5, gfx::Point(9, 6))); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); @@ -1030,289 +1072,16 @@ void InputHandlerProxyTest::ScrollHandlingSwitchedToMainThread() { VERIFY_AND_RESET_MOCKS(); } TEST_P(InputHandlerProxyTest, WheelScrollHandlingSwitchedToMainThread) { - gesture_.source_device = blink::kWebGestureDeviceTouchpad; + gesture_.SetSourceDevice(blink::kWebGestureDeviceTouchpad); ScrollHandlingSwitchedToMainThread(); } TEST_P(InputHandlerProxyTest, TouchScrollHandlingSwitchedToMainThread) { - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; + gesture_.SetSourceDevice(blink::kWebGestureDeviceTouchscreen); ScrollHandlingSwitchedToMainThread(); } -TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchscreen) { - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - - gesture_.SetType(WebInputEvent::kGestureFlingStart); - gesture_.data.fling_start.velocity_x = 10; - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - - // Verify that a GestureFlingCancel during an animation cancels it. - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, GestureFlingOnMainThreadTouchscreen) { - // We should send all events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kMainThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()).Times(0); - - gesture_.SetType(WebInputEvent::kGestureFlingStart); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Even if we didn't start a fling ourselves, we still need to send the cancel - // event to the widget. - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); -} - -TEST_P(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) { - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - expected_disposition_ = InputHandlerProxy::DROP_EVENT; - VERIFY_AND_RESET_MOCKS(); - - // Flings ignored by the InputHandler should be dropped, signalling the end - // of the touch scroll sequence. - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kScrollIgnoredScrollState)); - - gesture_.SetType(WebInputEvent::kGestureFlingStart); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Subsequent scrolls should behave normally, even without an intervening - // GestureFlingCancel, as the original GestureFlingStart was dropped. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchscreen) { - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - WebFloatPoint fling_delta = WebFloatPoint(100, 0); - WebPoint fling_point = WebPoint(7, 13); - WebPoint fling_global_point = WebPoint(17, 23); - // Note that for touchscreen the control modifier is not special. - int modifiers = WebInputEvent::kControlKey; - gesture_ = CreateFling(blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_global_point, modifiers); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - // The first animate call should let us pick up an animation start time, but - // we shouldn't actually move anywhere just yet. The first frame after the - // fling start will typically include the last scroll from the gesture that - // lead to the scroll (either wheel or gesture scroll), so there should be no - // visible hitch. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - // The second call should start scrolling in the -X direction. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - time += base::TimeDelta::FromMilliseconds(100); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, GestureFlingWithValidTimestamp) { - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(100, 0); - WebPoint fling_point = WebPoint(7, 13); - WebPoint fling_global_point = WebPoint(17, 23); - int modifiers = WebInputEvent::kControlKey; - gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_global_point, modifiers); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - // With a valid time stamp, the first animate call should skip start time - // initialization and immediately begin scroll update production. This reduces - // the likelihood of a hitch between the scroll preceding the fling and - // the first scroll generated by the fling. - // Scrolling should start in the -X direction. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - time += dt; - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, GestureFlingWithInvalidTimestamp) { - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - base::TimeDelta start_time_offset = base::TimeDelta::FromMilliseconds(10); - gesture_.SetType(WebInputEvent::kGestureFlingStart); - WebFloatPoint fling_delta = WebFloatPoint(100, 0); - WebPoint fling_point = WebPoint(7, 13); - WebPoint fling_global_point = WebPoint(17, 23); - int modifiers = WebInputEvent::kControlKey; - gesture_.SetTimeStampSeconds(start_time_offset.InSecondsF()); - gesture_.data.fling_start.velocity_x = fling_delta.x; - gesture_.data.fling_start.velocity_y = fling_delta.y; - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - gesture_.x = fling_point.x; - gesture_.y = fling_point.y; - gesture_.global_x = fling_global_point.x; - gesture_.global_y = fling_global_point.y; - gesture_.SetModifiers(modifiers); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - // Event though a time stamp was provided for the fling event, it will be - // ignored as its too far in the past relative to the first animate call's - // timestamp. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - base::TimeTicks time = - base::TimeTicks() + start_time_offset + base::TimeDelta::FromSeconds(1); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - // Further animation ticks should update the fling as usual. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - time += base::TimeDelta::FromMilliseconds(10); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, GestureScrollOnImplThreadFlagClearedAfterFling) { +TEST_P(InputHandlerProxyTest, + GestureScrollOnImplThreadFlagClearedAfterScrollEnd) { // We shouldn't send any events to the widget for this gesture. expected_disposition_ = InputHandlerProxy::DID_HANDLE; VERIFY_AND_RESET_MOCKS(); @@ -1327,56 +1096,15 @@ TEST_P(InputHandlerProxyTest, GestureScrollOnImplThreadFlagClearedAfterFling) { // |gesture_scroll_on_impl_thread_| should be true. EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; VERIFY_AND_RESET_MOCKS(); - // On the fling start, we should schedule an animation but not actually start - // scrolling. - WebFloatPoint fling_delta = WebFloatPoint(100, 0); - WebPoint fling_point = WebPoint(7, 13); - WebPoint fling_global_point = WebPoint(17, 23); - int modifiers = WebInputEvent::kControlKey | WebInputEvent::kAltKey; - gesture_ = CreateFling(blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_global_point, modifiers); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - // |gesture_scroll_on_impl_thread_| should still be true after - // a GestureFlingStart is sent. - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - - VERIFY_AND_RESET_MOCKS(); - // The first animate call should let us pick up an animation start time, but - // we shouldn't actually move anywhere just yet. The first frame after the - // fling start will typically include the last scroll from the gesture that - // lead to the scroll (either wheel or gesture scroll), so there should be no - // visible hitch. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - // The second call should start scrolling in the -X direction. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - time += base::TimeDelta::FromMilliseconds(100); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - gesture_.SetType(WebInputEvent::kGestureFlingCancel); + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + gesture_.SetType(WebInputEvent::kGestureScrollEnd); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - // |gesture_scroll_on_impl_thread_| should be false once - // the fling has finished (note no GestureScrollEnd has been sent). - EXPECT_TRUE(!input_handler_->gesture_scroll_on_impl_thread_for_testing()); + // |gesture_scroll_on_impl_thread_| should be false once a GestureScrollEnd + // gets handled. + EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); VERIFY_AND_RESET_MOCKS(); } @@ -1399,290 +1127,6 @@ TEST_P(InputHandlerProxyTest, expected_disposition_ = InputHandlerProxy::DID_HANDLE; VERIFY_AND_RESET_MOCKS(); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - WebFloatPoint fling_delta = WebFloatPoint(100, 0); - WebPoint fling_point = WebPoint(7, 13); - WebPoint fling_global_point = WebPoint(17, 23); - int modifiers = WebInputEvent::kControlKey | WebInputEvent::kAltKey; - gesture_ = CreateFling(blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_global_point, modifiers); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - // |gesture_scroll_on_impl_thread_| should still be true after - // a GestureFlingStart is sent. - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - - VERIFY_AND_RESET_MOCKS(); - - // gesture_scroll_on_impl_thread_ is still true when this scroll begins. As a - // result, this scroll begin will cancel the previous fling. - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - // After sending a GestureScrollBegin, the member variable - // |gesture_scroll_on_impl_thread_| should be true. - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdgeTouchscreen) { - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_. - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - gesture_.SetType(WebInputEvent::kGestureFlingStart); - WebFloatPoint fling_delta = WebFloatPoint(100, 100); - gesture_.data.fling_start.velocity_x = fling_delta.x; - gesture_.data.fling_start.velocity_y = fling_delta.y; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); - - // The first animate doesn't cause any scrolling. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The second animate starts scrolling in the positive X and Y directions. - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - time += base::TimeDelta::FromMilliseconds(100); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The third animate overscrolls in the positive Y direction but scrolls - // somewhat. - cc::InputHandlerScrollResult overscroll; - overscroll.did_scroll = true; - overscroll.did_overscroll_root = true; - overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100); - overscroll.unused_scroll_delta = gfx::Vector2dF(0, 10); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) - .WillOnce(testing::Return(overscroll)); - EXPECT_CALL( - mock_client_, - DidOverscroll(overscroll.accumulated_root_overscroll, - overscroll.unused_scroll_delta, - testing::Property(&gfx::Vector2dF::y, testing::Lt(0)), - testing::_, overscroll.overscroll_behavior)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - time += base::TimeDelta::FromMilliseconds(100); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The next call to animate will no longer scroll vertically. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Eq(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - time += base::TimeDelta::FromMilliseconds(100); - Animate(time); - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, GestureFlingNotCancelledBySmallTimeDelta) { - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(100, 0); - WebPoint fling_point = WebPoint(7, 13); - WebPoint fling_global_point = WebPoint(17, 23); - int modifiers = WebInputEvent::kControlKey; - gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_global_point, modifiers); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - // With an animation timestamp equivalent to the starting timestamp, the - // animation will simply be rescheduled. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - - // A small time delta should not stop the fling, even if the client - // reports no scrolling. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_not_scroll_)); - time += base::TimeDelta::FromMicroseconds(5); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - - // A time delta of zero should not stop the fling, and neither should it - // trigger scrolling on the client. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - - // Lack of movement on the client, with a non-trivial scroll delta, should - // terminate the fling. - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(1)))) - .WillOnce(testing::Return(scroll_result_did_not_scroll_)); - time += base::TimeDelta::FromMilliseconds(100); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); -} - -TEST_P(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { - cc::InputHandlerScrollResult overscroll; - overscroll.did_scroll = true; - overscroll.did_overscroll_root = true; - - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - gesture_.SetType(WebInputEvent::kGestureFlingStart); - WebFloatPoint fling_delta = WebFloatPoint(100, 100); - gesture_.data.fling_start.velocity_x = fling_delta.x; - gesture_.data.fling_start.velocity_y = fling_delta.y; - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); - - // The first animate doesn't cause any scrolling. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The second animate starts scrolling in the positive X and Y directions. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - time += base::TimeDelta::FromMilliseconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The third animate hits the bottom content edge. - overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100); - overscroll.unused_scroll_delta = gfx::Vector2dF(0, 100); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0)))) - .WillOnce(testing::Return(overscroll)); - EXPECT_CALL( - mock_client_, - DidOverscroll(overscroll.accumulated_root_overscroll, - overscroll.unused_scroll_delta, - testing::Property(&gfx::Vector2dF::y, testing::Lt(0)), - testing::_, overscroll.overscroll_behavior)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - time += base::TimeDelta::FromMilliseconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The next call to animate will no longer scroll vertically. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Eq(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - time += base::TimeDelta::FromMilliseconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The next call will hit the right edge. - overscroll.accumulated_root_overscroll = gfx::Vector2dF(100, 100); - overscroll.unused_scroll_delta = gfx::Vector2dF(100, 0); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) - .WillOnce(testing::Return(overscroll)); - EXPECT_CALL( - mock_client_, - DidOverscroll(overscroll.accumulated_root_overscroll, - overscroll.unused_scroll_delta, - testing::Property(&gfx::Vector2dF::x, testing::Lt(0)), - testing::_, overscroll.overscroll_behavior)); - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - time += base::TimeDelta::FromMilliseconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // The next call to animate will no longer scroll horizontally or vertically, - // and the fling should be cancelled. - EXPECT_SET_NEEDS_ANIMATE_INPUT(0); - EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); - time += base::TimeDelta::FromMilliseconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); } TEST_P(InputHandlerProxyTest, HitTestTouchEventNonNullTouchAction) { @@ -1990,620 +1434,6 @@ TEST_P(InputHandlerProxyTest, TouchMoveBlockingAddedAfterPassiveTouchStart) { VERIFY_AND_RESET_MOCKS(); } -TEST_P(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) { - // We shouldn't send any events to the widget for this gesture. - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(InputHandlerProxy::DID_HANDLE, - input_handler_->HandleInputEvent(gesture_)); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // Keyboard events received during a scroll should have no effect. - WebKeyboardEvent key_event(WebInputEvent::kKeyDown, - WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests()); - EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE, - input_handler_->HandleInputEvent(key_event)); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, animation should be scheduled, but no scrolling occurs. - gesture_.SetType(WebInputEvent::kGestureFlingStart); - WebFloatPoint fling_delta = WebFloatPoint(100, 100); - gesture_.data.fling_start.velocity_x = fling_delta.x; - gesture_.data.fling_start.velocity_y = fling_delta.y; - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // Keyboard events received during a fling should cancel the active fling. - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE, - input_handler_->HandleInputEvent(key_event)); - EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // The call to animate should have no effect, as the fling was cancelled. - base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // A fling cancel should be dropped, as there is nothing to cancel. - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(InputHandlerProxy::DROP_EVENT, - input_handler_->HandleInputEvent(gesture_)); - EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); -} - -TEST_P(InputHandlerProxyTest, GestureFlingCancelledByWheelEvent) { - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // Wheel events received during a scroll shouldn't cancel the fling, but will - // cause scrolling. - cc::InputHandlerScrollResult result; - - EXPECT_CALL(mock_input_handler_, - GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) - .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); - - WebMouseWheelEvent wheel_event(WebInputEvent::kMouseWheel, - WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests()); - input_handler_->HandleInputEvent(wheel_event); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, animation should be scheduled, but no scrolling occurs. - gesture_.SetType(WebInputEvent::kGestureFlingStart); - WebFloatPoint fling_delta = WebFloatPoint(100, 100); - gesture_.data.fling_start.velocity_x = fling_delta.x; - gesture_.data.fling_start.velocity_y = fling_delta.y; - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // Wheel events received during a fling should cancel the active fling, and - // cause a scroll. - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - - EXPECT_CALL(mock_input_handler_, - GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) - .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking)); - - input_handler_->HandleInputEvent(wheel_event); - EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); - VERIFY_AND_RESET_MOCKS(); - - // The call to animate should have no effect, as the fling was cancelled. - base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); - Animate(time); - VERIFY_AND_RESET_MOCKS(); - - // A fling cancel should be dropped, as there is nothing to cancel. - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(InputHandlerProxy::DROP_EVENT, - input_handler_->HandleInputEvent(gesture_)); - EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing()); -} - -TEST_P(InputHandlerProxyTest, GestureFlingWithNegativeTimeDelta) { - // We shouldn't send any events to the widget for this gesture. - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // On the fling start, we should schedule an animation but not actually start - // scrolling. - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(100, 0); - WebPoint fling_point = WebPoint(7, 13); - WebPoint fling_global_point = WebPoint(17, 23); - int modifiers = WebInputEvent::kControlKey; - gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_global_point, modifiers); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // If we get a negative time delta, that is, the Animation tick time happens - // before the fling's start time then we should *not* try scrolling and - // instead reset the fling start time. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0); - time -= base::TimeDelta::FromMilliseconds(5); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - // The first call should have reset the start time so subsequent calls should - // generate scroll events. - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - - Animate(time + base::TimeDelta::FromMilliseconds(1)); - - VERIFY_AND_RESET_MOCKS(); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, FlingBoost) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - base::TimeTicks last_animate_time = time; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - // Now cancel the fling. The fling cancellation should be deferred to allow - // fling boosting events to arrive. - time += dt; - CancelFling(time); - - // The GestureScrollBegin should be swallowed by the fling if a fling cancel - // is deferred. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Animate calls within the deferred cancellation window should continue. - time += dt; - float expected_delta = - (time - last_animate_time).InSecondsF() * -fling_delta.x; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - Animate(time); - last_animate_time = time; - - VERIFY_AND_RESET_MOCKS(); - - // GestureScrollUpdates in the same direction and at sufficient speed should - // be swallowed by the fling. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollUpdate); - gesture_.data.scroll_update.delta_x = fling_delta.x; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Animate calls within the deferred cancellation window should continue. - time += dt; - expected_delta = (time - last_animate_time).InSecondsF() * -fling_delta.x; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - Animate(time); - last_animate_time = time; - - VERIFY_AND_RESET_MOCKS(); - - // GestureFlingStart in the same direction and at sufficient speed should - // boost the active fling. - - gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_point, 0); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); - - time += dt; - // Note we get *2x* as much delta because 2 flings have combined. - expected_delta = 2 * (time - last_animate_time).InSecondsF() * -fling_delta.x; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - Animate(time); - last_animate_time = time; - - VERIFY_AND_RESET_MOCKS(); - - // Repeated GestureFlingStarts should accumulate. - - CancelFling(time); - gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point, fling_point, 0); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); - - time += dt; - // Note we get *3x* as much delta because 3 flings have combined. - expected_delta = 3 * (time - last_animate_time).InSecondsF() * -fling_delta.x; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - Animate(time); - last_animate_time = time; - - VERIFY_AND_RESET_MOCKS(); - - // GestureFlingCancel should terminate the fling if no boosting gestures are - // received within the timeout window. - - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - time += base::TimeDelta::FromMilliseconds(100); - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, NoFlingBoostIfScrollDelayed) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - // Cancel the fling. The fling cancellation should be deferred to allow - // fling boosting events to arrive. - time += dt; - CancelFling(time); - - // The GestureScrollBegin should be swallowed by the fling if a fling cancel - // is deferred. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // If no GestureScrollUpdate or GestureFlingStart is received within the - // timeout window, the fling should be cancelled and scrolling should resume. - time += base::TimeDelta::FromMilliseconds(100); - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, NoFlingBoostIfNotAnimated) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - // Animate fling once. - time += dt; - EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - Animate(time); - - // Cancel the fling after long delay of no animate. The fling cancellation - // should be deferred to allow fling boosting events to arrive. - time += base::TimeDelta::FromMilliseconds(100); - CancelFling(time); - - // The GestureScrollBegin should be swallowed by the fling if a fling cancel - // is deferred. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Should exit scroll bosting on GestureScrollUpdate due to long delay - // since last animate. Cancel old fling and start new scroll. - gesture_.SetType(WebInputEvent::kGestureScrollUpdate); - gesture_.data.scroll_update.delta_y = -40; - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, NoFlingBoostIfFlingInDifferentDirection) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - // Cancel the fling. The fling cancellation should be deferred to allow - // fling boosting events to arrive. - time += dt; - CancelFling(time); - - // If the new fling is orthogonal to the existing fling, no boosting should - // take place, with the new fling replacing the old. - WebFloatPoint orthogonal_fling_delta = - WebFloatPoint(fling_delta.y, -fling_delta.x); - gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, - orthogonal_fling_delta, fling_point, fling_point, 0); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Note that the new fling delta uses the orthogonal, unboosted fling - // velocity. - time += dt; - float expected_delta = dt.InSecondsF() * -orthogonal_fling_delta.y; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, NoFlingBoostIfScrollInDifferentDirection) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - // Cancel the fling. The fling cancellation should be deferred to allow - // fling boosting events to arrive. - time += dt; - CancelFling(time); - - // The GestureScrollBegin should be swallowed by the fling if a fling cancel - // is deferred. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // If the GestureScrollUpdate is in a different direction than the fling, - // the fling should be cancelled and scrolling should resume. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollUpdate); - gesture_.data.scroll_update.delta_x = -fling_delta.x; - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(fling_delta.x)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, NoFlingBoostIfFlingTooSlow) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - // Cancel the fling. The fling cancellation should be deferred to allow - // fling boosting events to arrive. - time += dt; - CancelFling(time); - - // If the new fling is too slow, no boosting should take place, with the new - // fling replacing the old. - WebFloatPoint small_fling_delta = WebFloatPoint(100, 0); - gesture_ = CreateFling(time, blink::kWebGestureDeviceTouchscreen, - small_fling_delta, fling_point, fling_point, 0); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Note that the new fling delta uses the *slow*, unboosted fling velocity. - time += dt; - float expected_delta = dt.InSecondsF() * -small_fling_delta.x; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, NoFlingBoostIfPreventBoostingFlagIsSet) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - - // Cancel the fling. The fling cancellation should not be deferred because of - // prevent boosting flag set. - gesture_.data.fling_cancel.prevent_boosting = true; - time += dt; - CancelFling(time); - - // VERIFY_AND_RESET_MOCKS already called by CancelFling -} - -TEST_P(InputHandlerProxyTest, FlingBoostTerminatedDuringScrollSequence) { - base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); - base::TimeTicks time = base::TimeTicks() + dt; - base::TimeTicks last_animate_time = time; - WebFloatPoint fling_delta = WebFloatPoint(1000, 0); - WebPoint fling_point = WebPoint(7, 13); - StartFling(time, blink::kWebGestureDeviceTouchscreen, fling_delta, - fling_point); - - // Now cancel the fling. The fling cancellation should be deferred to allow - // fling boosting events to arrive. - time += dt; - CancelFling(time); - - // The GestureScrollBegin should be swallowed by the fling. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // Now animate the fling to completion (in this case, the fling should - // terminate because the input handler reports a failed scroll). As the fling - // was cancelled during an active scroll sequence, a synthetic - // GestureScrollBegin should be processed, resuming the scroll. - time += dt; - float expected_delta = - (time - last_animate_time).InSecondsF() * -fling_delta.x; - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_not_scroll_)); - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)); - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); - - // Subsequent GestureScrollUpdates after the cancelled, boosted fling should - // cause scrolling as usual. - time += dt; - expected_delta = 7.3f; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollUpdate); - gesture_.data.scroll_update.delta_x = -expected_delta; - EXPECT_CALL(mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_x, - testing::Eq(expected_delta)))) - .WillOnce(testing::Return(scroll_result_did_scroll_)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - // GestureScrollEnd should terminate the resumed scroll properly. - time += dt; - gesture_.SetTimeStampSeconds(InSecondsF(time)); - gesture_.SetType(WebInputEvent::kGestureScrollEnd); - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, DidReceiveInputEvent_ForFlingTouchscreen) { - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - testing::StrictMock<MockInputHandlerProxyClientWithDidAnimateForInput> - mock_client; - input_handler_.reset( - new TestInputHandlerProxy(&mock_input_handler_, &mock_client, - touchpad_and_wheel_scroll_latching_enabled_, - async_wheel_events_enabled_)); - if (install_synchronous_handler_) { - EXPECT_CALL(mock_input_handler_, RequestUpdateForSynchronousInputHandler()) - .Times(1); - input_handler_->SetOnlySynchronouslyAnimateRootFlings( - &mock_synchronous_input_handler_); - } - mock_input_handler_.set_is_scrolling_root(synchronous_root_scroll_); - - // A GSB must be sent before a GFS. - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - gesture_.SetType(WebInputEvent::kGestureScrollBegin); - gesture_.source_device = blink::kWebGestureDeviceTouchscreen; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); - - VERIFY_AND_RESET_MOCKS(); - - gesture_.SetType(WebInputEvent::kGestureFlingStart); - WebFloatPoint fling_delta = WebFloatPoint(100, 100); - gesture_.data.fling_start.velocity_x = fling_delta.x; - gesture_.data.fling_start.velocity_y = fling_delta.y; - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - EXPECT_EQ(InputHandlerProxy::DID_HANDLE, - input_handler_->HandleInputEvent(gesture_)); - VERIFY_AND_RESET_MOCKS(); - - EXPECT_SET_NEEDS_ANIMATE_INPUT(1); - EXPECT_CALL(mock_client, DidAnimateForInput()); - base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10); - Animate(time); - - VERIFY_AND_RESET_MOCKS(); -} - TEST(SynchronousInputHandlerProxyTest, StartupShutdown) { testing::StrictMock<MockInputHandler> mock_input_handler; testing::StrictMock<MockInputHandlerProxyClient> mock_client; @@ -2712,13 +1542,15 @@ TEST_P(InputHandlerProxyTest, GestureScrollingThreadStatusHistogram) { touch_start.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); - WebGestureEvent gesture_scroll_begin; - gesture_scroll_begin.SetType(WebInputEvent::kGestureScrollBegin); - gesture_scroll_begin.source_device = blink::kWebGestureDeviceTouchscreen; + WebGestureEvent gesture_scroll_begin( + WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests(), + blink::kWebGestureDeviceTouchscreen); - WebGestureEvent gesture_scroll_end; - gesture_scroll_end.SetType(WebInputEvent::kGestureScrollEnd); - gesture_scroll_end.source_device = blink::kWebGestureDeviceTouchscreen; + WebGestureEvent gesture_scroll_end( + WebInputEvent::kGestureScrollEnd, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests(), + blink::kWebGestureDeviceTouchscreen); // Touch start with passive event listener. EXPECT_CALL( @@ -2816,13 +1648,15 @@ TEST_P(InputHandlerProxyTest, WheelScrollingThreadStatusHistogram) { WebInputEvent::kControlKey, WebInputEvent::GetStaticTimeStampForTests()); - WebGestureEvent gesture_scroll_begin; - gesture_scroll_begin.SetType(WebInputEvent::kGestureScrollBegin); - gesture_scroll_begin.source_device = blink::kWebGestureDeviceTouchpad; + WebGestureEvent gesture_scroll_begin( + WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests(), + blink::kWebGestureDeviceTouchpad); - WebGestureEvent gesture_scroll_end; - gesture_scroll_end.SetType(WebInputEvent::kGestureScrollEnd); - gesture_scroll_end.source_device = 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_, @@ -3073,12 +1907,15 @@ TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedCoalesceScrollAndPinch) { // GSUs and GPUs in one sequence should be coalesced into 1 GSU and 1 GPU. HandleGestureEvent(WebInputEvent::kGestureScrollBegin); + HandleGestureEvent(WebInputEvent::kGesturePinchBegin); HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -7); HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 2.0f, 13, 10); HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -6); + HandleGestureEvent(WebInputEvent::kGesturePinchEnd); HandleGestureEvent(WebInputEvent::kGestureScrollEnd); + HandleGestureEvent(WebInputEvent::kGestureScrollBegin); HandleGestureEvent(WebInputEvent::kGesturePinchBegin); HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 0.2f, 2, 20); HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 10.0f, 1, 10); @@ -3086,37 +1923,46 @@ TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedCoalesceScrollAndPinch) { HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 0.25f, 3, 30); HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); HandleGestureEvent(WebInputEvent::kGesturePinchEnd); + HandleGestureEvent(WebInputEvent::kGestureScrollEnd); // Only the first GSB was dispatched. - EXPECT_EQ(7ul, event_queue().size()); + EXPECT_EQ(11ul, event_queue().size()); EXPECT_EQ(1ul, event_disposition_recorder_.size()); - EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, + EXPECT_EQ(WebInputEvent::kGesturePinchBegin, event_queue()[0]->event().GetType()); + EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, + event_queue()[1]->event().GetType()); EXPECT_EQ( -35, - ToWebGestureEvent(event_queue()[0]->event()).data.scroll_update.delta_y); + ToWebGestureEvent(event_queue()[1]->event()).data.scroll_update.delta_y); EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, - event_queue()[1]->event().GetType()); + event_queue()[2]->event().GetType()); EXPECT_EQ( 2.0f, - ToWebGestureEvent(event_queue()[1]->event()).data.pinch_update.scale); + ToWebGestureEvent(event_queue()[2]->event()).data.pinch_update.scale); + EXPECT_EQ(WebInputEvent::kGesturePinchEnd, + event_queue()[3]->event().GetType()); EXPECT_EQ(WebInputEvent::kGestureScrollEnd, - event_queue()[2]->event().GetType()); + event_queue()[4]->event().GetType()); + EXPECT_EQ(WebInputEvent::kGestureScrollBegin, + event_queue()[5]->event().GetType()); EXPECT_EQ(WebInputEvent::kGesturePinchBegin, - event_queue()[3]->event().GetType()); + event_queue()[6]->event().GetType()); EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, - event_queue()[4]->event().GetType()); + event_queue()[7]->event().GetType()); EXPECT_EQ( -85, - ToWebGestureEvent(event_queue()[4]->event()).data.scroll_update.delta_y); + ToWebGestureEvent(event_queue()[7]->event()).data.scroll_update.delta_y); EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, - event_queue()[5]->event().GetType()); + event_queue()[8]->event().GetType()); EXPECT_EQ( 0.5f, - ToWebGestureEvent(event_queue()[5]->event()).data.pinch_update.scale); + ToWebGestureEvent(event_queue()[8]->event()).data.pinch_update.scale); EXPECT_EQ(WebInputEvent::kGesturePinchEnd, - event_queue()[6]->event().GetType()); + event_queue()[9]->event().GetType()); + EXPECT_EQ(WebInputEvent::kGestureScrollEnd, + event_queue()[10]->event().GetType()); testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); } @@ -3136,6 +1982,10 @@ TEST_P(InputHandlerProxyEventQueueTest, OriginalEventsTracing) { EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)) .Times(::testing::AtLeast(1)); + EXPECT_CALL(mock_input_handler_, PinchGestureBegin()); + EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(testing::_, testing::_)); + EXPECT_CALL(mock_input_handler_, PinchGestureEnd(testing::_, testing::_)); + trace_analyzer::Start("*"); // Simulate scroll. HandleGestureEvent(WebInputEvent::kGestureScrollBegin); @@ -3146,10 +1996,12 @@ TEST_P(InputHandlerProxyEventQueueTest, OriginalEventsTracing) { // Simulate scroll and pinch. HandleGestureEvent(WebInputEvent::kGestureScrollBegin); + HandleGestureEvent(WebInputEvent::kGesturePinchBegin); HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 10.0f, 1, 10); HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -10); HandleGestureEvent(WebInputEvent::kGesturePinchUpdate, 2.0f, 1, 10); HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); + HandleGestureEvent(WebInputEvent::kGesturePinchEnd); HandleGestureEvent(WebInputEvent::kGestureScrollEnd); // Dispatch all events. @@ -3167,8 +2019,8 @@ TEST_P(InputHandlerProxyEventQueueTest, OriginalEventsTracing) { trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END); analyzer->FindEvents(end_query, &end_events); - EXPECT_EQ(5ul, begin_events.size()); - EXPECT_EQ(5ul, end_events.size()); + EXPECT_EQ(7ul, begin_events.size()); + EXPECT_EQ(7ul, end_events.size()); EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, end_events[0]->GetKnownArgAsInt("type")); EXPECT_EQ(3, end_events[0]->GetKnownArgAsInt("coalesced_count")); @@ -3177,107 +2029,22 @@ TEST_P(InputHandlerProxyEventQueueTest, OriginalEventsTracing) { EXPECT_EQ(WebInputEvent::kGestureScrollBegin, end_events[2]->GetKnownArgAsInt("type")); + EXPECT_EQ(WebInputEvent::kGesturePinchBegin, + end_events[3]->GetKnownArgAsInt("type")); // Original scroll and pinch updates will be stored in the coalesced // PinchUpdate of the <ScrollUpdate, PinchUpdate> pair. // The ScrollUpdate of the pair doesn't carry original events and won't be // traced. EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, - end_events[3]->GetKnownArgAsInt("type")); - EXPECT_EQ(4, end_events[3]->GetKnownArgAsInt("coalesced_count")); - EXPECT_EQ(WebInputEvent::kGestureScrollEnd, end_events[4]->GetKnownArgAsInt("type")); + EXPECT_EQ(4, end_events[4]->GetKnownArgAsInt("coalesced_count")); + EXPECT_EQ(WebInputEvent::kGesturePinchEnd, + end_events[5]->GetKnownArgAsInt("type")); + EXPECT_EQ(WebInputEvent::kGestureScrollEnd, + end_events[6]->GetKnownArgAsInt("type")); testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); } -TEST_P(InputHandlerProxyEventQueueTest, GestureScrollFlingOrder) { - // Handle scroll on compositor. - cc::InputHandlerScrollResult scroll_result_did_scroll_; - scroll_result_did_scroll_.did_scroll = true; - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillRepeatedly(testing::Return(kImplThreadScrollState)); - EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()) - .Times(::testing::AtLeast(1)); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) - .WillRepeatedly(testing::Return(scroll_result_did_scroll_)); - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)) - .Times(::testing::AtLeast(1)); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - - // Simulate scroll. - HandleGestureEvent(WebInputEvent::kGestureScrollBegin); - HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); - HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); - HandleGestureEvent(WebInputEvent::kGestureFlingStart, -10); - - // ScrollUpdate and FlingStart should be queued. - EXPECT_EQ(2ul, event_queue().size()); - EXPECT_EQ(1ul, event_disposition_recorder_.size()); - EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, - event_queue()[0]->event().GetType()); - EXPECT_EQ(WebInputEvent::kGestureFlingStart, - event_queue()[1]->event().GetType()); - - // Dispatch events. - input_handler_proxy_->DeliverInputForBeginFrame(); - EXPECT_EQ(0ul, event_queue().size()); - EXPECT_EQ(4ul, event_disposition_recorder_.size()); - EXPECT_TRUE( - input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); - - // Send FlingCancel to stop scrolling. - HandleGestureEvent(WebInputEvent::kGestureFlingCancel); - EXPECT_EQ(1ul, event_queue().size()); - EXPECT_EQ(WebInputEvent::kGestureFlingCancel, - event_queue()[0]->event().GetType()); - input_handler_proxy_->DeliverInputForBeginFrame(); - EXPECT_EQ(0ul, event_queue().size()); - EXPECT_EQ(5ul, event_disposition_recorder_.size()); - // Should stop scrolling. Note that no ScrollEnd was sent. - EXPECT_TRUE( - !input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); -} - -TEST_P(InputHandlerProxyEventQueueTest, GestureScrollAfterFling) { - // Handle scroll on compositor. - cc::InputHandlerScrollResult scroll_result_did_scroll_; - scroll_result_did_scroll_.did_scroll = true; - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillRepeatedly(testing::Return(kImplThreadScrollState)); - EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()) - .Times(::testing::AtLeast(1)); - EXPECT_CALL( - mock_input_handler_, - ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) - .WillRepeatedly(testing::Return(scroll_result_did_scroll_)); - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, false)) - .Times(::testing::AtLeast(1)); - EXPECT_CALL(mock_input_handler_, FlingScrollBegin()) - .WillOnce(testing::Return(kImplThreadScrollState)); - - // Simulate fling. - HandleGestureEvent(WebInputEvent::kGestureScrollBegin); - HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); - HandleGestureEvent(WebInputEvent::kGestureFlingStart, -10); - HandleGestureEvent(WebInputEvent::kGestureFlingCancel); - - // Dispatch events. - input_handler_proxy_->DeliverInputForBeginFrame(); - EXPECT_EQ(0ul, event_queue().size()); - EXPECT_EQ(4ul, event_disposition_recorder_.size()); - EXPECT_FALSE( - input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); - - // New ScrollBegin should be dispatched immediately as there is no on-going - // scroll, fling or pinch. - HandleGestureEvent(WebInputEvent::kGestureScrollBegin); - EXPECT_EQ(0ul, event_queue().size()); -} - TEST_P(InputHandlerProxyEventQueueTest, TouchpadGestureScrollEndFlushQueue) { // Handle scroll on compositor. cc::InputHandlerScrollResult scroll_result_did_scroll_; diff --git a/chromium/ui/events/blink/input_scroll_elasticity_controller.h b/chromium/ui/events/blink/input_scroll_elasticity_controller.h index 6c083053b29..f5478b5ca5b 100644 --- a/chromium/ui/events/blink/input_scroll_elasticity_controller.h +++ b/chromium/ui/events/blink/input_scroll_elasticity_controller.h @@ -9,8 +9,8 @@ #include "base/memory/weak_ptr.h" #include "cc/input/overscroll_behavior.h" #include "cc/input/scroll_elasticity_helper.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" +#include "third_party/blink/public/platform/web_gesture_event.h" +#include "third_party/blink/public/platform/web_input_event.h" // InputScrollElasticityController is based on // WebKit/Source/platform/mac/ScrollElasticityController.h diff --git a/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc b/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc index 777b5c933b4..e162423b553 100644 --- a/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc +++ b/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc @@ -6,8 +6,8 @@ #include "cc/input/input_handler.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" namespace ui { namespace { @@ -93,8 +93,8 @@ class ScrollElasticityControllerTest : public testing::Test { blink::WebGestureEvent event( blink::WebInputEvent::kGestureScrollBegin, blink::WebInputEvent::kNoModifiers, - (current_time_ - base::TimeTicks()).InSecondsF()); - event.source_device = blink::kWebGestureDeviceTouchpad; + (current_time_ - base::TimeTicks()).InSecondsF(), + blink::kWebGestureDeviceTouchpad); event.data.scroll_begin.inertial_phase = static_cast<blink::WebGestureEvent::InertialPhaseState>(inertialPhase); @@ -113,8 +113,8 @@ class ScrollElasticityControllerTest : public testing::Test { blink::WebGestureEvent event( blink::WebInputEvent::kGestureScrollUpdate, blink::WebInputEvent::kNoModifiers, - (current_time_ - base::TimeTicks()).InSecondsF()); - event.source_device = blink::kWebGestureDeviceTouchpad; + (current_time_ - base::TimeTicks()).InSecondsF(), + blink::kWebGestureDeviceTouchpad); event.data.scroll_update.inertial_phase = static_cast<blink::WebGestureEvent::InertialPhaseState>(inertialPhase); event.data.scroll_update.delta_x = -event_delta.x(); @@ -134,8 +134,8 @@ class ScrollElasticityControllerTest : public testing::Test { blink::WebGestureEvent event( blink::WebInputEvent::kGestureScrollEnd, blink::WebInputEvent::kNoModifiers, - (current_time_ - base::TimeTicks()).InSecondsF()); - event.source_device = blink::kWebGestureDeviceTouchpad; + (current_time_ - base::TimeTicks()).InSecondsF(), + blink::kWebGestureDeviceTouchpad); controller_.ObserveGestureEventAndResult(event, cc::InputHandlerScrollResult()); diff --git a/chromium/ui/events/blink/snap_fling_controller.cc b/chromium/ui/events/blink/snap_fling_controller.cc new file mode 100644 index 00000000000..35ae2b96145 --- /dev/null +++ b/chromium/ui/events/blink/snap_fling_controller.cc @@ -0,0 +1,102 @@ +// 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/snap_fling_controller.h" + +#include "third_party/blink/public/platform/web_gesture_event.h" +#include "ui/events/blink/snap_fling_curve.h" + +namespace ui { + +SnapFlingController::SnapFlingController(SnapFlingClient* client) + : client_(client), state_(State::kIdle) {} + +SnapFlingController::~SnapFlingController() = default; + +bool SnapFlingController::FilterEventForSnap( + const blink::WebInputEvent& event) { + switch (event.GetType()) { + case blink::WebInputEvent::kGestureScrollBegin: { + ClearSnapFling(); + return false; + } + // TODO(sunyunjia): Need to update the existing snap curve if the GSU is + // from a fling boosting event. + case blink::WebInputEvent::kGestureScrollUpdate: + case blink::WebInputEvent::kGestureScrollEnd: { + return state_ == State::kActive || state_ == State::kFinished; + } + default: + return false; + } +} + +void SnapFlingController::ClearSnapFling() { + if (state_ == State::kActive) + client_->ScrollEndForSnapFling(); + + curve_.reset(); + state_ = State::kIdle; +} + +bool SnapFlingController::HandleGestureScrollUpdate( + const blink::WebGestureEvent& event) { + DCHECK(state_ != State::kActive && state_ != State::kFinished); + if (state_ != State::kIdle) + return false; + + if (event.data.scroll_update.inertial_phase != + blink::WebGestureEvent::kMomentumPhase) { + return false; + } + + gfx::Vector2dF event_delta(-event.data.scroll_update.delta_x, + -event.data.scroll_update.delta_y); + gfx::Vector2dF ending_displacement = + SnapFlingCurve::EstimateDisplacement(event_delta); + + gfx::Vector2dF target_offset, start_offset; + if (!client_->GetSnapFlingInfo(ending_displacement, &start_offset, + &target_offset)) { + state_ = State::kIgnored; + return false; + } + + if (start_offset == target_offset) { + state_ = State::kFinished; + return true; + } + + base::TimeTicks event_time = + base::TimeTicks() + + base::TimeDelta::FromSecondsD(event.TimeStampSeconds()); + curve_ = + std::make_unique<SnapFlingCurve>(start_offset, target_offset, event_time); + state_ = State::kActive; + Animate(event_time); + return true; +} + +void SnapFlingController::Animate(base::TimeTicks time) { + if (state_ != State::kActive) + return; + + if (curve_->IsFinished()) { + client_->ScrollEndForSnapFling(); + state_ = State::kFinished; + return; + } + gfx::Vector2dF snapped_delta = curve_->GetScrollDelta(time); + gfx::Vector2dF current_offset = client_->ScrollByForSnapFling(snapped_delta); + curve_->UpdateCurrentOffset(current_offset); + client_->RequestAnimationForSnapFling(); +} + +void SnapFlingController::SetCurveForTest( + std::unique_ptr<SnapFlingCurve> curve) { + curve_ = std::move(curve); + state_ = State::kActive; +} + +} // namespace ui
\ No newline at end of file diff --git a/chromium/ui/events/blink/snap_fling_controller.h b/chromium/ui/events/blink/snap_fling_controller.h new file mode 100644 index 00000000000..52413b75377 --- /dev/null +++ b/chromium/ui/events/blink/snap_fling_controller.h @@ -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. + +#ifndef UI_EVENTS_BLINK_SNAP_FLING_CONTROLLER_H_ +#define UI_EVENTS_BLINK_SNAP_FLING_CONTROLLER_H_ + +#include <memory> + +#include "base/time/time.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace blink { +class WebGestureEvent; +class WebInputEvent; +} // namespace blink + +namespace ui { +namespace test { +class SnapFlingControllerTest; +} + +class SnapFlingCurve; + +// A client that provides information to the controller. It also executes the +// scroll operations and requests animation frames. All the inputs and outputs +// are in the same coordinate space. +class SnapFlingClient { + public: + virtual bool GetSnapFlingInfo(const gfx::Vector2dF& natural_displacement, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset) const = 0; + virtual gfx::Vector2dF ScrollByForSnapFling(const gfx::Vector2dF& delta) = 0; + virtual void ScrollEndForSnapFling() = 0; + virtual void RequestAnimationForSnapFling() = 0; +}; + +class SnapFlingController { + public: + explicit SnapFlingController(SnapFlingClient* client); + + static std::unique_ptr<SnapFlingController> CreateForTests( + SnapFlingClient* client, + std::unique_ptr<SnapFlingCurve> curve); + + ~SnapFlingController(); + + // Returns true if the event should be consumed for snapping and should not be + // processed further. + bool FilterEventForSnap(const blink::WebInputEvent& event); + + // Creates the snap fling curve from the first inertial GSU. Returns true if + // the event if a snap fling curve has been created and the event should not + // be processed further. + bool HandleGestureScrollUpdate(const blink::WebGestureEvent& event); + + // Notifies the snap fling controller to update or end the scroll animation. + void Animate(base::TimeTicks time); + + private: + friend class test::SnapFlingControllerTest; + + enum class State { + // We haven't received an inertial GSU in this scroll sequence. + kIdle, + // We have received an inertial GSU but decided not to snap for this scroll + // sequence. + kIgnored, + // We have received an inertial GSU and decided to snap and animate it for + // this scroll sequence. So subsequent GSUs and GSE in the scroll sequence + // are consumed for snapping. + kActive, + // The animation of the snap fling has finished for this scroll sequence. + // Subsequent GSUs and GSE in the scroll sequence are ignored. + kFinished, + }; + + SnapFlingController(SnapFlingClient* client, + std::unique_ptr<SnapFlingCurve> curve); + void ClearSnapFling(); + + // Sets the |curve_| to |curve| and the |state| to |kActive|. + void SetCurveForTest(std::unique_ptr<SnapFlingCurve> curve); + + void SetActiveStateForTest() { state_ = State::kActive; } + + SnapFlingClient* client_; + State state_ = State::kIdle; + std::unique_ptr<SnapFlingCurve> curve_; + + DISALLOW_COPY_AND_ASSIGN(SnapFlingController); +}; + +} // namespace ui + +#endif // UI_EVENTS_BLINK_SNAP_FLING_CONTROLLER_H_
\ No newline at end of file diff --git a/chromium/ui/events/blink/snap_fling_controller_unittest.cc b/chromium/ui/events/blink/snap_fling_controller_unittest.cc new file mode 100644 index 00000000000..e627b093e03 --- /dev/null +++ b/chromium/ui/events/blink/snap_fling_controller_unittest.cc @@ -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. + +#include "ui/events/blink/snap_fling_controller.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/web_gesture_event.h" +#include "ui/events/blink/snap_fling_curve.h" + +namespace ui { +namespace test { +namespace { + +class MockSnapFlingClient : public SnapFlingClient { + public: + MOCK_CONST_METHOD3(GetSnapFlingInfo, + bool(const gfx::Vector2dF& natural_displacement, + gfx::Vector2dF* initial_offset, + gfx::Vector2dF* target_offset)); + MOCK_METHOD0(ScrollEndForSnapFling, void()); + MOCK_METHOD0(RequestAnimationForSnapFling, void()); + MOCK_METHOD1(ScrollByForSnapFling, gfx::Vector2dF(const gfx::Vector2dF&)); +}; + +class MockSnapFlingCurve : public SnapFlingCurve { + public: + MockSnapFlingCurve() + : SnapFlingCurve(gfx::Vector2dF(), + gfx::Vector2dF(0, 100), + base::TimeTicks()) {} + MOCK_CONST_METHOD0(IsFinished, bool()); + MOCK_METHOD1(GetScrollDelta, gfx::Vector2dF(base::TimeTicks)); +}; + +} // namespace + +class SnapFlingControllerTest : public testing::Test { + public: + SnapFlingControllerTest() { + controller_ = std::make_unique<SnapFlingController>(&mock_client_); + } + void SetCurve(std::unique_ptr<SnapFlingCurve> curve) { + controller_->SetCurveForTest(std::move(curve)); + } + void SetActiveState() { controller_->SetActiveStateForTest(); } + + protected: + testing::StrictMock<MockSnapFlingClient> mock_client_; + std::unique_ptr<SnapFlingController> controller_; +}; + +TEST_F(SnapFlingControllerTest, DoesNotFilterGSBWhenIdle) { + blink::WebGestureEvent event(blink::WebInputEvent::kGestureScrollBegin, 0, 0); + EXPECT_FALSE(controller_->FilterEventForSnap(event)); +} + +TEST_F(SnapFlingControllerTest, FiltersGSUAndGSEDependingOnState) { + blink::WebGestureEvent scroll_update( + blink::WebInputEvent::kGestureScrollUpdate, 0, 0); + blink::WebGestureEvent scroll_end(blink::WebInputEvent::kGestureScrollEnd, 0, + 0); + // Should not filter GSU and GSE if the fling is not active. + EXPECT_FALSE(controller_->FilterEventForSnap(scroll_update)); + EXPECT_FALSE(controller_->FilterEventForSnap(scroll_end)); + + // Should filter GSU and GSE if the fling is active. + SetActiveState(); + EXPECT_TRUE(controller_->FilterEventForSnap(scroll_update)); + EXPECT_TRUE(controller_->FilterEventForSnap(scroll_end)); +} + +TEST_F(SnapFlingControllerTest, CreatesAndAnimatesCurveOnFirstInertialGSU) { + blink::WebGestureEvent event(blink::WebInputEvent::kGestureScrollUpdate, 0, + 0); + event.data.scroll_update.delta_x = 0; + event.data.scroll_update.delta_y = -10; + event.data.scroll_update.inertial_phase = + blink::WebGestureEvent::kMomentumPhase; + + EXPECT_CALL(mock_client_, + GetSnapFlingInfo(testing::_, testing::_, testing::_)) + .WillOnce(DoAll(testing::SetArgPointee<1>(gfx::Vector2dF(0, 0)), + testing::SetArgPointee<2>(gfx::Vector2dF(0, 100)), + testing::Return(true))); + EXPECT_CALL(mock_client_, RequestAnimationForSnapFling()).Times(1); + EXPECT_CALL(mock_client_, ScrollByForSnapFling(testing::_)).Times(1); + EXPECT_TRUE(controller_->HandleGestureScrollUpdate(event)); + testing::Mock::VerifyAndClearExpectations(&mock_client_); +} + +TEST_F(SnapFlingControllerTest, DoesNotHandleNonInertialGSU) { + blink::WebGestureEvent event(blink::WebInputEvent::kGestureScrollUpdate, 0, + 0); + event.data.scroll_update.delta_x = 0; + event.data.scroll_update.delta_y = -10; + event.data.scroll_update.inertial_phase = + blink::WebGestureEvent::kNonMomentumPhase; + + EXPECT_CALL(mock_client_, + GetSnapFlingInfo(testing::_, testing::_, testing::_)) + .Times(0); + EXPECT_CALL(mock_client_, RequestAnimationForSnapFling()).Times(0); + EXPECT_CALL(mock_client_, ScrollByForSnapFling(testing::_)).Times(0); + EXPECT_FALSE(controller_->HandleGestureScrollUpdate(event)); + testing::Mock::VerifyAndClearExpectations(&mock_client_); +} + +TEST_F(SnapFlingControllerTest, AnimatesTheCurve) { + std::unique_ptr<MockSnapFlingCurve> mock_curve = + std::make_unique<MockSnapFlingCurve>(); + MockSnapFlingCurve* curve = mock_curve.get(); + SetCurve(std::move(mock_curve)); + + EXPECT_CALL(*curve, IsFinished()).WillOnce(testing::Return(false)); + EXPECT_CALL(*curve, GetScrollDelta(testing::_)) + .WillOnce(testing::Return(gfx::Vector2dF(100, 100))); + EXPECT_CALL(mock_client_, RequestAnimationForSnapFling()).Times(1); + EXPECT_CALL(mock_client_, ScrollByForSnapFling(gfx::Vector2dF(100, 100))); + controller_->Animate(base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + testing::Mock::VerifyAndClearExpectations(&mock_client_); + testing::Mock::VerifyAndClearExpectations(curve); +} + +TEST_F(SnapFlingControllerTest, FinishesTheCurve) { + std::unique_ptr<MockSnapFlingCurve> mock_curve = + std::make_unique<MockSnapFlingCurve>(); + MockSnapFlingCurve* curve = mock_curve.get(); + SetCurve(std::move(mock_curve)); + EXPECT_CALL(*curve, IsFinished()).WillOnce(testing::Return(true)); + EXPECT_CALL(*curve, GetScrollDelta(testing::_)).Times(0); + EXPECT_CALL(mock_client_, RequestAnimationForSnapFling()).Times(0); + EXPECT_CALL(mock_client_, ScrollByForSnapFling(testing::_)).Times(0); + EXPECT_CALL(mock_client_, ScrollEndForSnapFling()).Times(1); + controller_->Animate(base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + testing::Mock::VerifyAndClearExpectations(curve); + testing::Mock::VerifyAndClearExpectations(&mock_client_); + + EXPECT_CALL(*curve, IsFinished()).Times(0); + EXPECT_CALL(mock_client_, RequestAnimationForSnapFling()).Times(0); + EXPECT_CALL(mock_client_, ScrollByForSnapFling(testing::_)).Times(0); + EXPECT_CALL(mock_client_, ScrollEndForSnapFling()).Times(0); + controller_->Animate(base::TimeTicks() + base::TimeDelta::FromSeconds(2)); + testing::Mock::VerifyAndClearExpectations(curve); + testing::Mock::VerifyAndClearExpectations(&mock_client_); +} + +TEST_F(SnapFlingControllerTest, GSBNotFilteredAndResetsStateWhenActive) { + SetActiveState(); + blink::WebGestureEvent update_event( + blink::WebInputEvent::kGestureScrollUpdate, 0, 0); + update_event.data.scroll_update.inertial_phase = + blink::WebGestureEvent::kMomentumPhase; + EXPECT_TRUE(controller_->FilterEventForSnap(update_event)); + + EXPECT_CALL(mock_client_, ScrollEndForSnapFling()).Times(1); + blink::WebGestureEvent begin_event(blink::WebInputEvent::kGestureScrollBegin, + 0, 0); + EXPECT_FALSE(controller_->FilterEventForSnap(begin_event)); + testing::Mock::VerifyAndClearExpectations(&mock_client_); + + EXPECT_FALSE(controller_->FilterEventForSnap(update_event)); +} + +} // namespace test +} // namespace ui diff --git a/chromium/ui/events/blink/snap_fling_curve.cc b/chromium/ui/events/blink/snap_fling_curve.cc new file mode 100644 index 00000000000..f1fb6a1eb5f --- /dev/null +++ b/chromium/ui/events/blink/snap_fling_curve.cc @@ -0,0 +1,119 @@ +// 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/snap_fling_curve.h" + +#include <cmath> +#include "build/build_config.h" + +namespace ui { +namespace { + +#if defined(OS_ANDROID) +constexpr double kDistanceEstimatorScalar = 40; +// The delta to be scrolled in next frame is 0.9 of the delta in last frame. +constexpr double kRatio = 0.9; +#else +constexpr double kDistanceEstimatorScalar = 25; +// The delta to be scrolled in next frame is 0.92 of the delta in last frame. +constexpr double kRatio = 0.92; +#endif +constexpr double kMsPerFrame = 16; +constexpr base::TimeDelta kMaximumSnapDuration = + base::TimeDelta::FromSecondsD(5); + +double GetDistanceFromDisplacement(gfx::Vector2dF displacement) { + return std::hypot(displacement.x(), displacement.y()); +} + +double EstimateFramesFromDistance(double distance) { + // We approximate scroll deltas as a geometric sequence with the ratio kRatio, + // and the last scrolled delta should be less or equal than 1, yielding the + // total distance as (1 - kRatio^(-n)) / (1 - (1 / kRatio)). Solving this + // could get n as below, which is the total number of deltas in the sequence, + // and is also the total frames needed to finish the curve. + return std::ceil(-std::log(1 - distance * (1 - 1 / kRatio)) / + std::log(kRatio)); +} + +double CalculateFirstDelta(double distance, double frames) { + // distance = first_delta (1 - kRatio^(frames) / (1 - kRatio)). + // We can get the |first_delta| by solving the equation above. + return distance * (1 - kRatio) / (1 - std::pow(kRatio, frames)); +} + +} // namespace + +gfx::Vector2dF SnapFlingCurve::EstimateDisplacement( + const gfx::Vector2dF& first_delta) { + gfx::Vector2dF destination = first_delta; + destination.Scale(kDistanceEstimatorScalar); + return destination; +} + +SnapFlingCurve::SnapFlingCurve(const gfx::Vector2dF& start_offset, + const gfx::Vector2dF& target_offset, + base::TimeTicks first_gsu_time) + : start_offset_(start_offset), + total_displacement_(target_offset - start_offset), + total_distance_(GetDistanceFromDisplacement(total_displacement_)), + start_time_(first_gsu_time), + total_frames_(EstimateFramesFromDistance(total_distance_)), + first_delta_(CalculateFirstDelta(total_distance_, total_frames_)), + duration_(base::TimeDelta::FromMilliseconds(total_frames_ * kMsPerFrame)), + is_finished_(total_distance_ == 0) { + if (is_finished_) + return; + ratio_x_ = total_displacement_.x() / total_distance_; + ratio_y_ = total_displacement_.y() / total_distance_; +} + +SnapFlingCurve::~SnapFlingCurve() = default; + +double SnapFlingCurve::GetCurrentCurveDistance(base::TimeTicks time_stamp) { + double current_distance = GetDistanceFromDisplacement(current_displacement_); + base::TimeDelta current_time = time_stamp - start_time_; + + // Finishes the curve if the time elapsed is longer than |duration_|, or the + // remaining distance is less than 1. + if (current_time >= duration_ || current_distance >= total_distance_ - 1) { + return total_distance_; + } + + double current_frame = current_time.InMillisecondsF() / kMsPerFrame + 1; + double sum = + first_delta_ * (1 - std::pow(kRatio, current_frame)) / (1 - kRatio); + return sum <= total_distance_ ? sum : total_distance_; +} + +gfx::Vector2dF SnapFlingCurve::GetScrollDelta(base::TimeTicks time_stamp) { + if (is_finished_) + return gfx::Vector2dF(); + + // The the snap offset may never be reached due to clamping or other factors. + // To avoid a never ending snap curve, we force the curve to end if the time + // has passed a maximum Duration. + if (time_stamp - start_time_ > kMaximumSnapDuration) { + is_finished_ = true; + return total_displacement_ - current_displacement_; + } + + double new_distance = GetCurrentCurveDistance(time_stamp); + gfx::Vector2dF new_displacement(new_distance * ratio_x_, + new_distance * ratio_y_); + + return new_displacement - current_displacement_; +} + +void SnapFlingCurve::UpdateCurrentOffset(const gfx::Vector2dF& current_offset) { + current_displacement_ = current_offset - start_offset_; + if (current_displacement_ == total_displacement_) + is_finished_ = true; +} + +bool SnapFlingCurve::IsFinished() const { + return is_finished_; +} + +} // namespace ui diff --git a/chromium/ui/events/blink/snap_fling_curve.h b/chromium/ui/events/blink/snap_fling_curve.h new file mode 100644 index 00000000000..28d6adbcff7 --- /dev/null +++ b/chromium/ui/events/blink/snap_fling_curve.h @@ -0,0 +1,77 @@ +// 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_BLINK_SNAP_FLING_CURVE_H_ +#define UI_EVENTS_BLINK_SNAP_FLING_CURVE_H_ + +#include "base/time/time.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/vector2d_f.h" + +namespace ui { + +// The curve for the snap fling animation. The curve would generate a geometric +// sequence of deltas to be scrolled at each frame. +class SnapFlingCurve { + public: + // Creates the curve based on the start offset, target offset, and the first + // inertial GSU's time_stamp. + SnapFlingCurve(const gfx::Vector2dF& start_offset, + const gfx::Vector2dF& target_offset, + base::TimeTicks first_gsu_time); + + virtual ~SnapFlingCurve(); + + // Estimate the total distance that will be scrolled given the first GSU's + // delta + static gfx::Vector2dF EstimateDisplacement(const gfx::Vector2dF& first_delta); + + // Returns the delta that should be scrolled at |time|. + virtual gfx::Vector2dF GetScrollDelta(base::TimeTicks time); + + // Updates |current_displacement_|. This sync is necessary because the node + // might be scrolled by other calls and the scrolls might be clamped. + void UpdateCurrentOffset(const gfx::Vector2dF& current_offset); + + // Returns true if the scroll has arrived at the snap destination. + virtual bool IsFinished() const; + + base::TimeDelta duration() const { return duration_; } + + private: + // Returns the curve's current distance at |time_stamp|. + double GetCurrentCurveDistance(base::TimeTicks time_stamp); + + // The initial scroll offset of the scroller. + const gfx::Vector2dF start_offset_; + + // The total displacement to the snap position. + const gfx::Vector2dF total_displacement_; + // 1D representation of |total_displacement_|. + const double total_distance_; + + // The current displacement that has been scrolled. + gfx::Vector2dF current_displacement_; + + // The timestamp of the first GSU. + const base::TimeTicks start_time_; + + // The number of deltas in the curve's geometric sequence. + const double total_frames_; + // The first delta that defines the curve's geometric sequence. + const double first_delta_; + // The total milliseconds needed to finish the curve. + const base::TimeDelta duration_; + + bool is_finished_ = false; + + // |total_displacement_.x| / |total_distance_| + double ratio_x_; + // |total_displacement_.y| / |total_distance_| + double ratio_y_; +}; + +} // namespace ui + +#endif // UI_EVENTS_BLINK_SNAP_FLING_CURVE_H_ diff --git a/chromium/ui/events/blink/snap_fling_curve_unittest.cc b/chromium/ui/events/blink/snap_fling_curve_unittest.cc new file mode 100644 index 00000000000..2c1666e3891 --- /dev/null +++ b/chromium/ui/events/blink/snap_fling_curve_unittest.cc @@ -0,0 +1,71 @@ +// 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/snap_fling_curve.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace ui { +namespace test { + +TEST(SnapFlingCurveTest, CurveInitialization) { + SnapFlingCurve active_curve(gfx::Vector2dF(100, 100), + gfx::Vector2dF(500, 500), base::TimeTicks()); + EXPECT_FALSE(active_curve.IsFinished()); + + SnapFlingCurve finished_curve(gfx::Vector2dF(100, 100), + gfx::Vector2dF(100, 100), base::TimeTicks()); + EXPECT_TRUE(finished_curve.IsFinished()); +} + +TEST(SnapFlingCurveTest, AdvanceHalfwayThrough) { + SnapFlingCurve curve(gfx::Vector2dF(100, 100), gfx::Vector2dF(500, 500), + base::TimeTicks()); + base::TimeDelta duration = curve.duration(); + gfx::Vector2dF delta1 = + curve.GetScrollDelta(base::TimeTicks() + duration / 2); + EXPECT_LT(0, delta1.x()); + EXPECT_LT(0, delta1.y()); + EXPECT_FALSE(curve.IsFinished()); + + // Repeated offset computations at the same timestamp before applying the + // scrolled delta should yield identical results. + gfx::Vector2dF delta2 = + curve.GetScrollDelta(base::TimeTicks() + duration / 2); + EXPECT_EQ(delta1, delta2); + EXPECT_FALSE(curve.IsFinished()); + + curve.UpdateCurrentOffset(gfx::Vector2dF(100, 100) + delta1); + EXPECT_FALSE(curve.IsFinished()); +} + +TEST(SnapFlingCurveTest, AdvanceFullyThroughOnlyFinishesAfterUpdate) { + SnapFlingCurve curve(gfx::Vector2dF(100, 100), gfx::Vector2dF(500, 500), + base::TimeTicks()); + gfx::Vector2dF delta = + curve.GetScrollDelta(base::TimeTicks() + curve.duration()); + EXPECT_EQ(gfx::Vector2dF(400, 400), delta); + EXPECT_FALSE(curve.IsFinished()); + + curve.UpdateCurrentOffset(gfx::Vector2dF(500, 500)); + EXPECT_TRUE(curve.IsFinished()); +} + +TEST(SnapFlingCurveTest, ReturnsZeroAfterFinished) { + SnapFlingCurve curve(gfx::Vector2dF(100, 100), gfx::Vector2dF(500, 500), + base::TimeTicks()); + curve.UpdateCurrentOffset(gfx::Vector2dF(500, 500)); + EXPECT_TRUE(curve.IsFinished()); + + gfx::Vector2dF delta = curve.GetScrollDelta(base::TimeTicks()); + EXPECT_EQ(gfx::Vector2dF(), delta); + EXPECT_TRUE(curve.IsFinished()); + + delta = curve.GetScrollDelta(base::TimeTicks() + curve.duration()); + EXPECT_EQ(gfx::Vector2dF(), delta); + EXPECT_TRUE(curve.IsFinished()); +} + +} // namespace test +} // namespace ui
\ No newline at end of file diff --git a/chromium/ui/events/blink/web_input_event.cc b/chromium/ui/events/blink/web_input_event.cc index 94015fbaeb0..b88916cae8f 100644 --- a/chromium/ui/events/blink/web_input_event.cc +++ b/chromium/ui/events/blink/web_input_event.cc @@ -68,7 +68,7 @@ blink::WebGestureEvent MakeWebGestureEventFromUIEvent( // construct our pre-translated events. blink::WebMouseEvent MakeUntranslatedWebMouseEventFromNativeEvent( - const base::NativeEvent& native_event, + const PlatformEvent& native_event, const base::TimeTicks& time_stamp, blink::WebPointerProperties::PointerType pointer_type) { return WebMouseEventBuilder::Build( @@ -77,7 +77,7 @@ blink::WebMouseEvent MakeUntranslatedWebMouseEventFromNativeEvent( } blink::WebMouseWheelEvent MakeUntranslatedWebMouseWheelEventFromNativeEvent( - const base::NativeEvent& native_event, + const PlatformEvent& native_event, const base::TimeTicks& time_stamp, blink::WebPointerProperties::PointerType pointer_type) { return WebMouseWheelEventBuilder::Build( @@ -149,6 +149,44 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEventFromUiEvent( webkit_event.pointer_type = EventPointerTypeToWebPointerType(event.pointer_details().pointer_type); + + switch (event.scroll_event_phase()) { + case ui::ScrollEventPhase::kNone: + webkit_event.phase = blink::WebMouseWheelEvent::kPhaseNone; + break; + case ui::ScrollEventPhase::kBegan: + webkit_event.phase = blink::WebMouseWheelEvent::kPhaseBegan; + break; + case ui::ScrollEventPhase::kUpdate: + webkit_event.phase = blink::WebMouseWheelEvent::kPhaseChanged; + break; + case ui::ScrollEventPhase::kEnd: + webkit_event.phase = blink::WebMouseWheelEvent::kPhaseEnded; + break; + default: + NOTREACHED(); + } + + switch (event.momentum_phase()) { + case ui::EventMomentumPhase::NONE: + webkit_event.momentum_phase = blink::WebMouseWheelEvent::kPhaseNone; + break; + case ui::EventMomentumPhase::BEGAN: + webkit_event.momentum_phase = blink::WebMouseWheelEvent::kPhaseBegan; + break; + case ui::EventMomentumPhase::MAY_BEGIN: + webkit_event.momentum_phase = blink::WebMouseWheelEvent::kPhaseMayBegin; + break; + case ui::EventMomentumPhase::INERTIAL_UPDATE: + webkit_event.momentum_phase = blink::WebMouseWheelEvent::kPhaseChanged; + break; + case ui::EventMomentumPhase::END: + webkit_event.momentum_phase = blink::WebMouseWheelEvent::kPhaseEnded; + break; + default: + NOTREACHED(); + } + return webkit_event; } @@ -171,8 +209,8 @@ blink::WebGestureEvent MakeWebGestureEventFromUiEvent( blink::WebGestureEvent webkit_event( type, EventFlagsToWebEventModifiers(event.flags()), - EventTimeStampToSeconds(event.time_stamp())); - webkit_event.source_device = blink::kWebGestureDeviceTouchpad; + EventTimeStampToSeconds(event.time_stamp()), + blink::kWebGestureDeviceTouchpad); if (event.type() == ET_SCROLL_FLING_START) { webkit_event.data.fling_start.velocity_x = event.x_offset(); webkit_event.data.fling_start.velocity_y = event.y_offset(); @@ -193,13 +231,13 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEventFromUiEvent( // information cleanly and consistently. // // The only place where an Event's data differs from what the underlying -// base::NativeEvent would provide is position data. We would like to provide +// 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. // // The approach is to fully construct a blink::WebInputEvent from the -// Event's base::NativeEvent, and then replace the coordinate fields with +// Event's PlatformEvent, and then replace the coordinate fields with // the translated values from the Event. // // The exception is mouse events on linux. The MouseEvent contains enough @@ -312,7 +350,7 @@ blink::WebKeyboardEvent MakeWebKeyboardEvent(const KeyEvent& event) { blink::WebKeyboardEvent webkit_event = MakeWebKeyboardEventFromUiEvent(event); #if defined(OS_WIN) if (event.HasNativeEvent()) { - const base::NativeEvent& native_event = event.native_event(); + const PlatformEvent& native_event = event.native_event(); // System key events are explicitly distinguished, under Windows. webkit_event.is_system_key = native_event.message == WM_SYSCHAR || @@ -332,13 +370,11 @@ blink::WebGestureEvent MakeWebGestureEvent( screen_location_callback) { blink::WebGestureEvent gesture_event = MakeWebGestureEventFromUIEvent(event); - gesture_event.x = event.x(); - gesture_event.y = event.y(); + gesture_event.SetPositionInWidget(event.location_f()); const gfx::PointF screen_point = GetScreenLocationFromEvent(event, screen_location_callback); - gesture_event.global_x = screen_point.x(); - gesture_event.global_y = screen_point.y(); + gesture_event.SetPositionInScreen(screen_point); return gesture_event; } @@ -348,13 +384,11 @@ blink::WebGestureEvent MakeWebGestureEvent( const base::Callback<gfx::PointF(const LocatedEvent& event)>& screen_location_callback) { blink::WebGestureEvent gesture_event = MakeWebGestureEventFromUiEvent(event); - gesture_event.x = event.x(); - gesture_event.y = event.y(); + gesture_event.SetPositionInWidget(event.location_f()); const gfx::PointF screen_point = GetScreenLocationFromEvent(event, screen_location_callback); - gesture_event.global_x = screen_point.x(); - gesture_event.global_y = screen_point.y(); + gesture_event.SetPositionInScreen(screen_point); return gesture_event; } @@ -363,10 +397,9 @@ blink::WebGestureEvent MakeWebGestureEventFlingCancel() { blink::WebGestureEvent gesture_event( blink::WebInputEvent::kGestureFlingCancel, blink::WebInputEvent::kNoModifiers, - EventTimeStampToSeconds(EventTimeForNow())); - + EventTimeStampToSeconds(EventTimeForNow()), + blink::kWebGestureDeviceTouchpad); // All other fields are ignored on a GestureFlingCancel event. - gesture_event.source_device = blink::kWebGestureDeviceTouchpad; return gesture_event; } @@ -387,7 +420,7 @@ blink::WebMouseEvent MakeWebMouseEventFromUiEvent(const MouseEvent& event) { // NotifyVirtual events are created for intermediate windows that the // pointer crosses through. These occur when middle clicking. // Change these into mouse move events. - const base::NativeEvent& native_event = event.native_event(); + const PlatformEvent& native_event = event.native_event(); if (native_event && native_event->type == LeaveNotify && native_event->xcrossing.detail == NotifyVirtual) { @@ -464,6 +497,9 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEventFromUiEvent( webkit_event.delta_x = event.x_offset(); webkit_event.delta_y = event.y_offset(); + if (event.flags() & ui::EF_PRECISION_SCROLLING_DELTA) + webkit_event.has_precise_scrolling_deltas = true; + webkit_event.wheel_ticks_x = webkit_event.delta_x / MouseWheelEvent::kWheelDelta; webkit_event.wheel_ticks_y = diff --git a/chromium/ui/events/blink/web_input_event.h b/chromium/ui/events/blink/web_input_event.h index 90d553e7695..58d024527cd 100644 --- a/chromium/ui/events/blink/web_input_event.h +++ b/chromium/ui/events/blink/web_input_event.h @@ -6,11 +6,11 @@ #define UI_EVENTS_BLINK_WEB_INPUT_EVENT_H_ #include "base/callback.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebKeyboardEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" -#include "third_party/WebKit/public/platform/WebTouchEvent.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" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" +#include "third_party/blink/public/platform/web_touch_event.h" namespace ui { class GestureEvent; diff --git a/chromium/ui/events/blink/web_input_event_builders_win.h b/chromium/ui/events/blink/web_input_event_builders_win.h index 8fc5b86681c..47c0afdfa74 100644 --- a/chromium/ui/events/blink/web_input_event_builders_win.h +++ b/chromium/ui/events/blink/web_input_event_builders_win.h @@ -7,9 +7,9 @@ #include <windows.h> -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebKeyboardEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" +#include "third_party/blink/public/platform/web_keyboard_event.h" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" namespace ui { diff --git a/chromium/ui/events/blink/web_input_event_builders_win_unittest.cc b/chromium/ui/events/blink/web_input_event_builders_win_unittest.cc index 61837d0dbf7..4ea7d84aa87 100644 --- a/chromium/ui/events/blink/web_input_event_builders_win_unittest.cc +++ b/chromium/ui/events/blink/web_input_event_builders_win_unittest.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/events/blink/web_input_event_builders_win.h" #include "base/command_line.h" #include "base/win/windows_version.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" #include "ui/display/display.h" #include "ui/display/display_switches.h" -#include "ui/events/blink/web_input_event_builders_win.h" #include "ui/events/event_constants.h" using blink::WebMouseEvent; diff --git a/chromium/ui/events/blink/web_input_event_traits.cc b/chromium/ui/events/blink/web_input_event_traits.cc index 6fc2c8718dc..917d99e96cc 100644 --- a/chromium/ui/events/blink/web_input_event_traits.cc +++ b/chromium/ui/events/blink/web_input_event_traits.cc @@ -6,10 +6,10 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" -#include "third_party/WebKit/public/platform/WebKeyboardEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" -#include "third_party/WebKit/public/platform/WebTouchEvent.h" +#include "third_party/blink/public/platform/web_gesture_event.h" +#include "third_party/blink/public/platform/web_keyboard_event.h" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" +#include "third_party/blink/public/platform/web_touch_event.h" using base::StringAppendF; using base::SStringPrintf; @@ -56,14 +56,16 @@ void ApppendEventDetails(const WebMouseWheelEvent& event, std::string* result) { } void ApppendEventDetails(const WebGestureEvent& event, std::string* result) { - StringAppendF( - result, - "{\n Pos: (%d, %d)\n GlobalPos: (%d, %d)\n SourceDevice: %d\n" - " RawData: (%f, %f, %f, %f, %d)\n}", - event.x, event.y, event.global_x, event.global_y, event.source_device, - event.data.scroll_update.delta_x, event.data.scroll_update.delta_y, - event.data.scroll_update.velocity_x, event.data.scroll_update.velocity_y, - event.data.scroll_update.previous_update_in_sequence_prevented); + StringAppendF(result, + "{\n Pos: (%f, %f)\n GlobalPos: (%f, %f)\n SourceDevice: %d\n" + " RawData: (%f, %f, %f, %f, %d)\n}", + event.PositionInWidget().x, event.PositionInWidget().y, + event.PositionInScreen().x, event.PositionInScreen().y, + event.SourceDevice(), event.data.scroll_update.delta_x, + event.data.scroll_update.delta_y, + event.data.scroll_update.velocity_x, + event.data.scroll_update.velocity_y, + event.data.scroll_update.previous_update_in_sequence_prevented); } void ApppendTouchPointDetails(const WebTouchPoint& point, std::string* result) { @@ -247,10 +249,10 @@ uint32_t WebInputEventTraits::GetUniqueTouchEventId( LatencyInfo WebInputEventTraits::CreateLatencyInfoForWebGestureEvent( const WebGestureEvent& event) { SourceEventType source_event_type = SourceEventType::UNKNOWN; - if (event.source_device == + if (event.SourceDevice() == blink::WebGestureDevice::kWebGestureDeviceTouchpad) { source_event_type = SourceEventType::WHEEL; - } else if (event.source_device == + } else if (event.SourceDevice() == blink::WebGestureDevice::kWebGestureDeviceTouchscreen) { source_event_type = SourceEventType::TOUCH; } diff --git a/chromium/ui/events/blink/web_input_event_traits.h b/chromium/ui/events/blink/web_input_event_traits.h index 94b2ad14b3a..fa3a7c13ad0 100644 --- a/chromium/ui/events/blink/web_input_event_traits.h +++ b/chromium/ui/events/blink/web_input_event_traits.h @@ -5,7 +5,7 @@ #ifndef UI_EVENTS_BLINK_WEB_INPUT_EVENT_TRAITS_H_ #define UI_EVENTS_BLINK_WEB_INPUT_EVENT_TRAITS_H_ -#include "third_party/WebKit/public/platform/WebInputEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" #include "ui/latency/latency_info.h" namespace blink { diff --git a/chromium/ui/events/blink/web_input_event_traits_unittest.cc b/chromium/ui/events/blink/web_input_event_traits_unittest.cc index a904dc421d6..f3d1d1b90b7 100644 --- a/chromium/ui/events/blink/web_input_event_traits_unittest.cc +++ b/chromium/ui/events/blink/web_input_event_traits_unittest.cc @@ -5,11 +5,11 @@ #include "ui/events/blink/web_input_event_traits.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" -#include "third_party/WebKit/public/platform/WebKeyboardEvent.h" -#include "third_party/WebKit/public/platform/WebMouseWheelEvent.h" -#include "third_party/WebKit/public/platform/WebTouchEvent.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" +#include "third_party/blink/public/platform/web_mouse_wheel_event.h" +#include "third_party/blink/public/platform/web_touch_event.h" using blink::WebGestureEvent; using blink::WebInputEvent; @@ -41,8 +41,7 @@ TEST_F(WebInputEventTraitsTest, ToString) { WebGestureEvent gesture(WebInputEvent::kGesturePinchBegin, WebInputEvent::kNoModifiers, WebInputEvent::GetStaticTimeStampForTests()); - gesture.x = 1; - gesture.y = 1; + gesture.SetPositionInWidget(gfx::PointF(1, 1)); EXPECT_FALSE(WebInputEventTraits::ToString(gesture).empty()); WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, diff --git a/chromium/ui/events/chromecast/OWNERS b/chromium/ui/events/chromecast/OWNERS new file mode 100644 index 00000000000..16454c361a8 --- /dev/null +++ b/chromium/ui/events/chromecast/OWNERS @@ -0,0 +1,4 @@ +alexst@chromium.org +kpschoedel@chromium.org +rdaum@chromium.org +spang@chromium.org diff --git a/chromium/ui/events/chromecast/scroller.cc b/chromium/ui/events/chromecast/scroller.cc index 9c989a95f10..0e2e2dcb4c8 100644 --- a/chromium/ui/events/chromecast/scroller.cc +++ b/chromium/ui/events/chromecast/scroller.cc @@ -186,7 +186,7 @@ Scroller::Scroller(const Config& config) distance_(0), fling_friction_(config.fling_friction), deceleration_(ComputeDeceleration(fling_friction_)), - tuning_coeff_(ComputeDeceleration(0.84f)) { + tuning_coeff_(ComputeDeceleration(3.25f)) { } Scroller::~Scroller() { diff --git a/chromium/ui/events/cocoa/events_mac.mm b/chromium/ui/events/cocoa/events_mac.mm index a0280c53dc3..4825cb9270e 100644 --- a/chromium/ui/events/cocoa/events_mac.mm +++ b/chromium/ui/events/cocoa/events_mac.mm @@ -21,7 +21,7 @@ namespace ui { -EventType EventTypeFromNative(const base::NativeEvent& native_event) { +EventType EventTypeFromNative(const PlatformEvent& native_event) { NSEventType type = [native_event type]; switch (type) { case NSKeyDown: @@ -72,19 +72,19 @@ EventType EventTypeFromNative(const base::NativeEvent& native_event) { return ET_UNKNOWN; } -int EventFlagsFromNative(const base::NativeEvent& event) { +int EventFlagsFromNative(const PlatformEvent& event) { NSUInteger modifiers = [event modifierFlags]; return EventFlagsFromNSEventWithModifiers(event, modifiers); } -base::TimeTicks EventTimeFromNative(const base::NativeEvent& native_event) { +base::TimeTicks EventTimeFromNative(const PlatformEvent& native_event) { base::TimeTicks timestamp = ui::EventTimeStampFromSeconds([native_event timestamp]); ValidateEventTimeClock(×tamp); return timestamp; } -gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { +gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) { NSWindow* window = [native_event window]; if (!window) { NOTIMPLEMENTED(); // Point will be in screen coordinates. @@ -95,19 +95,17 @@ gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { return gfx::PointF(location.x, NSHeight(content_rect) - location.y); } -gfx::Point EventSystemLocationFromNative( - const base::NativeEvent& native_event) { +gfx::Point EventSystemLocationFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return gfx::Point(); } -int EventButtonFromNative(const base::NativeEvent& native_event) { +int EventButtonFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } -int GetChangedMouseButtonFlagsFromNative( - const base::NativeEvent& native_event) { +int GetChangedMouseButtonFlagsFromNative(const PlatformEvent& native_event) { NSEventType type = [native_event type]; switch (type) { case NSLeftMouseDown: @@ -129,11 +127,11 @@ int GetChangedMouseButtonFlagsFromNative( } PointerDetails GetMousePointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { return PointerDetails(EventPointerType::POINTER_TYPE_MOUSE); } -gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& event) { +gfx::Vector2d GetMouseWheelOffset(const PlatformEvent& event) { if ([event hasPreciseScrollingDeltas]) { // Handle continuous scrolling devices such as a Magic Mouse or a trackpad. // -scrollingDelta{X|Y} have float return types but they return values that @@ -154,25 +152,25 @@ gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& event) { } } -base::NativeEvent CopyNativeEvent(const base::NativeEvent& event) { +PlatformEvent CopyNativeEvent(const PlatformEvent& event) { return [event copy]; } -void ReleaseCopiedNativeEvent(const base::NativeEvent& event) { +void ReleaseCopiedNativeEvent(const PlatformEvent& event) { [event release]; } -void ClearTouchIdIfReleased(const base::NativeEvent& native_event) { +void ClearTouchIdIfReleased(const PlatformEvent& native_event) { NOTIMPLEMENTED(); } -int GetTouchId(const base::NativeEvent& native_event) { +int GetTouchId(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } PointerDetails GetTouchPointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { NOTIMPLEMENTED(); return PointerDetails(EventPointerType::POINTER_TYPE_UNKNOWN, /* pointer_id*/ 0, @@ -181,7 +179,7 @@ PointerDetails GetTouchPointerDetailsFromNative( /* force */ 0.f); } -bool GetScrollOffsets(const base::NativeEvent& native_event, +bool GetScrollOffsets(const PlatformEvent& native_event, float* x_offset, float* y_offset, float* x_offset_ordinal, @@ -230,7 +228,7 @@ bool GetScrollOffsets(const base::NativeEvent& native_event, return true; } -bool GetFlingData(const base::NativeEvent& native_event, +bool GetFlingData(const PlatformEvent& native_event, float* vx, float* vy, float* vx_ordinal, @@ -240,19 +238,19 @@ bool GetFlingData(const base::NativeEvent& native_event, return false; } -KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { +KeyboardCode KeyboardCodeFromNative(const PlatformEvent& native_event) { return KeyboardCodeFromNSEvent(native_event); } -DomCode CodeFromNative(const base::NativeEvent& native_event) { +DomCode CodeFromNative(const PlatformEvent& native_event) { return DomCodeFromNSEvent(native_event); } -uint32_t WindowsKeycodeFromNative(const base::NativeEvent& native_event) { +uint32_t WindowsKeycodeFromNative(const PlatformEvent& native_event) { return static_cast<uint32_t>(KeyboardCodeFromNSEvent(native_event)); } -uint16_t TextFromNative(const base::NativeEvent& native_event) { +uint16_t TextFromNative(const PlatformEvent& native_event) { NSString* text = @""; if ([native_event type] != NSFlagsChanged) text = [native_event characters]; @@ -271,7 +269,7 @@ uint16_t TextFromNative(const base::NativeEvent& native_event) { return return_value; } -uint16_t UnmodifiedTextFromNative(const base::NativeEvent& native_event) { +uint16_t UnmodifiedTextFromNative(const PlatformEvent& native_event) { NSString* text = @""; if ([native_event type] != NSFlagsChanged) text = [native_event charactersIgnoringModifiers]; @@ -290,7 +288,7 @@ uint16_t UnmodifiedTextFromNative(const base::NativeEvent& native_event) { return return_value; } -bool IsCharFromNative(const base::NativeEvent& native_event) { +bool IsCharFromNative(const PlatformEvent& native_event) { return false; } diff --git a/chromium/ui/events/devices/BUILD.gn b/chromium/ui/events/devices/BUILD.gn index 2caa514ba6c..ed92538f45f 100644 --- a/chromium/ui/events/devices/BUILD.gn +++ b/chromium/ui/events/devices/BUILD.gn @@ -44,6 +44,7 @@ jumbo_component("devices") { "//base", "//base/third_party/dynamic_annotations", "//skia", + "//ui/events:platform_event", "//ui/gfx:geometry_skia", "//ui/gfx/geometry", ] diff --git a/chromium/ui/events/devices/input_device.cc b/chromium/ui/events/devices/input_device.cc index b60f85a0a15..e85ee1ec2d2 100644 --- a/chromium/ui/events/devices/input_device.cc +++ b/chromium/ui/events/devices/input_device.cc @@ -24,12 +24,14 @@ InputDevice::InputDevice(int id, InputDeviceType type, const std::string& name) InputDevice::InputDevice(int id, InputDeviceType type, const std::string& name, + const std::string& phys, const base::FilePath& sys_path, uint16_t vendor, uint16_t product) : id(id), type(type), name(name), + phys(phys), sys_path(sys_path), vendor_id(vendor), product_id(product) {} diff --git a/chromium/ui/events/devices/input_device.h b/chromium/ui/events/devices/input_device.h index f048fcc2f90..f97154d9f49 100644 --- a/chromium/ui/events/devices/input_device.h +++ b/chromium/ui/events/devices/input_device.h @@ -30,6 +30,7 @@ struct EVENTS_DEVICES_EXPORT InputDevice { InputDevice(int id, InputDeviceType type, const std::string& name, + const std::string& phys, const base::FilePath& sys_path, uint16_t vendor, uint16_t product); @@ -45,6 +46,12 @@ struct EVENTS_DEVICES_EXPORT InputDevice { // Name of the device. std::string name; + // The physical location(port) associated with the input device. This is + // (supposed to be) stable between reboots and hotplugs. However this may not + // always be set and will change when the device is connected via a different + // port. + std::string phys; + // If the device is enabled, and whether events should be dispatched to UI. bool enabled = true; diff --git a/chromium/ui/events/devices/x11/device_data_manager_x11.h b/chromium/ui/events/devices/x11/device_data_manager_x11.h index b26132763ca..daadcb877bd 100644 --- a/chromium/ui/events/devices/x11/device_data_manager_x11.h +++ b/chromium/ui/events/devices/x11/device_data_manager_x11.h @@ -12,12 +12,12 @@ #include <set> #include <vector> -#include "base/event_types.h" #include "base/macros.h" #include "ui/events/devices/device_data_manager.h" #include "ui/events/devices/x11/events_devices_x11_export.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/events/platform_event.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_types.h" diff --git a/chromium/ui/events/event.cc b/chromium/ui/events/event.cc index 68daf98868e..d8c1fb39e49 100644 --- a/chromium/ui/events/event.cc +++ b/chromium/ui/events/event.cc @@ -179,16 +179,8 @@ SourceEventType EventTypeToLatencySourceEventType(EventType type) { return SourceEventType::UNKNOWN; } -bool IsX11SendEventTrue(const base::NativeEvent& event) { #if defined(USE_X11) - return event && event->xany.send_event; -#else - return false; -#endif -} - -#if defined(USE_X11) -bool X11EventHasNonStandardState(const base::NativeEvent& event) { +bool X11EventHasNonStandardState(const PlatformEvent& event) { const unsigned int kAllStateMask = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask | ShiftMask | @@ -198,6 +190,11 @@ bool X11EventHasNonStandardState(const base::NativeEvent& event) { } #endif +constexpr int kChangedButtonFlagMask = + ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON | + ui::EF_RIGHT_MOUSE_BUTTON | ui::EF_BACK_MOUSE_BUTTON | + ui::EF_FORWARD_MOUSE_BUTTON; + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -357,7 +354,7 @@ const TouchEvent* Event::AsTouchEvent() const { } bool Event::HasNativeEvent() const { - base::NativeEvent null_event; + PlatformEvent null_event; std::memset(&null_event, 0, sizeof(null_event)); return !!std::memcmp(&native_event_, &null_event, sizeof(null_event)); } @@ -382,7 +379,7 @@ Event::Event(EventType type, base::TimeTicks time_stamp, int flags) : type_(type), time_stamp_(time_stamp), flags_(flags), - native_event_(base::NativeEvent()), + native_event_(PlatformEvent()), delete_native_event_(false), cancelable_(true), target_(NULL), @@ -393,9 +390,7 @@ Event::Event(EventType type, base::TimeTicks time_stamp, int flags) latency()->set_source_event_type(EventTypeToLatencySourceEventType(type)); } -Event::Event(const base::NativeEvent& native_event, - EventType type, - int flags) +Event::Event(const PlatformEvent& native_event, EventType type, int flags) : type_(type), time_stamp_(EventTimeFromNative(native_event)), flags_(flags), @@ -459,7 +454,7 @@ CancelModeEvent::~CancelModeEvent() { LocatedEvent::~LocatedEvent() = default; -LocatedEvent::LocatedEvent(const base::NativeEvent& native_event) +LocatedEvent::LocatedEvent(const PlatformEvent& native_event) : Event(native_event, EventTypeFromNative(native_event), EventFlagsFromNative(native_event)), @@ -550,7 +545,7 @@ const PointerId PointerDetails::kUnknownPointerId = -1; //////////////////////////////////////////////////////////////////////////////// // MouseEvent -MouseEvent::MouseEvent(const base::NativeEvent& native_event) +MouseEvent::MouseEvent(const PlatformEvent& native_event) : LocatedEvent(native_event), changed_button_flags_(GetChangedMouseButtonFlagsFromNative(native_event)), pointer_details_(GetMousePointerDetailsFromNative(native_event)) { @@ -624,6 +619,8 @@ MouseEvent::MouseEvent(EventType type, changed_button_flags_(changed_button_flags), pointer_details_(pointer_details) { DCHECK_NE(ET_MOUSEWHEEL, type); + DCHECK_EQ(changed_button_flags_, + changed_button_flags_ & kChangedButtonFlagMask); latency()->set_source_event_type(ui::SourceEventType::MOUSE); latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0); if (this->type() == ET_MOUSE_MOVED && IsAnyButton()) @@ -635,9 +632,8 @@ MouseEvent::MouseEvent(const MouseEvent& other) = default; MouseEvent::~MouseEvent() = default; // static -bool MouseEvent::IsRepeatedClickEvent( - const MouseEvent& event1, - const MouseEvent& event2) { +bool MouseEvent::IsRepeatedClickEvent(const MouseEvent& event1, + const MouseEvent& event2) { // These values match the Windows defaults. static const int kDoubleClickTimeMS = 500; static const int kDoubleClickWidth = 4; @@ -677,7 +673,6 @@ int MouseEvent::GetRepeatCount(const MouseEvent& event) { if (event.type() == ui::ET_MOUSE_RELEASED) { if (event.changed_button_flags() == last_click_event_->changed_button_flags()) { - last_click_complete_ = true; return last_click_event_->GetClickCount(); } else { // If last_click_event_ has changed since this button was pressed @@ -685,18 +680,15 @@ int MouseEvent::GetRepeatCount(const MouseEvent& event) { return click_count; } } - if (event.time_stamp() != last_click_event_->time_stamp()) - last_click_complete_ = true; - if (!last_click_complete_ || - IsX11SendEventTrue(event.native_event())) { - click_count = last_click_event_->GetClickCount(); - } else if (IsRepeatedClickEvent(*last_click_event_, event)) { + // Return the prior click count and do not update |last_click_event_| when + // re-processing a native event, or when proccesing a reposted event. + if (event.time_stamp() == last_click_event_->time_stamp()) + return last_click_event_->GetClickCount(); + if (IsRepeatedClickEvent(*last_click_event_, event)) click_count = last_click_event_->GetClickCount() + 1; - } delete last_click_event_; } last_click_event_ = new MouseEvent(event); - last_click_complete_ = false; if (click_count > 3) click_count = 3; last_click_event_->SetClickCount(click_count); @@ -707,13 +699,11 @@ void MouseEvent::ResetLastClickForTest() { if (last_click_event_) { delete last_click_event_; last_click_event_ = NULL; - last_click_complete_ = false; } } // static MouseEvent* MouseEvent::last_click_event_ = NULL; -bool MouseEvent::last_click_complete_ = false; int MouseEvent::GetClickCount() const { if (type() != ET_MOUSE_PRESSED && type() != ET_MOUSE_RELEASED) @@ -758,10 +748,8 @@ const PointerId MouseEvent::kMousePointerId = //////////////////////////////////////////////////////////////////////////////// // MouseWheelEvent -MouseWheelEvent::MouseWheelEvent(const base::NativeEvent& native_event) - : MouseEvent(native_event), - offset_(GetMouseWheelOffset(native_event)) { -} +MouseWheelEvent::MouseWheelEvent(const PlatformEvent& native_event) + : MouseEvent(native_event), offset_(GetMouseWheelOffset(native_event)) {} MouseWheelEvent::MouseWheelEvent(const ScrollEvent& scroll_event) : MouseEvent(scroll_event), @@ -823,7 +811,7 @@ const int MouseWheelEvent::kWheelDelta = 53; //////////////////////////////////////////////////////////////////////////////// // TouchEvent -TouchEvent::TouchEvent(const base::NativeEvent& native_event) +TouchEvent::TouchEvent(const PlatformEvent& native_event) : LocatedEvent(native_event), unique_event_id_(ui::GetNextTouchEventId()), may_cause_scrolling_(false), @@ -1160,10 +1148,10 @@ bool KeyEvent::IsRepeated(const KeyEvent& event) { return false; } -KeyEvent::KeyEvent(const base::NativeEvent& native_event) +KeyEvent::KeyEvent(const PlatformEvent& native_event) : KeyEvent(native_event, EventFlagsFromNative(native_event)) {} -KeyEvent::KeyEvent(const base::NativeEvent& native_event, int event_flags) +KeyEvent::KeyEvent(const PlatformEvent& native_event, int event_flags) : Event(native_event, EventTypeFromNative(native_event), event_flags), key_code_(KeyboardCodeFromNative(native_event)), code_(CodeFromNative(native_event)), @@ -1410,14 +1398,15 @@ std::string KeyEvent::GetCodeString() const { //////////////////////////////////////////////////////////////////////////////// // ScrollEvent -ScrollEvent::ScrollEvent(const base::NativeEvent& native_event) +ScrollEvent::ScrollEvent(const PlatformEvent& native_event) : MouseEvent(native_event), x_offset_(0.0f), y_offset_(0.0f), x_offset_ordinal_(0.0f), y_offset_ordinal_(0.0f), finger_count_(0), - momentum_phase_(EventMomentumPhase::NONE) { + momentum_phase_(EventMomentumPhase::NONE), + scroll_event_phase_(ScrollEventPhase::kNone) { if (type() == ET_SCROLL) { GetScrollOffsets(native_event, &x_offset_, &y_offset_, &x_offset_ordinal_, &y_offset_ordinal_, &finger_count_, &momentum_phase_); @@ -1446,14 +1435,16 @@ ScrollEvent::ScrollEvent(EventType type, float x_offset_ordinal, float y_offset_ordinal, int finger_count, - EventMomentumPhase momentum_phase) + EventMomentumPhase momentum_phase, + ScrollEventPhase scroll_event_phase) : MouseEvent(type, location, location, time_stamp, flags, 0), x_offset_(x_offset), y_offset_(y_offset), x_offset_ordinal_(x_offset_ordinal), y_offset_ordinal_(y_offset_ordinal), finger_count_(finger_count), - momentum_phase_(momentum_phase) { + momentum_phase_(momentum_phase), + scroll_event_phase_(scroll_event_phase) { CHECK(IsScrollEvent()); latency()->set_source_event_type(ui::SourceEventType::WHEEL); } diff --git a/chromium/ui/events/event.h b/chromium/ui/events/event.h index 01fbde3ce9a..809840105b2 100644 --- a/chromium/ui/events/event.h +++ b/chromium/ui/events/event.h @@ -13,7 +13,6 @@ #include <vector> #include "base/compiler_specific.h" -#include "base/event_types.h" #include "base/gtest_prod_util.h" #include "base/logging.h" #include "base/macros.h" @@ -24,6 +23,7 @@ #include "ui/events/gestures/gesture_types.h" #include "ui/events/keycodes/dom/dom_key.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/events/platform_event.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/latency/latency_info.h" @@ -77,7 +77,7 @@ class EVENTS_EXPORT Event { DISALLOW_COPY_AND_ASSIGN(DispatcherApi); }; - const base::NativeEvent& native_event() const { return native_event_; } + const PlatformEvent& native_event() const { return native_event_; } EventType type() const { return type_; } // time_stamp represents time since machine was booted. const base::TimeTicks time_stamp() const { return time_stamp_; } @@ -308,7 +308,7 @@ class EVENTS_EXPORT Event { protected: Event(EventType type, base::TimeTicks time_stamp, int flags); - Event(const base::NativeEvent& native_event, EventType type, int flags); + Event(const PlatformEvent& native_event, EventType type, int flags); Event(const Event& copy); void SetType(EventType type); void set_cancelable(bool cancelable) { cancelable_ = cancelable; } @@ -324,7 +324,7 @@ class EVENTS_EXPORT Event { base::TimeTicks time_stamp_; LatencyInfo latency_; int flags_; - base::NativeEvent native_event_; + PlatformEvent native_event_; bool delete_native_event_; bool cancelable_; EventTarget* target_; @@ -394,7 +394,7 @@ class EVENTS_EXPORT LocatedEvent : public Event { LocatedEvent(const LocatedEvent& copy); - explicit LocatedEvent(const base::NativeEvent& native_event); + explicit LocatedEvent(const PlatformEvent& native_event); // Create a new LocatedEvent which is identical to the provided model. // If source / target windows are provided, the model location will be @@ -506,8 +506,8 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { static const PointerId kMousePointerId; // NOTE: On some platforms this will allow an event to be constructed from a - // void*, see base::NativeEvent. - explicit MouseEvent(const base::NativeEvent& native_event); + // void*, see PlatformEvent. + explicit MouseEvent(const PlatformEvent& native_event); // |pointer_event.IsMousePointerEvent()| must be true. // Note: If |pointer_event| is a mouse wheel pointer event, use the @@ -614,7 +614,7 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { const PointerDetails& pointer_details() const { return pointer_details_; } private: - FRIEND_TEST_ALL_PREFIXES(EventTest, DoubleClickRequiresRelease); + FRIEND_TEST_ALL_PREFIXES(EventTest, DoubleClickRequiresUniqueTimestamp); FRIEND_TEST_ALL_PREFIXES(EventTest, SingleClickRightLeft); // Returns the repeat count based on the previous mouse click, if it is @@ -627,13 +627,9 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { // See description above getter for details. int changed_button_flags_; + // The most recent user-generated MouseEvent, used to detect double clicks. static MouseEvent* last_click_event_; - // We can create a MouseEvent for a native event more than once. We set this - // to true when the next event either has a different timestamp or we see a - // release signalling that the press (click) event was completed. - static bool last_click_complete_; - // Structure for holding pointer details for implementing PointerEvents API. PointerDetails pointer_details_; }; @@ -645,7 +641,7 @@ class EVENTS_EXPORT MouseWheelEvent : public MouseEvent { // See |offset| for details. static const int kWheelDelta; - explicit MouseWheelEvent(const base::NativeEvent& native_event); + explicit MouseWheelEvent(const PlatformEvent& native_event); explicit MouseWheelEvent(const ScrollEvent& scroll_event); explicit MouseWheelEvent(const PointerEvent& pointer_event); MouseWheelEvent(const MouseEvent& mouse_event, int x_offset, int y_offset); @@ -683,7 +679,7 @@ class EVENTS_EXPORT MouseWheelEvent : public MouseEvent { // decided not to show hover effects for pen. class EVENTS_EXPORT TouchEvent : public LocatedEvent { public: - explicit TouchEvent(const base::NativeEvent& native_event); + explicit TouchEvent(const PlatformEvent& native_event); // |pointer_event.IsTouchPointerEvent()| must be true. explicit TouchEvent(const PointerEvent& pointer_event); @@ -836,11 +832,11 @@ class EVENTS_EXPORT KeyEvent : public Event { // 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. - explicit KeyEvent(const base::NativeEvent& native_event); + explicit KeyEvent(const PlatformEvent& native_event); // Create a KeyEvent from a NativeEvent but with mocked flags. // This method is necessary for Windows since MSG does not contain all flags. - KeyEvent(const base::NativeEvent& native_event, int event_flags); + KeyEvent(const PlatformEvent& native_event, int event_flags); // Create a keystroke event from a legacy KeyboardCode. // This should not be used in new code. @@ -995,20 +991,18 @@ class EVENTS_EXPORT KeyEvent : public Event { class EVENTS_EXPORT ScrollEvent : public MouseEvent { public: - explicit ScrollEvent(const base::NativeEvent& native_event); + explicit ScrollEvent(const PlatformEvent& native_event); template <class T> - ScrollEvent(const ScrollEvent& model, - T* source, - T* target) + ScrollEvent(const ScrollEvent& model, T* source, T* target) : MouseEvent(model, source, target), x_offset_(model.x_offset_), y_offset_(model.y_offset_), x_offset_ordinal_(model.x_offset_ordinal_), y_offset_ordinal_(model.y_offset_ordinal_), - finger_count_(model.finger_count_){ - } + finger_count_(model.finger_count_), + momentum_phase_(model.momentum_phase_), + scroll_event_phase_(model.scroll_event_phase_) {} - // Used for tests. ScrollEvent(EventType type, const gfx::Point& location, base::TimeTicks time_stamp, @@ -1018,7 +1012,8 @@ class EVENTS_EXPORT ScrollEvent : public MouseEvent { float x_offset_ordinal, float y_offset_ordinal, int finger_count, - EventMomentumPhase momentum_phase = EventMomentumPhase::NONE); + EventMomentumPhase momentum_phase = EventMomentumPhase::NONE, + ScrollEventPhase phase = ScrollEventPhase::kNone); ScrollEvent(const ScrollEvent& copy); ~ScrollEvent() override; @@ -1034,6 +1029,7 @@ class EVENTS_EXPORT ScrollEvent : public MouseEvent { float y_offset_ordinal() const { return y_offset_ordinal_; } int finger_count() const { return finger_count_; } EventMomentumPhase momentum_phase() const { return momentum_phase_; } + ScrollEventPhase scroll_event_phase() const { return scroll_event_phase_; } private: // Potential accelerated offsets. @@ -1047,7 +1043,10 @@ class EVENTS_EXPORT ScrollEvent : public MouseEvent { // For non-fling events, provides momentum information (e.g. for the case // where the device provides continuous event updates during a fling). - EventMomentumPhase momentum_phase_; + EventMomentumPhase momentum_phase_ = EventMomentumPhase::NONE; + + // Provides phase information if device can provide. + ScrollEventPhase scroll_event_phase_ = ScrollEventPhase::kNone; }; class EVENTS_EXPORT GestureEvent : public LocatedEvent { diff --git a/chromium/ui/events/event_constants.h b/chromium/ui/events/event_constants.h index 9340ec0b28f..90c04152f9a 100644 --- a/chromium/ui/events/event_constants.h +++ b/chromium/ui/events/event_constants.h @@ -139,6 +139,9 @@ enum MouseEventFlags { EF_CURSOR_HIDE = 1 << 20, // Indicates this mouse event is generated // because the cursor was just hidden. This // can be used to update hover state. + EF_PRECISION_SCROLLING_DELTA = // Indicates this mouse event is from high + 1 << 21, // precision touchpad and will come with a + // high precision delta. }; // Result of dispatching an event. @@ -165,12 +168,40 @@ enum EventPhase { EP_POSTDISPATCH }; +// Phase information used for a ScrollEvent. ScrollEventPhase is for scroll +// stream from user gesture, EventMomentumPhase is for inertia scroll stream +// after user gesture. +enum class ScrollEventPhase { + // Event has no phase information. eg. the Event is not in a scroll stream. + kNone, + + // Event is the beginning of a scroll event stream. + kBegan, + + // Event is a scroll event with phase information. + kUpdate, + + // Event is the end of the current scroll event stream. + kEnd, +}; + // Momentum phase information used for a ScrollEvent. enum class EventMomentumPhase { // Event is a non-momentum update to an event stream already begun. NONE, // Event is the beginning of an event stream that may result in momentum. + // BEGAN vs MAY_BEGIN: + // - BEGAN means we already know the inertia scroll stream must happen after + // BEGAN event. On Windows touchpad, we sent this when receive the first + // inertia scroll event or Direct Manipulation state change to INERTIA. + // - MAY_BEGIN means the inertia scroll stream may happen after MAY_BEGIN + // event. On Mac, we send this when receive releaseTouches, but we do not + // know the inertia scroll stream will happen or not at that time. + BEGAN, + + // Event maybe the beginning of an event stream that may result in momentum. + // This state used on Mac. MAY_BEGIN, // Event is an update while in a momentum phase. A "begin" event for the diff --git a/chromium/ui/events/event_rewriter_unittest.cc b/chromium/ui/events/event_rewriter_unittest.cc index d649232fbbe..ae9abcea417 100644 --- a/chromium/ui/events/event_rewriter_unittest.cc +++ b/chromium/ui/events/event_rewriter_unittest.cc @@ -11,28 +11,14 @@ #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/keycodes/keyboard_codes.h" #include "ui/events/test/test_event_processor.h" namespace ui { namespace { -// To test the handling of |EventRewriter|s through |EventSource|, -// we rewrite and test event types. -class TestEvent : public Event { - public: - explicit TestEvent(EventType type) - : Event(type, base::TimeTicks(), 0), unique_id_(next_unique_id_++) {} - ~TestEvent() override {} - int unique_id() const { return unique_id_; } - - private: - static int next_unique_id_; - int unique_id_; -}; - -int TestEvent::next_unique_id_ = 0; - // TestEventRewriteProcessor is set up with a sequence of event types, // and fails if the events received via OnEventFromSource() do not match // this sequence. These expected event types are consumed on receipt. @@ -58,6 +44,28 @@ class TestEventRewriteProcessor : public test::TestEventProcessor { DISALLOW_COPY_AND_ASSIGN(TestEventRewriteProcessor); }; +std::unique_ptr<Event> CreateEventForType(EventType type) { + switch (type) { + case ET_CANCEL_MODE: + return std::make_unique<CancelModeEvent>(); + case ET_MOUSE_DRAGGED: + case ET_MOUSE_PRESSED: + case ET_MOUSE_RELEASED: + return std::make_unique<MouseEvent>(type, gfx::Point(), gfx::Point(), + base::TimeTicks::Now(), 0, 0); + case ET_KEY_PRESSED: + case ET_KEY_RELEASED: + return std::make_unique<KeyEvent>(type, ui::VKEY_TAB, DomCode::NONE, 0); + case ET_SCROLL_FLING_CANCEL: + case ET_SCROLL_FLING_START: + return std::make_unique<ScrollEvent>( + type, gfx::Point(), base::TimeTicks::Now(), 0, 0, 0, 0, 0, 0); + default: + NOTREACHED() << type; + return nullptr; + } +} + // Trivial EventSource that does nothing but send events. class TestEventRewriteSource : public EventSource { public: @@ -65,7 +73,7 @@ class TestEventRewriteSource : public EventSource { : processor_(processor) {} EventProcessor* GetEventSink() override { return processor_; } void Send(EventType type) { - std::unique_ptr<Event> event(new TestEvent(type)); + auto event = CreateEventForType(type); SendEventToSink(event.get()); } @@ -88,7 +96,7 @@ class TestConstantEventRewriter : public EventRewriter { const Event& event, std::unique_ptr<Event>* rewritten_event) override { if (status_ == EVENT_REWRITE_REWRITTEN) - rewritten_event->reset(new TestEvent(type_)); + *rewritten_event = CreateEventForType(type_); return status_; } EventRewriteStatus NextDispatchEvent( @@ -123,10 +131,10 @@ class TestStateMachineEventRewriter : public EventRewriter { return EVENT_REWRITE_CONTINUE; if ((find->second.status == EVENT_REWRITE_REWRITTEN) || (find->second.status == EVENT_REWRITE_DISPATCH_ANOTHER)) { - last_rewritten_event_ = new TestEvent(find->second.type); - rewritten_event->reset(last_rewritten_event_); + *rewritten_event = CreateEventForType(find->second.type); + last_rewritten_event_ = rewritten_event->get(); } else { - last_rewritten_event_ = 0; + last_rewritten_event_ = nullptr; } state_ = find->second.state; return find->second.status; @@ -135,10 +143,8 @@ class TestStateMachineEventRewriter : public EventRewriter { const Event& last_event, std::unique_ptr<Event>* new_event) override { EXPECT_TRUE(last_rewritten_event_); - const TestEvent* arg_last = static_cast<const TestEvent*>(&last_event); - EXPECT_EQ(last_rewritten_event_->unique_id(), arg_last->unique_id()); - const TestEvent* arg_new = static_cast<const TestEvent*>(new_event->get()); - EXPECT_FALSE(arg_new && arg_last->unique_id() == arg_new->unique_id()); + EXPECT_EQ(last_rewritten_event_, &last_event); + EXPECT_FALSE(new_event->get() && new_event->get() == &last_event); return RewriteEvent(last_event, new_event); } @@ -151,7 +157,7 @@ class TestStateMachineEventRewriter : public EventRewriter { }; typedef std::map<RewriteCase, RewriteResult> RewriteRules; RewriteRules rules_; - TestEvent* last_rewritten_event_; + Event* last_rewritten_event_; int state_; }; diff --git a/chromium/ui/events/event_source.cc b/chromium/ui/events/event_source.cc index fcdffe5dd1b..e45be7c97b4 100644 --- a/chromium/ui/events/event_source.cc +++ b/chromium/ui/events/event_source.cc @@ -11,6 +11,17 @@ #include "ui/events/event_sink.h" namespace ui { +namespace { + +bool IsLocatedEventWithDifferentLocations(const Event& event) { + if (!event.IsLocatedEvent()) + return false; + const LocatedEvent* located_event = event.AsLocatedEvent(); + return located_event->target() && + located_event->location_f() != located_event->root_location_f(); +} + +} // namespace EventSource::EventSource() {} @@ -30,12 +41,23 @@ void EventSource::RemoveEventRewriter(EventRewriter* rewriter) { } EventDispatchDetails EventSource::SendEventToSink(Event* event) { + std::unique_ptr<ui::Event> event_for_rewriting_ptr; + Event* event_for_rewriting = event; + if (!rewriter_list_.empty() && IsLocatedEventWithDifferentLocations(*event)) { + // EventRewriters don't expect an event with differing location and + // root-location (because they don't honor the target). Provide such an + // event for rewriters. + event_for_rewriting_ptr = ui::Event::Clone(*event); + event_for_rewriting_ptr->AsLocatedEvent()->set_location_f( + event_for_rewriting_ptr->AsLocatedEvent()->root_location_f()); + event_for_rewriting = event_for_rewriting_ptr.get(); + } std::unique_ptr<Event> rewritten_event; EventRewriteStatus status = EVENT_REWRITE_CONTINUE; EventRewriterList::const_iterator it = rewriter_list_.begin(), end = rewriter_list_.end(); for (; it != end; ++it) { - status = (*it)->RewriteEvent(*event, &rewritten_event); + status = (*it)->RewriteEvent(*event_for_rewriting, &rewritten_event); if (status == EVENT_REWRITE_DISCARD) { CHECK(!rewritten_event); return EventDispatchDetails(); diff --git a/chromium/ui/events/event_target.cc b/chromium/ui/events/event_target.cc index d2bdb4be4f0..bbd1422ba28 100644 --- a/chromium/ui/events/event_target.cc +++ b/chromium/ui/events/event_target.cc @@ -22,23 +22,43 @@ void EventTarget::ConvertEventToTarget(EventTarget* target, LocatedEvent* event) { } -void EventTarget::AddPreTargetHandler(EventHandler* handler) { +void EventTarget::AddPreTargetHandler(EventHandler* handler, + Priority priority) { DCHECK(handler); - pre_target_list_.push_back(handler); -} - -void EventTarget::PrependPreTargetHandler(EventHandler* handler) { - DCHECK(handler); - pre_target_list_.insert(pre_target_list_.begin(), handler); + PrioritizedHandler prioritized = PrioritizedHandler(); + prioritized.handler = handler; + prioritized.priority = priority; + EventHandlerPriorityList::iterator it; + switch (priority) { + case Priority::kDefault: + pre_target_list_.push_back(prioritized); + return; + case Priority::kSystem: + // Find the beginning of the kSystem part of the list and prepend + // this new handler to that section. + // TODO(katie): We are adding this to the front of the list because + // previously the function PrependPreTargetHandler added items to the + // front in this way. See if we can simply put each item in the list and + // sort, or insert each item the same way, in a later change. + it = std::lower_bound(pre_target_list_.begin(), pre_target_list_.end(), + prioritized); + pre_target_list_.insert(it, prioritized); + return; + case Priority::kAccessibility: + pre_target_list_.insert(pre_target_list_.begin(), prioritized); + return; + } } void EventTarget::RemovePreTargetHandler(EventHandler* handler) { - EventHandlerList::iterator find = - std::find(pre_target_list_.begin(), - pre_target_list_.end(), - handler); - if (find != pre_target_list_.end()) - pre_target_list_.erase(find); + EventHandlerPriorityList::iterator it, end; + for (it = pre_target_list_.begin(), end = pre_target_list_.end(); it != end; + ++it) { + if (it->handler == handler) { + pre_target_list_.erase(it); + return; + } + } } void EventTarget::AddPostTargetHandler(EventHandler* handler) { @@ -65,15 +85,18 @@ EventHandler* EventTarget::SetTargetHandler(EventHandler* target_handler) { return original_target_handler; } +// TODO(katie): trigger all kAccessibility handlers in the tree first, +// then kSystem and finally kDefault, rather than doing each set per +// parent level. void EventTarget::GetPreTargetHandlers(EventHandlerList* list) { EventTarget* target = this; while (target) { - EventHandlerList::reverse_iterator it, rend; + EventHandlerPriorityList::reverse_iterator it, rend; for (it = target->pre_target_list_.rbegin(), rend = target->pre_target_list_.rend(); it != rend; ++it) { - list->insert(list->begin(), *it); + list->insert(list->begin(), it->handler); } target = target->GetParentTarget(); } diff --git a/chromium/ui/events/event_target.h b/chromium/ui/events/event_target.h index 0d7328637ac..8b136fadc8c 100644 --- a/chromium/ui/events/event_target.h +++ b/chromium/ui/events/event_target.h @@ -26,10 +26,6 @@ class EVENTS_EXPORT EventTarget { public: explicit DispatcherApi(EventTarget* target) : target_(target) {} - const EventHandlerList& pre_target_list() const { - return target_->pre_target_list_; - } - private: DispatcherApi(); EventTarget* target_; @@ -58,14 +54,26 @@ class EVENTS_EXPORT EventTarget { virtual void ConvertEventToTarget(EventTarget* target, LocatedEvent* event); + // Priority levels for PreTargetHandlers. + enum class Priority { + // The Accessibility level is the highest, and gets events before + // other priority levels. This allows accessibility features to + // modify events directly from the user. + kAccessibility, + + // System priority EventHandlers get events before default level, and + // should be used for drag and drop, menus, etc. + kSystem, + + // The default level should be used by most EventHandlers. + kDefault, + }; + // Adds a handler to receive events before the target. The handler must be // explicitly removed from the target before the handler is destroyed. The // EventTarget does not take ownership of the handler. - void AddPreTargetHandler(EventHandler* handler); - - // Same as AddPreTargetHandler except that the |handler| is added to the front - // of the list so it is the first one to receive events. - void PrependPreTargetHandler(EventHandler* handler); + void AddPreTargetHandler(EventHandler* handler, + Priority priority = Priority::kDefault); void RemovePreTargetHandler(EventHandler* handler); // Adds a handler to receive events after the target. The handler must be @@ -87,6 +95,17 @@ class EVENTS_EXPORT EventTarget { friend class EventDispatcher; friend class EventTargetTestApi; + // A handler with a priority. + struct PrioritizedHandler { + EventHandler* handler = nullptr; + Priority priority = Priority::kDefault; + + bool operator<(const PrioritizedHandler& ph) const { + return priority < ph.priority; + } + }; + using EventHandlerPriorityList = std::vector<PrioritizedHandler>; + // Returns the list of handlers that should receive the event before the // target. The handlers from the outermost target are first in the list, and // the handlers on |this| are the last in the list. @@ -97,7 +116,7 @@ class EVENTS_EXPORT EventTarget { // the handlers on |this| are the first in the list. void GetPostTargetHandlers(EventHandlerList* list); - EventHandlerList pre_target_list_; + EventHandlerPriorityList pre_target_list_; EventHandlerList post_target_list_; EventHandler* target_handler_; diff --git a/chromium/ui/events/event_target_unittest.cc b/chromium/ui/events/event_target_unittest.cc new file mode 100644 index 00000000000..4b98d5dc905 --- /dev/null +++ b/chromium/ui/events/event_target_unittest.cc @@ -0,0 +1,104 @@ +// 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/event_target.h" + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/test/events_test_utils.h" +#include "ui/events/test/test_event_handler.h" +#include "ui/events/test/test_event_processor.h" +#include "ui/events/test/test_event_target.h" + +namespace ui { + +namespace { + +TEST(EventTargetTest, AddsAndRemovesHandlers) { + test::TestEventTarget target; + EventTargetTestApi test_api(&target); + test::TestEventHandler handler; + EventHandlerList list; + + // Try at the default priority + target.AddPreTargetHandler(&handler); + list = test_api.GetPreTargetHandlers(); + ASSERT_EQ(1u, list.size()); + target.RemovePreTargetHandler(&handler); + list = test_api.GetPreTargetHandlers(); + ASSERT_EQ(0u, list.size()); + + // Try at a different priority + target.AddPreTargetHandler(&handler, EventTarget::Priority::kAccessibility); + list = test_api.GetPreTargetHandlers(); + ASSERT_EQ(1u, list.size()); + target.RemovePreTargetHandler(&handler); + list = test_api.GetPreTargetHandlers(); + ASSERT_EQ(0u, list.size()); + + // Doesn't crash if we remove a handler that doesn't exist. + target.RemovePreTargetHandler(&handler); +} + +TEST(EventTargetTest, HandlerOrdering) { + test::TestEventTarget target; + EventTargetTestApi test_api(&target); + test::TestEventHandler default_handler; + test::TestEventHandler system_handler; + test::TestEventHandler a11y_handler; + EventHandlerList list; + + // Try adding default then system then a11y, which is backwards of the + // desired order. + target.AddPreTargetHandler(&default_handler, EventTarget::Priority::kDefault); + target.AddPreTargetHandler(&system_handler, EventTarget::Priority::kSystem); + target.AddPreTargetHandler(&a11y_handler, + EventTarget::Priority::kAccessibility); + list = test_api.GetPreTargetHandlers(); + ASSERT_EQ(3u, list.size()); + EXPECT_EQ(list[0], &a11y_handler); + EXPECT_EQ(list[1], &system_handler); + EXPECT_EQ(list[2], &default_handler); +} + +TEST(EventTargetTest, HandlerOrderingComplex) { + test::TestEventTarget target; + EventTargetTestApi test_api(&target); + test::TestEventHandler default_handler_1; + test::TestEventHandler default_handler_2; + test::TestEventHandler system_handler_1; + test::TestEventHandler system_handler_2; + test::TestEventHandler system_handler_3; + test::TestEventHandler a11y_handler_1; + test::TestEventHandler a11y_handler_2; + EventHandlerList list; + + // Adding a new system or accessibility handler will insert it before others + // of its type. Adding a new default handler puts it at the end of the list, + // but this will change in a later patch set. + target.AddPreTargetHandler(&system_handler_3, EventTarget::Priority::kSystem); + target.AddPreTargetHandler(&default_handler_2, + EventTarget::Priority::kDefault); + target.AddPreTargetHandler(&system_handler_2, EventTarget::Priority::kSystem); + target.AddPreTargetHandler(&a11y_handler_2, + EventTarget::Priority::kAccessibility); + target.AddPreTargetHandler(&system_handler_1, EventTarget::Priority::kSystem); + target.AddPreTargetHandler(&default_handler_1, + EventTarget::Priority::kDefault); + target.AddPreTargetHandler(&a11y_handler_1, + EventTarget::Priority::kAccessibility); + list = test_api.GetPreTargetHandlers(); + ASSERT_EQ(7u, list.size()); + EXPECT_EQ(list[0], &a11y_handler_1); + EXPECT_EQ(list[1], &a11y_handler_2); + EXPECT_EQ(list[2], &system_handler_1); + EXPECT_EQ(list[3], &system_handler_2); + EXPECT_EQ(list[4], &system_handler_3); + EXPECT_EQ(list[5], &default_handler_2); + EXPECT_EQ(list[6], &default_handler_1); +} + +} // namespace + +} // namespace ui diff --git a/chromium/ui/events/event_unittest.cc b/chromium/ui/events/event_unittest.cc index 3702b5b1daa..aba11b95f62 100644 --- a/chromium/ui/events/event_unittest.cc +++ b/chromium/ui/events/event_unittest.cc @@ -88,112 +88,107 @@ TEST(EventTest, ClickCount) { TEST(EventTest, RepeatedClick) { const gfx::Point origin(0, 0); - MouseEvent mouse_ev1(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, - 0); - MouseEvent mouse_ev2(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, - 0); - LocatedEventTestApi test_ev1(&mouse_ev1); - LocatedEventTestApi test_ev2(&mouse_ev2); + MouseEvent event1(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0); + MouseEvent event2(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), 0, 0); + LocatedEventTestApi test_event1(&event1); + LocatedEventTestApi test_event2(&event2); base::TimeTicks start = base::TimeTicks(); base::TimeTicks soon = start + base::TimeDelta::FromMilliseconds(1); base::TimeTicks later = start + base::TimeDelta::FromMilliseconds(1000); - // Same event. - test_ev1.set_location(gfx::Point(0, 0)); - test_ev2.set_location(gfx::Point(1, 0)); - test_ev1.set_time_stamp(start); - test_ev2.set_time_stamp(start); - EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(mouse_ev1, mouse_ev2)); - MouseEvent mouse_ev3(mouse_ev1); - EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(mouse_ev1, mouse_ev3)); + // Same time stamp (likely the same native event). + test_event1.set_location(gfx::Point(0, 0)); + test_event2.set_location(gfx::Point(1, 0)); + test_event1.set_time_stamp(start); + test_event2.set_time_stamp(start); + EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, event2)); + MouseEvent mouse_ev3(event1); + EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, mouse_ev3)); // Close point. - test_ev1.set_location(gfx::Point(0, 0)); - test_ev2.set_location(gfx::Point(1, 0)); - test_ev1.set_time_stamp(start); - test_ev2.set_time_stamp(soon); - EXPECT_TRUE(MouseEvent::IsRepeatedClickEvent(mouse_ev1, mouse_ev2)); + test_event1.set_location(gfx::Point(0, 0)); + test_event2.set_location(gfx::Point(1, 0)); + test_event1.set_time_stamp(start); + test_event2.set_time_stamp(soon); + EXPECT_TRUE(MouseEvent::IsRepeatedClickEvent(event1, event2)); // Too far. - test_ev1.set_location(gfx::Point(0, 0)); - test_ev2.set_location(gfx::Point(10, 0)); - test_ev1.set_time_stamp(start); - test_ev2.set_time_stamp(soon); - EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(mouse_ev1, mouse_ev2)); + test_event1.set_location(gfx::Point(0, 0)); + test_event2.set_location(gfx::Point(10, 0)); + test_event1.set_time_stamp(start); + test_event2.set_time_stamp(soon); + EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, event2)); // Too long a time between clicks. - test_ev1.set_location(gfx::Point(0, 0)); - test_ev2.set_location(gfx::Point(0, 0)); - test_ev1.set_time_stamp(start); - test_ev2.set_time_stamp(later); - EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(mouse_ev1, mouse_ev2)); + test_event1.set_location(gfx::Point(0, 0)); + test_event2.set_location(gfx::Point(0, 0)); + test_event1.set_time_stamp(start); + test_event2.set_time_stamp(later); + EXPECT_FALSE(MouseEvent::IsRepeatedClickEvent(event1, event2)); } -// Tests that an event only increases the click count and gets marked as a -// double click if a release event was seen for the previous click. This -// prevents the same PRESSED event from being processed twice: -// http://crbug.com/389162 -TEST(EventTest, DoubleClickRequiresRelease) { - const gfx::Point origin1(0, 0); - const gfx::Point origin2(100, 0); - std::unique_ptr<MouseEvent> ev; - base::TimeTicks start = base::TimeTicks(); - base::TimeTicks soon = start + base::TimeDelta::FromMilliseconds(1); +// Tests that re-processing the same mouse press event (detected by timestamp) +// does not yield a double click event: http://crbug.com/389162 +TEST(EventTest, DoubleClickRequiresUniqueTimestamp) { + const gfx::Point point(0, 0); + base::TimeTicks time1 = base::TimeTicks(); + base::TimeTicks time2 = time1 + base::TimeDelta::FromMilliseconds(1); + + // Re-processing the same press doesn't yield a double-click. + MouseEvent event(ET_MOUSE_PRESSED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + // Processing a press with the same timestamp doesn't yield a double-click. + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + // Processing a press with a later timestamp does yield a double-click. + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time2, 0, 0); + EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); + MouseEvent::ResetLastClickForTest(); + + // Test processing a double press and release sequence with one timestamp. + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_RELEASED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_RELEASED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + MouseEvent::ResetLastClickForTest(); - ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin1, origin1, EventTimeForNow(), - 0, 0)); - ev->set_time_stamp(start); - EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev)); - ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin1, origin1, EventTimeForNow(), - 0, 0)); - ev->set_time_stamp(start); - EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev)); - - ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin2, origin2, EventTimeForNow(), - 0, 0)); - ev->set_time_stamp(start); - EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev)); - ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin2, origin2, - EventTimeForNow(), 0, 0)); - ev->set_time_stamp(start); - EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev)); - ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin2, origin2, EventTimeForNow(), - 0, 0)); - ev->set_time_stamp(soon); - EXPECT_EQ(2, MouseEvent::GetRepeatCount(*ev)); - ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin2, origin2, - EventTimeForNow(), 0, 0)); - ev->set_time_stamp(soon); - EXPECT_EQ(2, MouseEvent::GetRepeatCount(*ev)); + // Test processing a double press and release sequence with two timestamps. + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_RELEASED, point, point, time1, 0, 0); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time2, 0, 0); + EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_RELEASED, point, point, time2, 0, 0); + EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); MouseEvent::ResetLastClickForTest(); } -// Tests that clicking right and then left clicking does not generate a double -// click. +// Tests that right clicking, then left clicking does not yield double clicks. TEST(EventTest, SingleClickRightLeft) { - const gfx::Point origin(0, 0); - std::unique_ptr<MouseEvent> ev; - base::TimeTicks start = base::TimeTicks(); - base::TimeTicks soon = start + base::TimeDelta::FromMilliseconds(1); - - ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), - ui::EF_RIGHT_MOUSE_BUTTON, - ui::EF_RIGHT_MOUSE_BUTTON)); - ev->set_time_stamp(start); - EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev)); - ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - ev->set_time_stamp(start); - EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev)); - ev.reset(new MouseEvent(ET_MOUSE_RELEASED, origin, origin, EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - ev->set_time_stamp(start); - EXPECT_EQ(1, MouseEvent::GetRepeatCount(*ev)); - ev.reset(new MouseEvent(ET_MOUSE_PRESSED, origin, origin, EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - ev->set_time_stamp(soon); - EXPECT_EQ(2, MouseEvent::GetRepeatCount(*ev)); + const gfx::Point point(0, 0); + base::TimeTicks time1 = base::TimeTicks(); + base::TimeTicks time2 = time1 + base::TimeDelta::FromMilliseconds(1); + base::TimeTicks time3 = time1 + base::TimeDelta::FromMilliseconds(2); + + MouseEvent event(ET_MOUSE_PRESSED, point, point, time1, + ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_RIGHT_MOUSE_BUTTON); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time2, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_RELEASED, point, point, time2, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + EXPECT_EQ(1, MouseEvent::GetRepeatCount(event)); + event = MouseEvent(ET_MOUSE_PRESSED, point, point, time3, + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); + EXPECT_EQ(2, MouseEvent::GetRepeatCount(event)); MouseEvent::ResetLastClickForTest(); } @@ -1166,8 +1161,8 @@ const struct AltGraphEventTestCase { EF_ALTGR_DOWN}, }; -class AltGraphEventTest : public testing::TestWithParam< - std::tr1::tuple<UINT, AltGraphEventTestCase>> { +class AltGraphEventTest + : public testing::TestWithParam<std::tuple<UINT, AltGraphEventTestCase>> { public: AltGraphEventTest() : msg_({nullptr, message_type(), @@ -1193,9 +1188,9 @@ class AltGraphEventTest : public testing::TestWithParam< } protected: - UINT message_type() const { return std::tr1::get<0>(GetParam()); } + UINT message_type() const { return std::get<0>(GetParam()); } const AltGraphEventTestCase& test_case() const { - return std::tr1::get<1>(GetParam()); + return std::get<1>(GetParam()); } const MSG msg_; diff --git a/chromium/ui/events/event_utils.cc b/chromium/ui/events/event_utils.cc index aa9b26e309d..a92ebb5a669 100644 --- a/chromium/ui/events/event_utils.cc +++ b/chromium/ui/events/event_utils.cc @@ -16,7 +16,7 @@ namespace { int g_custom_event_types = ET_LAST; } // namespace -std::unique_ptr<Event> EventFromNative(const base::NativeEvent& native_event) { +std::unique_ptr<Event> EventFromNative(const PlatformEvent& native_event) { std::unique_ptr<Event> event; EventType type = EventTypeFromNative(native_event); switch(type) { @@ -103,7 +103,7 @@ display::Display::TouchSupport GetInternalDisplayTouchSupport() { return display::Display::TouchSupport::UNAVAILABLE; } -void ComputeEventLatencyOS(const base::NativeEvent& native_event) { +void ComputeEventLatencyOS(const PlatformEvent& native_event) { base::TimeTicks current_time = EventTimeForNow(); base::TimeTicks time_stamp = EventTimeFromNative(native_event); base::TimeDelta delta = current_time - time_stamp; diff --git a/chromium/ui/events/event_utils.h b/chromium/ui/events/event_utils.h index 5e1d931efea..b0672fe107a 100644 --- a/chromium/ui/events/event_utils.h +++ b/chromium/ui/events/event_utils.h @@ -9,7 +9,6 @@ #include <memory> -#include "base/event_types.h" #include "base/strings/string16.h" #include "build/build_config.h" #include "ui/display/display.h" @@ -18,6 +17,7 @@ #include "ui/events/event_constants.h" #include "ui/events/events_export.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/events/platform_event.h" #include "ui/gfx/native_widget_types.h" #if defined(OS_WIN) @@ -43,20 +43,19 @@ enum class DomCode; // Returns a ui::Event wrapping a native event. Ownership of the returned value // is transferred to the caller. EVENTS_EXPORT std::unique_ptr<Event> EventFromNative( - const base::NativeEvent& native_event); + const PlatformEvent& native_event); // Get the EventType from a native event. -EVENTS_EXPORT EventType EventTypeFromNative( - const base::NativeEvent& native_event); +EVENTS_EXPORT EventType EventTypeFromNative(const PlatformEvent& native_event); // Get the EventFlags from a native event. -EVENTS_EXPORT int EventFlagsFromNative(const base::NativeEvent& native_event); +EVENTS_EXPORT int EventFlagsFromNative(const PlatformEvent& native_event); // Get the timestamp from a native event. // Note: This is not a pure function meaning that multiple applications on the // same native event may return different values. EVENTS_EXPORT base::TimeTicks EventTimeFromNative( - const base::NativeEvent& native_event); + const PlatformEvent& native_event); // Ensures that the event timestamp values are coming from the same underlying // monotonic clock as base::TimeTicks::Now() and if it is not then falls @@ -68,67 +67,64 @@ EVENTS_EXPORT void ValidateEventTimeClock(base::TimeTicks* timestamp); // this "root window" and how it maps to platform-specific drawing surfaces is // defined in ui/aura/root_window.* and ui/aura/window_tree_host*. EVENTS_EXPORT gfx::PointF EventLocationFromNative( - const base::NativeEvent& native_event); + const PlatformEvent& native_event); // Gets the location in native system coordinate space. EVENTS_EXPORT gfx::Point EventSystemLocationFromNative( - const base::NativeEvent& native_event); + const PlatformEvent& native_event); #if defined(USE_X11) // Returns the 'real' button for an event. The button reported in slave events // does not take into account any remapping (e.g. using xmodmap), while the // button reported in master events do. This is a utility function to always // return the mapped button. -EVENTS_EXPORT int EventButtonFromNative(const base::NativeEvent& native_event); +EVENTS_EXPORT int EventButtonFromNative(const PlatformEvent& native_event); #endif // Returns the KeyboardCode from a native event. -EVENTS_EXPORT KeyboardCode KeyboardCodeFromNative( - const base::NativeEvent& native_event); +EVENTS_EXPORT KeyboardCode +KeyboardCodeFromNative(const PlatformEvent& native_event); // Returns the DOM KeyboardEvent code (physical location in the // keyboard) from a native event. -EVENTS_EXPORT DomCode CodeFromNative(const base::NativeEvent& native_event); +EVENTS_EXPORT DomCode CodeFromNative(const PlatformEvent& native_event); // Returns true if the keyboard event is a character event rather than // a keystroke event. -EVENTS_EXPORT bool IsCharFromNative(const base::NativeEvent& native_event); +EVENTS_EXPORT bool IsCharFromNative(const PlatformEvent& native_event); // Returns the flags of the button that changed during a press/release. EVENTS_EXPORT int GetChangedMouseButtonFlagsFromNative( - const base::NativeEvent& native_event); + const PlatformEvent& native_event); // Returns the detailed pointer information for mouse events. -EVENTS_EXPORT PointerDetails GetMousePointerDetailsFromNative( - const base::NativeEvent& native_event); +EVENTS_EXPORT PointerDetails +GetMousePointerDetailsFromNative(const PlatformEvent& native_event); // Gets the mouse wheel offsets from a native event. EVENTS_EXPORT gfx::Vector2d GetMouseWheelOffset( - const base::NativeEvent& native_event); + const PlatformEvent& native_event); // Returns a copy of |native_event|. Depending on the platform, this copy may // need to be deleted with ReleaseCopiedNativeEvent(). -base::NativeEvent CopyNativeEvent( - const base::NativeEvent& native_event); +PlatformEvent CopyNativeEvent(const PlatformEvent& native_event); // Delete a |native_event| previously created by CopyNativeEvent(). -void ReleaseCopiedNativeEvent( - const base::NativeEvent& native_event); +void ReleaseCopiedNativeEvent(const PlatformEvent& native_event); // Returns the detailed pointer information for touch events. EVENTS_EXPORT PointerDetails -GetTouchPointerDetailsFromNative(const base::NativeEvent& native_event); +GetTouchPointerDetailsFromNative(const PlatformEvent& native_event); // Gets the touch id from a native event. -EVENTS_EXPORT int GetTouchId(const base::NativeEvent& native_event); +EVENTS_EXPORT int GetTouchId(const PlatformEvent& native_event); // Clear the touch id from bookkeeping if it is a release/cancel event. -EVENTS_EXPORT void ClearTouchIdIfReleased( - const base::NativeEvent& native_event); +EVENTS_EXPORT void ClearTouchIdIfReleased(const PlatformEvent& native_event); // Gets the fling velocity from a native event. is_cancel is set to true if // this was a tap down, intended to stop an ongoing fling. -EVENTS_EXPORT bool GetFlingData(const base::NativeEvent& native_event, +EVENTS_EXPORT bool GetFlingData(const PlatformEvent& native_event, float* vx, float* vy, float* vx_ordinal, @@ -137,7 +133,7 @@ EVENTS_EXPORT bool GetFlingData(const base::NativeEvent& native_event, // Returns whether this is a scroll event and optionally gets the amount to be // scrolled. |x_offset|, |y_offset| and |finger_count| can be NULL. -EVENTS_EXPORT bool GetScrollOffsets(const base::NativeEvent& native_event, +EVENTS_EXPORT bool GetScrollOffsets(const PlatformEvent& native_event, float* x_offset, float* y_offset, float* x_offset_ordinal, @@ -151,7 +147,7 @@ EVENTS_EXPORT bool ShouldDefaultToNaturalScroll(); // Returns whether or not the internal display produces touch events. EVENTS_EXPORT display::Display::TouchSupport GetInternalDisplayTouchSupport(); -EVENTS_EXPORT void ComputeEventLatencyOS(const base::NativeEvent& native_event); +EVENTS_EXPORT void ComputeEventLatencyOS(const PlatformEvent& native_event); #if defined(OS_WIN) EVENTS_EXPORT int GetModifiersFromKeyState(); diff --git a/chromium/ui/events/events_default.cc b/chromium/ui/events/events_default.cc index 665e21d67d4..f293ed60bd1 100644 --- a/chromium/ui/events/events_default.cc +++ b/chromium/ui/events/events_default.cc @@ -8,23 +8,22 @@ namespace ui { -base::TimeTicks EventTimeFromNative(const base::NativeEvent& native_event) { +base::TimeTicks EventTimeFromNative(const PlatformEvent& native_event) { const ui::Event* event = static_cast<const ui::Event*>(native_event); return event->time_stamp(); } -int EventFlagsFromNative(const base::NativeEvent& native_event) { +int EventFlagsFromNative(const PlatformEvent& native_event) { const ui::Event* event = static_cast<const ui::Event*>(native_event); return event->flags(); } -EventType EventTypeFromNative(const base::NativeEvent& native_event) { +EventType EventTypeFromNative(const PlatformEvent& native_event) { const ui::Event* event = static_cast<const ui::Event*>(native_event); return event->type(); } -gfx::Point EventSystemLocationFromNative( - const base::NativeEvent& native_event) { +gfx::Point EventSystemLocationFromNative(const PlatformEvent& native_event) { const ui::LocatedEvent* e = static_cast<const ui::LocatedEvent*>(native_event); DCHECK(e->IsMouseEvent() || e->IsTouchEvent() || e->IsGestureEvent() || @@ -32,7 +31,7 @@ gfx::Point EventSystemLocationFromNative( return e->location(); } -gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { +gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) { const ui::LocatedEvent* e = static_cast<const ui::LocatedEvent*>(native_event); DCHECK(e->IsMouseEvent() || e->IsTouchEvent() || e->IsGestureEvent() || @@ -40,8 +39,7 @@ gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { return e->location_f(); } -int GetChangedMouseButtonFlagsFromNative( - const base::NativeEvent& native_event) { +int GetChangedMouseButtonFlagsFromNative(const PlatformEvent& native_event) { const ui::MouseEvent* event = static_cast<const ui::MouseEvent*>(native_event); DCHECK(event->IsMouseEvent() || event->IsScrollEvent()); @@ -49,7 +47,7 @@ int GetChangedMouseButtonFlagsFromNative( } PointerDetails GetMousePointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { const ui::MouseEvent* event = static_cast<const ui::MouseEvent*>(native_event); DCHECK(event->IsMouseEvent() || event->IsScrollEvent()); @@ -58,43 +56,41 @@ PointerDetails GetMousePointerDetailsFromNative( return pointer_detail; } -KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { +KeyboardCode KeyboardCodeFromNative(const PlatformEvent& native_event) { const ui::KeyEvent* event = static_cast<const ui::KeyEvent*>(native_event); DCHECK(event->IsKeyEvent()); return event->key_code(); } -DomCode CodeFromNative(const base::NativeEvent& native_event) { +DomCode CodeFromNative(const PlatformEvent& native_event) { const ui::KeyEvent* event = static_cast<const ui::KeyEvent*>(native_event); DCHECK(event->IsKeyEvent()); return event->code(); } -bool IsCharFromNative(const base::NativeEvent& native_event) { +bool IsCharFromNative(const PlatformEvent& native_event) { const ui::KeyEvent* event = static_cast<const ui::KeyEvent*>(native_event); DCHECK(event->IsKeyEvent()); return event->is_char(); } -gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) { +gfx::Vector2d GetMouseWheelOffset(const PlatformEvent& native_event) { const ui::MouseWheelEvent* event = static_cast<const ui::MouseWheelEvent*>(native_event); DCHECK(event->type() == ET_MOUSEWHEEL); return event->offset(); } -base::NativeEvent CopyNativeEvent(const base::NativeEvent& event) { +PlatformEvent CopyNativeEvent(const PlatformEvent& event) { return NULL; } -void ReleaseCopiedNativeEvent(const base::NativeEvent& event) { -} +void ReleaseCopiedNativeEvent(const PlatformEvent& event) {} -void ClearTouchIdIfReleased(const base::NativeEvent& xev) { -} +void ClearTouchIdIfReleased(const PlatformEvent& xev) {} // TODO(687724): Will remove all GetTouchId functions. -int GetTouchId(const base::NativeEvent& native_event) { +int GetTouchId(const PlatformEvent& native_event) { const ui::TouchEvent* event = static_cast<const ui::TouchEvent*>(native_event); DCHECK(event->IsTouchEvent()); @@ -102,14 +98,14 @@ int GetTouchId(const base::NativeEvent& native_event) { } PointerDetails GetTouchPointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { const ui::TouchEvent* event = static_cast<const ui::TouchEvent*>(native_event); DCHECK(event->IsTouchEvent()); return event->pointer_details(); } -bool GetScrollOffsets(const base::NativeEvent& native_event, +bool GetScrollOffsets(const PlatformEvent& native_event, float* x_offset, float* y_offset, float* x_offset_ordinal, @@ -135,7 +131,7 @@ bool GetScrollOffsets(const base::NativeEvent& native_event, return true; } -bool GetFlingData(const base::NativeEvent& native_event, +bool GetFlingData(const PlatformEvent& native_event, float* vx, float* vy, float* vx_ordinal, diff --git a/chromium/ui/events/events_stub.cc b/chromium/ui/events/events_stub.cc index 26fc632d9d2..b9529520cb9 100644 --- a/chromium/ui/events/events_stub.cc +++ b/chromium/ui/events/events_stub.cc @@ -17,73 +17,69 @@ namespace ui { // Stub implementations of platform-specific methods in events_util.h, built // on platforms that currently do not have a complete implementation of events. -EventType EventTypeFromNative(const base::NativeEvent& native_event) { +EventType EventTypeFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return ET_UNKNOWN; } -int EventFlagsFromNative(const base::NativeEvent& native_event) { +int EventFlagsFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } -base::TimeTicks EventTimeFromNative(const base::NativeEvent& native_event) { +base::TimeTicks EventTimeFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return base::TimeTicks(); } -gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { +gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return gfx::PointF(); } -gfx::Point EventSystemLocationFromNative( - const base::NativeEvent& native_event) { +gfx::Point EventSystemLocationFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return gfx::Point(); } -int EventButtonFromNative(const base::NativeEvent& native_event) { +int EventButtonFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } -int GetChangedMouseButtonFlagsFromNative( - const base::NativeEvent& native_event) { +int GetChangedMouseButtonFlagsFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } PointerDetails GetMousePointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { return PointerDetails(EventPointerType::POINTER_TYPE_MOUSE); } -gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) { +gfx::Vector2d GetMouseWheelOffset(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return gfx::Vector2d(); } -base::NativeEvent CopyNativeEvent(const base::NativeEvent& event) { - NOTIMPLEMENTED() << - "Don't know how to copy base::NativeEvent for this platform"; +PlatformEvent CopyNativeEvent(const PlatformEvent& event) { + NOTIMPLEMENTED() << "Don't know how to copy PlatformEvent for this platform"; return NULL; } -void ReleaseCopiedNativeEvent(const base::NativeEvent& event) { -} +void ReleaseCopiedNativeEvent(const PlatformEvent& event) {} -void ClearTouchIdIfReleased(const base::NativeEvent& native_event) { +void ClearTouchIdIfReleased(const PlatformEvent& native_event) { NOTIMPLEMENTED(); } -int GetTouchId(const base::NativeEvent& native_event) { +int GetTouchId(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } PointerDetails GetTouchPointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { NOTIMPLEMENTED(); return PointerDetails(EventPointerType::POINTER_TYPE_UNKNOWN, /* radius_x */ 1.0, @@ -94,7 +90,7 @@ PointerDetails GetTouchPointerDetailsFromNative( /* tilt_y */ 0.f); } -bool GetScrollOffsets(const base::NativeEvent& native_event, +bool GetScrollOffsets(const PlatformEvent& native_event, float* x_offset, float* y_offset, float* x_offset_ordinal, @@ -105,7 +101,7 @@ bool GetScrollOffsets(const base::NativeEvent& native_event, return false; } -bool GetFlingData(const base::NativeEvent& native_event, +bool GetFlingData(const PlatformEvent& native_event, float* vx, float* vy, float* vx_ordinal, @@ -115,32 +111,32 @@ bool GetFlingData(const base::NativeEvent& native_event, return false; } -KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { +KeyboardCode KeyboardCodeFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return static_cast<KeyboardCode>(0); } -DomCode CodeFromNative(const base::NativeEvent& native_event) { +DomCode CodeFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return DomCode::NONE; } -bool IsCharFromNative(const base::NativeEvent& native_event) { +bool IsCharFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return false; } -uint32_t WindowsKeycodeFromNative(const base::NativeEvent& native_event) { +uint32_t WindowsKeycodeFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } -uint16_t TextFromNative(const base::NativeEvent& native_event) { +uint16_t TextFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } -uint16_t UnmodifiedTextFromNative(const base::NativeEvent& native_event) { +uint16_t UnmodifiedTextFromNative(const PlatformEvent& native_event) { NOTIMPLEMENTED(); return 0; } diff --git a/chromium/ui/events/gestures/blink/BUILD.gn b/chromium/ui/events/gestures/blink/BUILD.gn index e52f4a462ca..5ba80ccac1a 100644 --- a/chromium/ui/events/gestures/blink/BUILD.gn +++ b/chromium/ui/events/gestures/blink/BUILD.gn @@ -10,7 +10,7 @@ source_set("blink") { deps = [ "//base", - "//third_party/WebKit/public:blink_headers", + "//third_party/blink/public:blink_headers", "//ui/events", "//ui/events:gesture_detection", "//ui/gfx/geometry", diff --git a/chromium/ui/events/gestures/blink/DEPS b/chromium/ui/events/gestures/blink/DEPS index 8d8323e9688..61b00b07eac 100644 --- a/chromium/ui/events/gestures/blink/DEPS +++ b/chromium/ui/events/gestures/blink/DEPS @@ -1,6 +1,6 @@ include_rules = [ - "+third_party/WebKit/public/platform/WebFloatSize.h", - "+third_party/WebKit/public/platform/WebGestureCurve.h", - "+third_party/WebKit/public/platform/WebGestureDevice.h", - "+third_party/WebKit/public/platform/WebGestureCurveTarget.h" + "+third_party/blink/public/platform/web_float_size.h", + "+third_party/blink/public/platform/web_gesture_curve.h", + "+third_party/blink/public/platform/web_gesture_device.h", + "+third_party/blink/public/platform/web_gesture_curve_target.h" ] diff --git a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc index b2aec0cbeb1..cc8d67ea2e3 100644 --- a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc +++ b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc @@ -9,11 +9,10 @@ #include <utility> #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "build/build_config.h" -#include "third_party/WebKit/public/platform/WebFloatSize.h" -#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h" +#include "third_party/blink/public/platform/web_float_size.h" +#include "third_party/blink/public/platform/web_gesture_curve_target.h" #include "ui/events/gestures/fixed_velocity_curve.h" #include "ui/events/gestures/fling_curve.h" #include "ui/gfx/geometry/safe_integer_conversions.h" diff --git a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h index 4480fdcfa43..ede9a036d52 100644 --- a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h +++ b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h @@ -11,8 +11,8 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "third_party/WebKit/public/platform/WebGestureCurve.h" -#include "third_party/WebKit/public/platform/WebGestureDevice.h" +#include "third_party/blink/public/platform/web_gesture_curve.h" +#include "third_party/blink/public/platform/web_gesture_device.h" #include "ui/gfx/geometry/vector2d_f.h" namespace ui { diff --git a/chromium/ui/events/gestures/blink/web_gesture_curve_impl_unittest.cc b/chromium/ui/events/gestures/blink/web_gesture_curve_impl_unittest.cc index c5c6cfa6e8f..63b56a427d9 100644 --- a/chromium/ui/events/gestures/blink/web_gesture_curve_impl_unittest.cc +++ b/chromium/ui/events/gestures/blink/web_gesture_curve_impl_unittest.cc @@ -7,9 +7,9 @@ #include <memory> #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/WebKit/public/platform/WebFloatSize.h" -#include "third_party/WebKit/public/platform/WebGestureCurve.h" -#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h" +#include "third_party/blink/public/platform/web_float_size.h" +#include "third_party/blink/public/platform/web_gesture_curve.h" +#include "third_party/blink/public/platform/web_gesture_curve_target.h" #include "ui/events/gestures/fling_curve.h" using blink::WebFloatSize; diff --git a/chromium/ui/events/keyboard_hook.h b/chromium/ui/events/keyboard_hook.h index 32f05492772..ea99e978353 100644 --- a/chromium/ui/events/keyboard_hook.h +++ b/chromium/ui/events/keyboard_hook.h @@ -33,6 +33,9 @@ class EVENTS_EXPORT KeyboardHook { static std::unique_ptr<KeyboardHook> Create( base::Optional<base::flat_set<int>> native_key_codes, KeyEventCallback callback); + + // True if |native_key_code| is reserved for an active KeyboardLock request. + virtual bool IsKeyLocked(int native_key_code) = 0; }; } // namespace ui diff --git a/chromium/ui/events/keyboard_hook_base.cc b/chromium/ui/events/keyboard_hook_base.cc index d9a7938ff30..ae0e785a7bf 100644 --- a/chromium/ui/events/keyboard_hook_base.cc +++ b/chromium/ui/events/keyboard_hook_base.cc @@ -22,6 +22,10 @@ KeyboardHookBase::KeyboardHookBase( KeyboardHookBase::~KeyboardHookBase() = default; +bool KeyboardHookBase::IsKeyLocked(int native_key_code) { + return ShouldCaptureKeyEvent(native_key_code); +} + bool KeyboardHookBase::ShouldCaptureKeyEvent(int key_code) const { return !key_codes_ || base::ContainsKey(key_codes_.value(), key_code); } diff --git a/chromium/ui/events/keyboard_hook_base.h b/chromium/ui/events/keyboard_hook_base.h index 428c24fa077..c0aa26ab155 100644 --- a/chromium/ui/events/keyboard_hook_base.h +++ b/chromium/ui/events/keyboard_hook_base.h @@ -20,6 +20,9 @@ class KeyboardHookBase : public KeyboardHook { KeyEventCallback callback); ~KeyboardHookBase() override; + // KeyboardHook implementation. + bool IsKeyLocked(int native_key_code) override; + protected: // Indicates whether |key_code| should be intercepted by the keyboard hook. bool ShouldCaptureKeyEvent(int key_code) const; diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc b/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc index ce35f7508dc..34c5ff80e1b 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_x.cc @@ -25,9 +25,83 @@ namespace ui { namespace { // MAP0 - MAP3: -// These are the generated VKEY code maps for all possible Latin keyboard -// layouts in Windows. And the maps are only for special letter keys excluding -// [a-z] & [0-9]. +// These are the generated VKEY code maps from these layout datas on Windows: +// KBDA1.DLL +// KBDAL.DLL +// KBDARME.DLL +// KBDARMW.DLL +// KBDAZEL.DLL +// KBDBE.DLL +// KBDBENE.DLL +// KBDBR.DLL +// KBDCA.DLL +// KBDCAN.DLL +// KBDCR.DLL +// KBDCZ.DLL +// KBDCZ1.DLL +// KBDCZ2.DLL +// KBDDA.DLL +// KBDDV.DLL +// KBDES.DLL +// KBDEST.DLL +// KBDFC.DLL +// KBDFI.DLL +// KBDFI1.DLL +// KBDFO.DLL +// KBDFR.DLL +// KBDHEB.DLL +// KBDGAE.DLL +// KBDGEO.DLL +// kbdgeoer.dll +// kbdgeoqw.dll +// KBDGR.DLL +// KBDGR1.DLL +// KBDGRLND.DLL +// KBDHAU.DLL +// KBDHU.DLL +// KBDHU1.DLL +// KBDIBO.DLL +// KBDIC.DLL +// KBDIR.DLL +// KBDIT.DLL +// KBDIT142.DLL +// KBDLA.DLL +// KBDLT.DLL +// KBDLT1.DLL +// KBDLT2.DLL +// KBDLV.DLL +// KBDLV1.DLL +// KBDMLT47.DLL +// KBDMLT48.DLL +// KBDNE.DLL +// KBDNO.DLL +// KBDNO1.DLL +// KBDPL.DLL +// KBDPL1.DLL +// KBDPO.DLL +// KBDRO.DLL +// KBDROPR.DLL +// KBDROST.DLL +// KBDRU.DLL +// KBDSF.DLL +// KBDSG.DLL +// KBDSL.DLL +// KBDSL1.DLL +// KBDSOREX.DLL +// KBDSORS1.DLL +// KBDSORST.DLL +// KBDSP.DLL +// KBDSW.DLL +// KBDSW09.DLL +// KBDUK.DLL +// KBDUKX.DLL +// KBDUS.DLL +// KBDUSL.DLL +// KBDUSR.DLL +// KBDUSX.DLL +// KBDVNTC.DLL +// KBDYBA.DLL +// And the maps are only for special letter keys excluding [a-z] & [0-9]. // // ch0: the keysym without modifier states. // ch1: the keysym with shift state. @@ -122,179 +196,183 @@ const struct MAP1 { return m1.ch0 < m2.ch0; } } map1[] = { - {0x0021, 0x0A, 0x31}, // XK_exclam+AE01: VKEY_1 - {0x0021, 0x11, 0x38}, // XK_exclam+AE08: VKEY_8 - {0x0021, 0x3D, 0xDF}, // XK_exclam+AB10: VKEY_OEM_8 - {0x0022, 0x0B, 0x32}, // XK_quotedbl+AE02: VKEY_2 - {0x0022, 0x0C, 0x33}, // XK_quotedbl+AE03: VKEY_3 - {0x0023, 0x31, 0xDE}, // XK_numbersign+TLDE: VKEY_OEM_7 - {0x0024, 0x23, 0xBA}, // XK_dollar+AD12: VKEY_OEM_1 - {0x0024, 0x33, 0xDF}, // XK_dollar+BKSL: VKEY_OEM_8 - {0x0027, 0x0D, 0x34}, // XK_quoteright+AE04: VKEY_4 - {0x0027, 0x18, 0xDE}, // XK_quoteright+AD01: VKEY_OEM_7 - {0x0027, 0x23, 0xBA}, // XK_quoteright+AD12: VKEY_OEM_1 - {0x0027, 0x3D, 0xDE}, // XK_quoteright+AB10: VKEY_OEM_7 - {0x0028, 0x0E, 0x35}, // XK_parenleft+AE05: VKEY_5 - {0x0028, 0x12, 0x39}, // XK_parenleft+AE09: VKEY_9 - {0x0028, 0x33, 0xDC}, // XK_parenleft+BKSL: VKEY_OEM_5 - {0x0029, 0x13, 0x30}, // XK_parenright+AE10: VKEY_0 - {0x0029, 0x14, 0xDB}, // XK_parenright+AE11: VKEY_OEM_4 - {0x0029, 0x23, 0xDD}, // XK_parenright+AD12: VKEY_OEM_6 - {0x002A, 0x23, 0xBA}, // XK_asterisk+AD12: VKEY_OEM_1 - {0x002A, 0x33, 0xDC}, // XK_asterisk+BKSL: VKEY_OEM_5 - {0x002B, 0x0A, 0x31}, // XK_plus+AE01: VKEY_1 - {0x002B, 0x15, 0xBB}, // XK_plus+AE12: VKEY_OEM_PLUS - {0x002B, 0x22, 0xBB}, // XK_plus+AD11: VKEY_OEM_PLUS - {0x002B, 0x23, 0xBB}, // XK_plus+AD12: VKEY_OEM_PLUS - {0x002B, 0x2F, 0xBB}, // XK_plus+AC10: VKEY_OEM_PLUS - {0x002B, 0x33, 0xBF}, // XK_plus+BKSL: VKEY_OEM_2 - {0x002C, 0x0C, 0x33}, // XK_comma+AE03: VKEY_3 - {0x002C, 0x0E, 0x35}, // XK_comma+AE05: VKEY_5 - {0x002C, 0x0F, 0x36}, // XK_comma+AE06: VKEY_6 - {0x002C, 0x12, 0x39}, // XK_comma+AE09: VKEY_9 - {0x002C, 0x19, 0xBC}, // XK_comma+AD02: VKEY_OEM_COMMA - {0x002C, 0x37, 0xBC}, // XK_comma+AB04: VKEY_OEM_COMMA - {0x002C, 0x3A, 0xBC}, // XK_comma+AB07: VKEY_OEM_COMMA - {0x002C, 0x3B, 0xBC}, // XK_comma+AB08: VKEY_OEM_COMMA - {0x002D, 0x0B, 0x32}, // XK_minus+AE02: VKEY_2 - {0x002D, 0x0F, 0x36}, // XK_minus+AE06: VKEY_6 - {0x002D, 0x14, 0xBD}, // XK_minus+AE11: VKEY_OEM_MINUS - {0x002D, 0x26, 0xBD}, // XK_minus+AC01: VKEY_OEM_MINUS - {0x002D, 0x30, 0xBD}, // XK_minus+AC11: VKEY_OEM_MINUS - {0x002E, 0x10, 0x37}, // XK_period+AE07: VKEY_7 - {0x002E, 0x11, 0x38}, // XK_period+AE08: VKEY_8 - {0x002E, 0x1A, 0xBE}, // XK_period+AD03: VKEY_OEM_PERIOD - {0x002E, 0x1B, 0xBE}, // XK_period+AD04: VKEY_OEM_PERIOD - {0x002E, 0x20, 0xBE}, // XK_period+AD09: VKEY_OEM_PERIOD - {0x002E, 0x30, 0xDE}, // XK_period+AC11: VKEY_OEM_7 - {0x002E, 0x3C, 0xBE}, // XK_period+AB09: VKEY_OEM_PERIOD - {0x002E, 0x3D, 0xBF}, // XK_period+AB10: VKEY_OEM_2 - {0x002F, 0x14, 0xDB}, // XK_slash+AE11: VKEY_OEM_4 - {0x002F, 0x22, 0xBF}, // XK_slash+AD11: VKEY_OEM_2 - {0x002F, 0x31, 0xDE}, // XK_slash+TLDE: VKEY_OEM_7 - {0x002F, 0x33, 0xDC}, // XK_slash+BKSL: VKEY_OEM_5 - {0x002F, 0x3D, 0xBF}, // XK_slash+AB10: VKEY_OEM_2 - {0x003A, 0x0A, 0x31}, // XK_colon+AE01: VKEY_1 - {0x003A, 0x0E, 0x35}, // XK_colon+AE05: VKEY_5 - {0x003A, 0x0F, 0x36}, // XK_colon+AE06: VKEY_6 - {0x003A, 0x3C, 0xBF}, // XK_colon+AB09: VKEY_OEM_2 - {0x003B, 0x0D, 0x34}, // XK_semicolon+AE04: VKEY_4 - {0x003B, 0x11, 0x38}, // XK_semicolon+AE08: VKEY_8 - {0x003B, 0x18, 0xBA}, // XK_semicolon+AD01: VKEY_OEM_1 - {0x003B, 0x22, 0xBA}, // XK_semicolon+AD11: VKEY_OEM_1 - {0x003B, 0x23, 0xDD}, // XK_semicolon+AD12: VKEY_OEM_6 - {0x003B, 0x2F, 0xBA}, // XK_semicolon+AC10: VKEY_OEM_1 - {0x003B, 0x31, 0xC0}, // XK_semicolon+TLDE: VKEY_OEM_3 - {0x003B, 0x34, 0xBA}, // XK_semicolon+AB01: VKEY_OEM_1 - {0x003B, 0x3B, 0xBE}, // XK_semicolon+AB08: VKEY_OEM_PERIOD - {0x003B, 0x3D, 0xBF}, // XK_semicolon+AB10: VKEY_OEM_2 - {0x003D, 0x11, 0x38}, // XK_equal+AE08: VKEY_8 - {0x003D, 0x15, 0xBB}, // XK_equal+AE12: VKEY_OEM_PLUS - {0x003D, 0x23, 0xBB}, // XK_equal+AD12: VKEY_OEM_PLUS - {0x003F, 0x0B, 0x32}, // XK_question+AE02: VKEY_2 - {0x003F, 0x10, 0x37}, // XK_question+AE07: VKEY_7 - {0x003F, 0x11, 0x38}, // XK_question+AE08: VKEY_8 - {0x003F, 0x14, 0xBB}, // XK_question+AE11: VKEY_OEM_PLUS - {0x0040, 0x23, 0xDD}, // XK_at+AD12: VKEY_OEM_6 - {0x0040, 0x31, 0xDE}, // XK_at+TLDE: VKEY_OEM_7 - {0x005B, 0x0A, 0xDB}, // XK_bracketleft+AE01: VKEY_OEM_4 - {0x005B, 0x14, 0xDB}, // XK_bracketleft+AE11: VKEY_OEM_4 - {0x005B, 0x22, 0xDB}, // XK_bracketleft+AD11: VKEY_OEM_4 - {0x005B, 0x23, 0xDD}, // XK_bracketleft+AD12: VKEY_OEM_6 - {0x005B, 0x30, 0xDE}, // XK_bracketleft+AC11: VKEY_OEM_7 - {0x005C, 0x15, 0xDB}, // XK_backslash+AE12: VKEY_OEM_4 - {0x005D, 0x0B, 0xDD}, // XK_bracketright+AE02: VKEY_OEM_6 - {0x005D, 0x15, 0xDD}, // XK_bracketright+AE12: VKEY_OEM_6 - {0x005D, 0x23, 0xDD}, // XK_bracketright+AD12: VKEY_OEM_6 - {0x005D, 0x31, 0xC0}, // XK_bracketright+TLDE: VKEY_OEM_3 - {0x005D, 0x33, 0xDC}, // XK_bracketright+BKSL: VKEY_OEM_5 - {0x005F, 0x11, 0x38}, // XK_underscore+AE08: VKEY_8 - {0x005F, 0x14, 0xBD}, // XK_underscore+AE11: VKEY_OEM_MINUS - {0x00A7, 0x0D, 0x34}, // XK_section+AE04: VKEY_4 - {0x00A7, 0x0F, 0x36}, // XK_section+AE06: VKEY_6 - {0x00A7, 0x30, 0xDE}, // XK_section+AC11: VKEY_OEM_7 - {0x00AB, 0x11, 0x38}, // XK_guillemotleft+AE08: VKEY_8 - {0x00AB, 0x15, 0xDD}, // XK_guillemotleft+AE12: VKEY_OEM_6 - {0x00B0, 0x15, 0xBF}, // XK_degree+AE12: VKEY_OEM_2 - {0x00B0, 0x31, 0xDE}, // XK_degree+TLDE: VKEY_OEM_7 - {0x00BA, 0x30, 0xDE}, // XK_masculine+AC11: VKEY_OEM_7 - {0x00BA, 0x31, 0xDC}, // XK_masculine+TLDE: VKEY_OEM_5 - {0x00E0, 0x13, 0x30}, // XK_agrave+AE10: VKEY_0 - {0x00E0, 0x33, 0xDC}, // XK_agrave+BKSL: VKEY_OEM_5 - {0x00E1, 0x11, 0x38}, // XK_aacute+AE08: VKEY_8 - {0x00E1, 0x30, 0xDE}, // XK_aacute+AC11: VKEY_OEM_7 - {0x00E2, 0x0B, 0x32}, // XK_acircumflex+AE02: VKEY_2 - {0x00E2, 0x33, 0xDC}, // XK_acircumflex+BKSL: VKEY_OEM_5 - {0x00E4, 0x23, 0xDD}, // XK_adiaeresis+AD12: VKEY_OEM_6 - {0x00E6, 0x2F, 0xC0}, // XK_ae+AC10: VKEY_OEM_3 - {0x00E6, 0x30, 0xDE}, // XK_ae+AC11: VKEY_OEM_7 - {0x00E7, 0x12, 0x39}, // XK_ccedilla+AE09: VKEY_9 - {0x00E7, 0x22, 0xDB}, // XK_ccedilla+AD11: VKEY_OEM_4 - {0x00E7, 0x23, 0xDD}, // XK_ccedilla+AD12: VKEY_OEM_6 - {0x00E7, 0x30, 0xDE}, // XK_ccedilla+AC11: VKEY_OEM_7 - {0x00E7, 0x33, 0xBF}, // XK_ccedilla+BKSL: VKEY_OEM_2 - {0x00E7, 0x3B, 0xBC}, // XK_ccedilla+AB08: VKEY_OEM_COMMA - {0x00E8, 0x10, 0x37}, // XK_egrave+AE07: VKEY_7 - {0x00E8, 0x22, 0xBA}, // XK_egrave+AD11: VKEY_OEM_1 - {0x00E8, 0x30, 0xC0}, // XK_egrave+AC11: VKEY_OEM_3 - {0x00E9, 0x0B, 0x32}, // XK_eacute+AE02: VKEY_2 - {0x00E9, 0x13, 0x30}, // XK_eacute+AE10: VKEY_0 - {0x00E9, 0x3D, 0xBF}, // XK_eacute+AB10: VKEY_OEM_2 - {0x00ED, 0x12, 0x39}, // XK_iacute+AE09: VKEY_9 - {0x00ED, 0x31, 0x30}, // XK_iacute+TLDE: VKEY_0 - {0x00F0, 0x22, 0xDD}, // XK_eth+AD11: VKEY_OEM_6 - {0x00F0, 0x23, 0xBA}, // XK_eth+AD12: VKEY_OEM_1 - {0x00F3, 0x15, 0xBB}, // XK_oacute+AE12: VKEY_OEM_PLUS - {0x00F3, 0x33, 0xDC}, // XK_oacute+BKSL: VKEY_OEM_5 - {0x00F4, 0x0D, 0x34}, // XK_ocircumflex+AE04: VKEY_4 - {0x00F4, 0x2F, 0xBA}, // XK_ocircumflex+AC10: VKEY_OEM_1 - {0x00F6, 0x13, 0xC0}, // XK_odiaeresis+AE10: VKEY_OEM_3 - {0x00F6, 0x14, 0xBB}, // XK_odiaeresis+AE11: VKEY_OEM_PLUS - {0x00F6, 0x22, 0xDB}, // XK_odiaeresis+AD11: VKEY_OEM_4 - {0x00F8, 0x2F, 0xC0}, // XK_oslash+AC10: VKEY_OEM_3 - {0x00F8, 0x30, 0xDE}, // XK_oslash+AC11: VKEY_OEM_7 - {0x00F9, 0x30, 0xC0}, // XK_ugrave+AC11: VKEY_OEM_3 - {0x00F9, 0x33, 0xBF}, // XK_ugrave+BKSL: VKEY_OEM_2 - {0x00FA, 0x22, 0xDB}, // XK_uacute+AD11: VKEY_OEM_4 - {0x00FA, 0x23, 0xDD}, // XK_uacute+AD12: VKEY_OEM_6 - {0x00FC, 0x19, 0x57}, // XK_udiaeresis+AD02: VKEY_W - {0x01B1, 0x0A, 0x31}, // XK_aogonek+AE01: VKEY_1 - {0x01B1, 0x18, 0x51}, // XK_aogonek+AD01: VKEY_Q - {0x01B1, 0x30, 0xDE}, // XK_aogonek+AC11: VKEY_OEM_7 - {0x01B3, 0x2F, 0xBA}, // XK_lstroke+AC10: VKEY_OEM_1 - {0x01B3, 0x33, 0xBF}, // XK_lstroke+BKSL: VKEY_OEM_2 - {0x01B9, 0x0C, 0x33}, // XK_scaron+AE03: VKEY_3 - {0x01B9, 0x0F, 0x36}, // XK_scaron+AE06: VKEY_6 - {0x01B9, 0x22, 0xDB}, // XK_scaron+AD11: VKEY_OEM_4 - {0x01B9, 0x26, 0xBA}, // XK_scaron+AC01: VKEY_OEM_1 - {0x01B9, 0x29, 0x46}, // XK_scaron+AC04: VKEY_F - {0x01B9, 0x3C, 0xBE}, // XK_scaron+AB09: VKEY_OEM_PERIOD - {0x01BA, 0x2F, 0xBA}, // XK_scedilla+AC10: VKEY_OEM_1 - {0x01BA, 0x3C, 0xBE}, // XK_scedilla+AB09: VKEY_OEM_PERIOD - {0x01BE, 0x0F, 0x36}, // XK_zcaron+AE06: VKEY_6 - {0x01BE, 0x15, 0xBB}, // XK_zcaron+AE12: VKEY_OEM_PLUS - {0x01BE, 0x19, 0x57}, // XK_zcaron+AD02: VKEY_W - {0x01BE, 0x22, 0x59}, // XK_zcaron+AD11: VKEY_Y - {0x01BE, 0x33, 0xDC}, // XK_zcaron+BKSL: VKEY_OEM_5 - {0x01BF, 0x22, 0xDB}, // XK_zabovedot+AD11: VKEY_OEM_4 - {0x01BF, 0x33, 0xDC}, // XK_zabovedot+BKSL: VKEY_OEM_5 - {0x01E3, 0x0A, 0x31}, // XK_abreve+AE01: VKEY_1 - {0x01E3, 0x22, 0xDB}, // XK_abreve+AD11: VKEY_OEM_4 - {0x01E8, 0x0B, 0x32}, // XK_ccaron+AE02: VKEY_2 - {0x01E8, 0x0D, 0x34}, // XK_ccaron+AE04: VKEY_4 - {0x01E8, 0x21, 0x58}, // XK_ccaron+AD10: VKEY_X - {0x01E8, 0x2F, 0xBA}, // XK_ccaron+AC10: VKEY_OEM_1 - {0x01E8, 0x3B, 0xBC}, // XK_ccaron+AB08: VKEY_OEM_COMMA - {0x01EA, 0x0C, 0x33}, // XK_eogonek+AE03: VKEY_3 - {0x01F0, 0x13, 0x30}, // XK_dstroke+AE10: VKEY_0 - {0x01F0, 0x23, 0xDD}, // XK_dstroke+AD12: VKEY_OEM_6 - {0x03E7, 0x0E, 0x35}, // XK_iogonek+AE05: VKEY_5 - {0x03EC, 0x0D, 0x34}, // XK_eabovedot+AE04: VKEY_4 - {0x03EC, 0x30, 0xDE}, // XK_eabovedot+AC11: VKEY_OEM_7 - {0x03F9, 0x10, 0x37}, // XK_uogonek+AE07: VKEY_7 - {0x03FE, 0x11, 0x38}, // XK_umacron+AE08: VKEY_8 - {0x03FE, 0x18, 0x51}, // XK_umacron+AD01: VKEY_Q - {0x03FE, 0x35, 0x58}, // XK_umacron+AB02: VKEY_X + {0x0021, 0x0A, 0x31}, // XK_exclam+AE01: VKEY_1 + {0x0021, 0x11, 0x38}, // XK_exclam+AE08: VKEY_8 + {0x0021, 0x3D, 0xDF}, // XK_exclam+AB10: VKEY_OEM_8 + {0x0022, 0x0B, 0x32}, // XK_quotedbl+AE02: VKEY_2 + {0x0022, 0x0C, 0x33}, // XK_quotedbl+AE03: VKEY_3 + {0x0023, 0x31, 0xDE}, // XK_numbersign+TLDE: VKEY_OEM_7 + {0x0024, 0x23, 0xBA}, // XK_dollar+AD12: VKEY_OEM_1 + {0x0024, 0x33, 0xDF}, // XK_dollar+BKSL: VKEY_OEM_8 + {0x0027, 0x0D, 0x34}, // XK_quoteright+AE04: VKEY_4 + {0x0027, 0x18, 0xDE}, // XK_quoteright+AD01: VKEY_OEM_7 + {0x0027, 0x19, 0x57}, // XK_quoteright+AD02: VKEY_W + {0x0027, 0x23, 0xBA}, // XK_quoteright+AD12: VKEY_OEM_1 + {0x0027, 0x3D, 0xDE}, // XK_quoteright+AB10: VKEY_OEM_7 + {0x0028, 0x0E, 0x35}, // XK_parenleft+AE05: VKEY_5 + {0x0028, 0x12, 0x39}, // XK_parenleft+AE09: VKEY_9 + {0x0028, 0x33, 0xDC}, // XK_parenleft+BKSL: VKEY_OEM_5 + {0x0029, 0x13, 0x30}, // XK_parenright+AE10: VKEY_0 + {0x0029, 0x14, 0xDB}, // XK_parenright+AE11: VKEY_OEM_4 + {0x0029, 0x23, 0xDD}, // XK_parenright+AD12: VKEY_OEM_6 + {0x002A, 0x23, 0xBA}, // XK_asterisk+AD12: VKEY_OEM_1 + {0x002A, 0x33, 0xDC}, // XK_asterisk+BKSL: VKEY_OEM_5 + {0x002B, 0x0A, 0x31}, // XK_plus+AE01: VKEY_1 + {0x002B, 0x15, 0xBB}, // XK_plus+AE12: VKEY_OEM_PLUS + {0x002B, 0x22, 0xBB}, // XK_plus+AD11: VKEY_OEM_PLUS + {0x002B, 0x23, 0xBB}, // XK_plus+AD12: VKEY_OEM_PLUS + {0x002B, 0x2F, 0xBB}, // XK_plus+AC10: VKEY_OEM_PLUS + {0x002B, 0x33, 0xBF}, // XK_plus+BKSL: VKEY_OEM_2 + {0x002C, 0x0C, 0x33}, // XK_comma+AE03: VKEY_3 + {0x002C, 0x0E, 0x35}, // XK_comma+AE05: VKEY_5 + {0x002C, 0x0F, 0x36}, // XK_comma+AE06: VKEY_6 + {0x002C, 0x12, 0x39}, // XK_comma+AE09: VKEY_9 + {0x002C, 0x19, 0xBC}, // XK_comma+AD02: VKEY_OEM_COMMA + {0x002C, 0x30, 0xDE}, // XK_comma+AC11: VKEY_OEM_7 + {0x002C, 0x37, 0xBC}, // XK_comma+AB04: VKEY_OEM_COMMA + {0x002C, 0x3A, 0xBC}, // XK_comma+AB07: VKEY_OEM_COMMA + {0x002C, 0x3B, 0xBC}, // XK_comma+AB08: VKEY_OEM_COMMA + {0x002D, 0x0B, 0x32}, // XK_minus+AE02: VKEY_2 + {0x002D, 0x0F, 0x36}, // XK_minus+AE06: VKEY_6 + {0x002D, 0x14, 0xBD}, // XK_minus+AE11: VKEY_OEM_MINUS + {0x002D, 0x26, 0xBD}, // XK_minus+AC01: VKEY_OEM_MINUS + {0x002D, 0x30, 0xBD}, // XK_minus+AC11: VKEY_OEM_MINUS + {0x002E, 0x10, 0x37}, // XK_period+AE07: VKEY_7 + {0x002E, 0x11, 0x38}, // XK_period+AE08: VKEY_8 + {0x002E, 0x1A, 0xBE}, // XK_period+AD03: VKEY_OEM_PERIOD + {0x002E, 0x1B, 0xBE}, // XK_period+AD04: VKEY_OEM_PERIOD + {0x002E, 0x20, 0xBE}, // XK_period+AD09: VKEY_OEM_PERIOD + {0x002E, 0x30, 0xDE}, // XK_period+AC11: VKEY_OEM_7 + {0x002E, 0x3C, 0xBE}, // XK_period+AB09: VKEY_OEM_PERIOD + {0x002E, 0x3D, 0xBF}, // XK_period+AB10: VKEY_OEM_2 + {0x002F, 0x14, 0xDB}, // XK_slash+AE11: VKEY_OEM_4 + {0x002F, 0x18, 0x51}, // XK_slash+AD01: VKEY_Q + {0x002F, 0x22, 0xBF}, // XK_slash+AD11: VKEY_OEM_2 + {0x002F, 0x31, 0xDE}, // XK_slash+TLDE: VKEY_OEM_7 + {0x002F, 0x33, 0xDC}, // XK_slash+BKSL: VKEY_OEM_5 + {0x002F, 0x3D, 0xBF}, // XK_slash+AB10: VKEY_OEM_2 + {0x003A, 0x0A, 0x31}, // XK_colon+AE01: VKEY_1 + {0x003A, 0x0E, 0x35}, // XK_colon+AE05: VKEY_5 + {0x003A, 0x0F, 0x36}, // XK_colon+AE06: VKEY_6 + {0x003A, 0x3C, 0xBF}, // XK_colon+AB09: VKEY_OEM_2 + {0x003B, 0x0D, 0x34}, // XK_semicolon+AE04: VKEY_4 + {0x003B, 0x11, 0x38}, // XK_semicolon+AE08: VKEY_8 + {0x003B, 0x18, 0xBA}, // XK_semicolon+AD01: VKEY_OEM_1 + {0x003B, 0x22, 0xBA}, // XK_semicolon+AD11: VKEY_OEM_1 + {0x003B, 0x23, 0xDD}, // XK_semicolon+AD12: VKEY_OEM_6 + {0x003B, 0x2F, 0xBA}, // XK_semicolon+AC10: VKEY_OEM_1 + {0x003B, 0x31, 0xC0}, // XK_semicolon+TLDE: VKEY_OEM_3 + {0x003B, 0x34, 0xBA}, // XK_semicolon+AB01: VKEY_OEM_1 + {0x003B, 0x3B, 0xBE}, // XK_semicolon+AB08: VKEY_OEM_PERIOD + {0x003B, 0x3D, 0xBF}, // XK_semicolon+AB10: VKEY_OEM_2 + {0x003D, 0x11, 0x38}, // XK_equal+AE08: VKEY_8 + {0x003D, 0x15, 0xBB}, // XK_equal+AE12: VKEY_OEM_PLUS + {0x003D, 0x23, 0xBB}, // XK_equal+AD12: VKEY_OEM_PLUS + {0x003F, 0x0B, 0x32}, // XK_question+AE02: VKEY_2 + {0x003F, 0x10, 0x37}, // XK_question+AE07: VKEY_7 + {0x003F, 0x11, 0x38}, // XK_question+AE08: VKEY_8 + {0x003F, 0x14, 0xBB}, // XK_question+AE11: VKEY_OEM_PLUS + {0x0040, 0x23, 0xDD}, // XK_at+AD12: VKEY_OEM_6 + {0x0040, 0x31, 0xDE}, // XK_at+TLDE: VKEY_OEM_7 + {0x005B, 0x0A, 0xDB}, // XK_bracketleft+AE01: VKEY_OEM_4 + {0x005B, 0x14, 0xDB}, // XK_bracketleft+AE11: VKEY_OEM_4 + {0x005B, 0x22, 0xDB}, // XK_bracketleft+AD11: VKEY_OEM_4 + {0x005B, 0x23, 0xDD}, // XK_bracketleft+AD12: VKEY_OEM_6 + {0x005B, 0x30, 0xDE}, // XK_bracketleft+AC11: VKEY_OEM_7 + {0x005C, 0x15, 0xDB}, // XK_backslash+AE12: VKEY_OEM_4 + {0x005D, 0x0B, 0xDD}, // XK_bracketright+AE02: VKEY_OEM_6 + {0x005D, 0x15, 0xDD}, // XK_bracketright+AE12: VKEY_OEM_6 + {0x005D, 0x22, 0xDB}, // XK_bracketright+AD11: VKEY_OEM_4 + {0x005D, 0x23, 0xDD}, // XK_bracketright+AD12: VKEY_OEM_6 + {0x005D, 0x31, 0xC0}, // XK_bracketright+TLDE: VKEY_OEM_3 + {0x005D, 0x33, 0xDC}, // XK_bracketright+BKSL: VKEY_OEM_5 + {0x005F, 0x11, 0x38}, // XK_underscore+AE08: VKEY_8 + {0x005F, 0x14, 0xBD}, // XK_underscore+AE11: VKEY_OEM_MINUS + {0x00A7, 0x0D, 0x34}, // XK_section+AE04: VKEY_4 + {0x00A7, 0x0F, 0x36}, // XK_section+AE06: VKEY_6 + {0x00A7, 0x30, 0xDE}, // XK_section+AC11: VKEY_OEM_7 + {0x00AB, 0x11, 0x38}, // XK_guillemotleft+AE08: VKEY_8 + {0x00AB, 0x15, 0xDD}, // XK_guillemotleft+AE12: VKEY_OEM_6 + {0x00B0, 0x15, 0xBF}, // XK_degree+AE12: VKEY_OEM_2 + {0x00B0, 0x31, 0xDE}, // XK_degree+TLDE: VKEY_OEM_7 + {0x00BA, 0x30, 0xDE}, // XK_masculine+AC11: VKEY_OEM_7 + {0x00BA, 0x31, 0xDC}, // XK_masculine+TLDE: VKEY_OEM_5 + {0x00E0, 0x13, 0x30}, // XK_agrave+AE10: VKEY_0 + {0x00E0, 0x33, 0xDC}, // XK_agrave+BKSL: VKEY_OEM_5 + {0x00E1, 0x11, 0x38}, // XK_aacute+AE08: VKEY_8 + {0x00E1, 0x30, 0xDE}, // XK_aacute+AC11: VKEY_OEM_7 + {0x00E2, 0x0B, 0x32}, // XK_acircumflex+AE02: VKEY_2 + {0x00E2, 0x33, 0xDC}, // XK_acircumflex+BKSL: VKEY_OEM_5 + {0x00E4, 0x23, 0xDD}, // XK_adiaeresis+AD12: VKEY_OEM_6 + {0x00E6, 0x2F, 0xC0}, // XK_ae+AC10: VKEY_OEM_3 + {0x00E6, 0x30, 0xDE}, // XK_ae+AC11: VKEY_OEM_7 + {0x00E7, 0x12, 0x39}, // XK_ccedilla+AE09: VKEY_9 + {0x00E7, 0x22, 0xDB}, // XK_ccedilla+AD11: VKEY_OEM_4 + {0x00E7, 0x23, 0xDD}, // XK_ccedilla+AD12: VKEY_OEM_6 + {0x00E7, 0x30, 0xDE}, // XK_ccedilla+AC11: VKEY_OEM_7 + {0x00E7, 0x33, 0xBF}, // XK_ccedilla+BKSL: VKEY_OEM_2 + {0x00E7, 0x3B, 0xBC}, // XK_ccedilla+AB08: VKEY_OEM_COMMA + {0x00E8, 0x10, 0x37}, // XK_egrave+AE07: VKEY_7 + {0x00E8, 0x22, 0xBA}, // XK_egrave+AD11: VKEY_OEM_1 + {0x00E8, 0x30, 0xC0}, // XK_egrave+AC11: VKEY_OEM_3 + {0x00E9, 0x0B, 0x32}, // XK_eacute+AE02: VKEY_2 + {0x00E9, 0x13, 0x30}, // XK_eacute+AE10: VKEY_0 + {0x00E9, 0x3D, 0xBF}, // XK_eacute+AB10: VKEY_OEM_2 + {0x00ED, 0x12, 0x39}, // XK_iacute+AE09: VKEY_9 + {0x00ED, 0x31, 0x30}, // XK_iacute+TLDE: VKEY_0 + {0x00F0, 0x22, 0xDD}, // XK_eth+AD11: VKEY_OEM_6 + {0x00F0, 0x23, 0xBA}, // XK_eth+AD12: VKEY_OEM_1 + {0x00F3, 0x15, 0xBB}, // XK_oacute+AE12: VKEY_OEM_PLUS + {0x00F3, 0x33, 0xDC}, // XK_oacute+BKSL: VKEY_OEM_5 + {0x00F4, 0x0D, 0x34}, // XK_ocircumflex+AE04: VKEY_4 + {0x00F4, 0x2F, 0xBA}, // XK_ocircumflex+AC10: VKEY_OEM_1 + {0x00F6, 0x13, 0xC0}, // XK_odiaeresis+AE10: VKEY_OEM_3 + {0x00F6, 0x14, 0xBB}, // XK_odiaeresis+AE11: VKEY_OEM_PLUS + {0x00F6, 0x22, 0xDB}, // XK_odiaeresis+AD11: VKEY_OEM_4 + {0x00F8, 0x2F, 0xC0}, // XK_oslash+AC10: VKEY_OEM_3 + {0x00F8, 0x30, 0xDE}, // XK_oslash+AC11: VKEY_OEM_7 + {0x00F9, 0x30, 0xC0}, // XK_ugrave+AC11: VKEY_OEM_3 + {0x00F9, 0x33, 0xBF}, // XK_ugrave+BKSL: VKEY_OEM_2 + {0x00FA, 0x22, 0xDB}, // XK_uacute+AD11: VKEY_OEM_4 + {0x00FA, 0x23, 0xDD}, // XK_uacute+AD12: VKEY_OEM_6 + {0x00FC, 0x19, 0x57}, // XK_udiaeresis+AD02: VKEY_W + {0x01B1, 0x0A, 0x31}, // XK_aogonek+AE01: VKEY_1 + {0x01B1, 0x18, 0x51}, // XK_aogonek+AD01: VKEY_Q + {0x01B1, 0x30, 0xDE}, // XK_aogonek+AC11: VKEY_OEM_7 + {0x01B3, 0x2F, 0xBA}, // XK_lstroke+AC10: VKEY_OEM_1 + {0x01B3, 0x33, 0xBF}, // XK_lstroke+BKSL: VKEY_OEM_2 + {0x01B9, 0x0C, 0x33}, // XK_scaron+AE03: VKEY_3 + {0x01B9, 0x0F, 0x36}, // XK_scaron+AE06: VKEY_6 + {0x01B9, 0x22, 0xDB}, // XK_scaron+AD11: VKEY_OEM_4 + {0x01B9, 0x26, 0xBA}, // XK_scaron+AC01: VKEY_OEM_1 + {0x01B9, 0x29, 0x46}, // XK_scaron+AC04: VKEY_F + {0x01B9, 0x3C, 0xBE}, // XK_scaron+AB09: VKEY_OEM_PERIOD + {0x01BA, 0x2F, 0xBA}, // XK_scedilla+AC10: VKEY_OEM_1 + {0x01BA, 0x3C, 0xBE}, // XK_scedilla+AB09: VKEY_OEM_PERIOD + {0x01BE, 0x0F, 0x36}, // XK_zcaron+AE06: VKEY_6 + {0x01BE, 0x15, 0xBB}, // XK_zcaron+AE12: VKEY_OEM_PLUS + {0x01BE, 0x19, 0x57}, // XK_zcaron+AD02: VKEY_W + {0x01BE, 0x22, 0x59}, // XK_zcaron+AD11: VKEY_Y + {0x01BE, 0x33, 0xDC}, // XK_zcaron+BKSL: VKEY_OEM_5 + {0x01BF, 0x22, 0xDB}, // XK_zabovedot+AD11: VKEY_OEM_4 + {0x01BF, 0x33, 0xDC}, // XK_zabovedot+BKSL: VKEY_OEM_5 + {0x01E3, 0x0A, 0x31}, // XK_abreve+AE01: VKEY_1 + {0x01E3, 0x22, 0xDB}, // XK_abreve+AD11: VKEY_OEM_4 + {0x01E8, 0x0B, 0x32}, // XK_ccaron+AE02: VKEY_2 + {0x01E8, 0x0D, 0x34}, // XK_ccaron+AE04: VKEY_4 + {0x01E8, 0x21, 0x58}, // XK_ccaron+AD10: VKEY_X + {0x01E8, 0x2F, 0xBA}, // XK_ccaron+AC10: VKEY_OEM_1 + {0x01E8, 0x3B, 0xBC}, // XK_ccaron+AB08: VKEY_OEM_COMMA + {0x01EA, 0x0C, 0x33}, // XK_eogonek+AE03: VKEY_3 + {0x01F0, 0x13, 0x30}, // XK_dstroke+AE10: VKEY_0 + {0x01F0, 0x23, 0xDD}, // XK_dstroke+AD12: VKEY_OEM_6 + {0x03E7, 0x0E, 0x35}, // XK_iogonek+AE05: VKEY_5 + {0x03EC, 0x0D, 0x34}, // XK_eabovedot+AE04: VKEY_4 + {0x03EC, 0x30, 0xDE}, // XK_eabovedot+AC11: VKEY_OEM_7 + {0x03F9, 0x10, 0x37}, // XK_uogonek+AE07: VKEY_7 + {0x03FE, 0x11, 0x38}, // XK_umacron+AE08: VKEY_8 + {0x03FE, 0x18, 0x51}, // XK_umacron+AD01: VKEY_Q + {0x03FE, 0x35, 0x58}, // XK_umacron+AB02: VKEY_X }; const struct MAP2 { @@ -326,7 +404,6 @@ const struct MAP2 { {0x002F, 0x13, 0x003F, 0xBF}, // XK_slash+AE10+XK_question: VKEY_OEM_2 {0x003D, 0x3D, 0x0025, 0xDF}, // XK_equal+AB10+XK_percent: VKEY_OEM_8 {0x003D, 0x3D, 0x002B, 0xBB}, // XK_equal+AB10+XK_plus: VKEY_OEM_PLUS - {0x005C, 0x33, 0x002F, 0xDE}, // XK_backslash+BKSL+XK_slash: VKEY_OEM_7 {0x005C, 0x33, 0x007C, 0xDC}, // XK_backslash+BKSL+XK_bar: VKEY_OEM_5 {0x0060, 0x31, 0x0000, 0xC0}, // XK_quoteleft+TLDE+NoSymbol: VKEY_OEM_3 {0x0060, 0x31, 0x00AC, 0xDF}, // XK_quoteleft+TLDE+XK_notsign: VKEY_OEM_8 @@ -357,94 +434,98 @@ const struct MAP3 { return m1.ch0 < m2.ch0; } } map3[] = { - {0x0023, 0x33, 0x007E, 0x0000, - 0xDE}, // XK_numbersign+BKSL+XK_asciitilde+NoSymbol: VKEY_OEM_7 - {0x0027, 0x14, 0x003F, 0x0000, - 0xDB}, // XK_quoteright+AE11+XK_question+NoSymbol: VKEY_OEM_4 - {0x0027, 0x14, 0x003F, 0x00DD, - 0xDB}, // XK_quoteright+AE11+XK_question+XK_Yacute: VKEY_OEM_4 - {0x0027, 0x15, 0x002A, 0x0000, - 0xBB}, // XK_quoteright+AE12+XK_asterisk+NoSymbol: VKEY_OEM_PLUS - {0x0027, 0x30, 0x0040, 0x0000, - 0xC0}, // XK_quoteright+AC11+XK_at+NoSymbol: VKEY_OEM_3 - {0x0027, 0x33, 0x002A, 0x0000, - 0xBF}, // XK_quoteright+BKSL+XK_asterisk+NoSymbol: VKEY_OEM_2 - {0x0027, 0x33, 0x002A, 0x00BD, - 0xDC}, // XK_quoteright+BKSL+XK_asterisk+XK_onehalf: VKEY_OEM_5 - {0x0027, 0x33, 0x002A, 0x01A3, - 0xBF}, // XK_quoteright+BKSL+XK_asterisk+XK_Lstroke: VKEY_OEM_2 - {0x0027, 0x34, 0x0022, 0x0000, - 0x5A}, // XK_quoteright+AB01+XK_quotedbl+NoSymbol: VKEY_Z - {0x0027, 0x34, 0x0022, 0x01D8, - 0xDE}, // XK_quoteright+AB01+XK_quotedbl+XK_Rcaron: VKEY_OEM_7 - {0x002B, 0x14, 0x003F, 0x0000, - 0xBB}, // XK_plus+AE11+XK_question+NoSymbol: VKEY_OEM_PLUS - {0x002B, 0x14, 0x003F, 0x005C, - 0xBD}, // XK_plus+AE11+XK_question+XK_backslash: VKEY_OEM_MINUS - {0x002B, 0x14, 0x003F, 0x01F5, - 0xBB}, // XK_plus+AE11+XK_question+XK_odoubleacute: VKEY_OEM_PLUS - {0x002D, 0x15, 0x005F, 0x0000, - 0xBD}, // XK_minus+AE12+XK_underscore+NoSymbol: VKEY_OEM_MINUS - {0x002D, 0x15, 0x005F, 0x03B3, - 0xDB}, // XK_minus+AE12+XK_underscore+XK_rcedilla: VKEY_OEM_4 - {0x002D, 0x3D, 0x005F, 0x0000, - 0xBD}, // XK_minus+AB10+XK_underscore+NoSymbol: VKEY_OEM_MINUS - {0x002D, 0x3D, 0x005F, 0x002A, - 0xBD}, // XK_minus+AB10+XK_underscore+XK_asterisk: VKEY_OEM_MINUS - {0x002D, 0x3D, 0x005F, 0x002F, - 0xBF}, // XK_minus+AB10+XK_underscore+XK_slash: VKEY_OEM_2 - {0x002D, 0x3D, 0x005F, 0x006E, - 0xBD}, // XK_minus+AB10+XK_underscore+XK_n: VKEY_OEM_MINUS - {0x003D, 0x14, 0x0025, 0x0000, - 0xBB}, // XK_equal+AE11+XK_percent+NoSymbol: VKEY_OEM_PLUS - {0x003D, 0x14, 0x0025, 0x002D, - 0xBD}, // XK_equal+AE11+XK_percent+XK_minus: VKEY_OEM_MINUS - {0x005C, 0x31, 0x007C, 0x0031, - 0xDC}, // XK_backslash+TLDE+XK_bar+XK_1: VKEY_OEM_5 - {0x005C, 0x31, 0x007C, 0x03D1, - 0xC0}, // XK_backslash+TLDE+XK_bar+XK_Ncedilla: VKEY_OEM_3 - {0x0060, 0x31, 0x007E, 0x0000, - 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+NoSymbol: VKEY_OEM_3 - {0x0060, 0x31, 0x007E, 0x0031, - 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_1: VKEY_OEM_3 - {0x0060, 0x31, 0x007E, 0x003B, - 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_semicolon: VKEY_OEM_3 - {0x0060, 0x31, 0x007E, 0x0060, - 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_quoteleft: VKEY_OEM_3 - {0x0060, 0x31, 0x007E, 0x00BF, - 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_questiondown: VKEY_OEM_3 - {0x0060, 0x31, 0x007E, 0x01F5, - 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_odoubleacute: VKEY_OEM_3 - {0x00E4, 0x30, 0x00C4, 0x0000, - 0xDE}, // XK_adiaeresis+AC11+XK_Adiaeresis+NoSymbol: VKEY_OEM_7 - {0x00E4, 0x30, 0x00C4, 0x01A6, - 0xDE}, // XK_adiaeresis+AC11+XK_Adiaeresis+XK_Sacute: VKEY_OEM_7 - {0x00E4, 0x30, 0x00C4, 0x01F8, - 0xDE}, // XK_adiaeresis+AC11+XK_Adiaeresis+XK_rcaron: VKEY_OEM_7 - {0x00E7, 0x2F, 0x00C7, 0x0000, - 0xBA}, // XK_ccedilla+AC10+XK_Ccedilla+NoSymbol: VKEY_OEM_1 - {0x00E7, 0x2F, 0x00C7, 0x00DE, - 0xC0}, // XK_ccedilla+AC10+XK_Ccedilla+XK_Thorn: VKEY_OEM_3 - {0x00F6, 0x2F, 0x00D6, 0x0000, - 0xC0}, // XK_odiaeresis+AC10+XK_Odiaeresis+NoSymbol: VKEY_OEM_3 - {0x00F6, 0x2F, 0x00D6, 0x01DE, - 0xC0}, // XK_odiaeresis+AC10+XK_Odiaeresis+XK_Tcedilla: VKEY_OEM_3 - {0x00FC, 0x14, 0x00DC, 0x0000, - 0xBF}, // XK_udiaeresis+AE11+XK_Udiaeresis+NoSymbol: VKEY_OEM_2 - {0x00FC, 0x22, 0x00DC, 0x0000, - 0xBA}, // XK_udiaeresis+AD11+XK_Udiaeresis+NoSymbol: VKEY_OEM_1 - {0x00FC, 0x22, 0x00DC, 0x01A3, - 0xC0}, // XK_udiaeresis+AD11+XK_Udiaeresis+XK_Lstroke: VKEY_OEM_3 - {0x01EA, 0x3D, 0x01CA, 0x0000, - 0xBD}, // XK_eogonek+AB10+XK_Eogonek+NoSymbol: VKEY_OEM_MINUS - {0x01EA, 0x3D, 0x01CA, 0x006E, - 0xBF}, // XK_eogonek+AB10+XK_Eogonek+XK_n: VKEY_OEM_2 - {0x03E7, 0x22, 0x03C7, 0x0000, - 0xDB}, // XK_iogonek+AD11+XK_Iogonek+NoSymbol: VKEY_OEM_4 - {0x03F9, 0x2F, 0x03D9, 0x0000, - 0xC0}, // XK_uogonek+AC10+XK_Uogonek+NoSymbol: VKEY_OEM_3 - {0x03F9, 0x2F, 0x03D9, 0x01DE, - 0xBA}, // XK_uogonek+AC10+XK_Uogonek+XK_Tcedilla: VKEY_OEM_1 + {0x0023, 0x33, 0x007E, 0x0000, + 0xDE}, // XK_numbersign+BKSL+XK_asciitilde+NoSymbol: VKEY_OEM_7 + {0x0027, 0x14, 0x003F, 0x0000, + 0xDB}, // XK_quoteright+AE11+XK_question+NoSymbol: VKEY_OEM_4 + {0x0027, 0x14, 0x003F, 0x00DD, + 0xDB}, // XK_quoteright+AE11+XK_question+XK_Yacute: VKEY_OEM_4 + {0x0027, 0x15, 0x002A, 0x0000, + 0xBB}, // XK_quoteright+AE12+XK_asterisk+NoSymbol: VKEY_OEM_PLUS + {0x0027, 0x30, 0x0040, 0x0000, + 0xC0}, // XK_quoteright+AC11+XK_at+NoSymbol: VKEY_OEM_3 + {0x0027, 0x33, 0x002A, 0x0000, + 0xBF}, // XK_quoteright+BKSL+XK_asterisk+NoSymbol: VKEY_OEM_2 + {0x0027, 0x33, 0x002A, 0x00BD, + 0xDC}, // XK_quoteright+BKSL+XK_asterisk+XK_onehalf: VKEY_OEM_5 + {0x0027, 0x33, 0x002A, 0x01A3, + 0xBF}, // XK_quoteright+BKSL+XK_asterisk+XK_Lstroke: VKEY_OEM_2 + {0x0027, 0x34, 0x0022, 0x0000, + 0x5A}, // XK_quoteright+AB01+XK_quotedbl+NoSymbol: VKEY_Z + {0x0027, 0x34, 0x0022, 0x01D8, + 0xDE}, // XK_quoteright+AB01+XK_quotedbl+XK_Rcaron: VKEY_OEM_7 + {0x002B, 0x14, 0x003F, 0x0000, + 0xBB}, // XK_plus+AE11+XK_question+NoSymbol: VKEY_OEM_PLUS + {0x002B, 0x14, 0x003F, 0x005C, + 0xBD}, // XK_plus+AE11+XK_question+XK_backslash: VKEY_OEM_MINUS + {0x002B, 0x14, 0x003F, 0x01F5, + 0xBB}, // XK_plus+AE11+XK_question+XK_odoubleacute: VKEY_OEM_PLUS + {0x002D, 0x15, 0x005F, 0x0000, + 0xBD}, // XK_minus+AE12+XK_underscore+NoSymbol: VKEY_OEM_MINUS + {0x002D, 0x15, 0x005F, 0x03B3, + 0xDB}, // XK_minus+AE12+XK_underscore+XK_rcedilla: VKEY_OEM_4 + {0x002D, 0x3D, 0x005F, 0x0000, + 0xBD}, // XK_minus+AB10+XK_underscore+NoSymbol: VKEY_OEM_MINUS + {0x002D, 0x3D, 0x005F, 0x002A, + 0xBD}, // XK_minus+AB10+XK_underscore+XK_asterisk: VKEY_OEM_MINUS + {0x002D, 0x3D, 0x005F, 0x002F, + 0xBF}, // XK_minus+AB10+XK_underscore+XK_slash: VKEY_OEM_2 + {0x002D, 0x3D, 0x005F, 0x006E, + 0xBD}, // XK_minus+AB10+XK_underscore+XK_n: VKEY_OEM_MINUS + {0x003D, 0x14, 0x0025, 0x0000, + 0xBB}, // XK_equal+AE11+XK_percent+NoSymbol: VKEY_OEM_PLUS + {0x003D, 0x14, 0x0025, 0x002D, + 0xBD}, // XK_equal+AE11+XK_percent+XK_minus: VKEY_OEM_MINUS + {0x005C, 0x31, 0x007C, 0x0031, + 0xDC}, // XK_backslash+TLDE+XK_bar+XK_1: VKEY_OEM_5 + {0x005C, 0x31, 0x007C, 0x03D1, + 0xC0}, // XK_backslash+TLDE+XK_bar+XK_Ncedilla: VKEY_OEM_3 + {0x005C, 0x33, 0x002F, 0x0000, + 0xDC}, // XK_backslash+BKSL+XK_slash+NoSymbol: VKEY_OEM_5 + {0x005C, 0x33, 0x002F, 0x01A3, + 0xDE}, // XK_backslash+BKSL+XK_slash+XK_Lstroke: VKEY_OEM_7 + {0x0060, 0x31, 0x007E, 0x0000, + 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+NoSymbol: VKEY_OEM_3 + {0x0060, 0x31, 0x007E, 0x0031, + 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_1: VKEY_OEM_3 + {0x0060, 0x31, 0x007E, 0x003B, + 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_semicolon: VKEY_OEM_3 + {0x0060, 0x31, 0x007E, 0x0060, + 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_quoteleft: VKEY_OEM_3 + {0x0060, 0x31, 0x007E, 0x00BF, + 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_questiondown: VKEY_OEM_3 + {0x0060, 0x31, 0x007E, 0x01F5, + 0xC0}, // XK_quoteleft+TLDE+XK_asciitilde+XK_odoubleacute: VKEY_OEM_3 + {0x00E4, 0x30, 0x00C4, 0x0000, + 0xDE}, // XK_adiaeresis+AC11+XK_Adiaeresis+NoSymbol: VKEY_OEM_7 + {0x00E4, 0x30, 0x00C4, 0x01A6, + 0xDE}, // XK_adiaeresis+AC11+XK_Adiaeresis+XK_Sacute: VKEY_OEM_7 + {0x00E4, 0x30, 0x00C4, 0x01F8, + 0xDE}, // XK_adiaeresis+AC11+XK_Adiaeresis+XK_rcaron: VKEY_OEM_7 + {0x00E7, 0x2F, 0x00C7, 0x0000, + 0xBA}, // XK_ccedilla+AC10+XK_Ccedilla+NoSymbol: VKEY_OEM_1 + {0x00E7, 0x2F, 0x00C7, 0x00DE, + 0xC0}, // XK_ccedilla+AC10+XK_Ccedilla+XK_Thorn: VKEY_OEM_3 + {0x00F6, 0x2F, 0x00D6, 0x0000, + 0xC0}, // XK_odiaeresis+AC10+XK_Odiaeresis+NoSymbol: VKEY_OEM_3 + {0x00F6, 0x2F, 0x00D6, 0x01DE, + 0xC0}, // XK_odiaeresis+AC10+XK_Odiaeresis+XK_Tcedilla: VKEY_OEM_3 + {0x00FC, 0x14, 0x00DC, 0x0000, + 0xBF}, // XK_udiaeresis+AE11+XK_Udiaeresis+NoSymbol: VKEY_OEM_2 + {0x00FC, 0x22, 0x00DC, 0x0000, + 0xBA}, // XK_udiaeresis+AD11+XK_Udiaeresis+NoSymbol: VKEY_OEM_1 + {0x00FC, 0x22, 0x00DC, 0x01A3, + 0xC0}, // XK_udiaeresis+AD11+XK_Udiaeresis+XK_Lstroke: VKEY_OEM_3 + {0x01EA, 0x3D, 0x01CA, 0x0000, + 0xBD}, // XK_eogonek+AB10+XK_Eogonek+NoSymbol: VKEY_OEM_MINUS + {0x01EA, 0x3D, 0x01CA, 0x006E, + 0xBF}, // XK_eogonek+AB10+XK_Eogonek+XK_n: VKEY_OEM_2 + {0x03E7, 0x22, 0x03C7, 0x0000, + 0xDB}, // XK_iogonek+AD11+XK_Iogonek+NoSymbol: VKEY_OEM_4 + {0x03F9, 0x2F, 0x03D9, 0x0000, + 0xC0}, // XK_uogonek+AC10+XK_Uogonek+NoSymbol: VKEY_OEM_3 + {0x03F9, 0x2F, 0x03D9, 0x01DE, + 0xBA}, // XK_uogonek+AC10+XK_Uogonek+XK_Tcedilla: VKEY_OEM_1 }; template <class T_MAP> diff --git a/chromium/ui/events/keycodes/platform_key_map_win.cc b/chromium/ui/events/keycodes/platform_key_map_win.cc index d7ecacc8553..fd694a984b7 100644 --- a/chromium/ui/events/keycodes/platform_key_map_win.cc +++ b/chromium/ui/events/keycodes/platform_key_map_win.cc @@ -279,7 +279,7 @@ base::LazyInstance<base::ThreadLocalStorage::Slot, // TODO(crbug.com/25503): Controls Control+Alt vs AltGraph disambiguation. const base::Feature kFixAltGraphModifier{"FixAltGraph", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; } // anonymous namespace diff --git a/chromium/ui/events/keycodes/platform_key_map_win.h b/chromium/ui/events/keycodes/platform_key_map_win.h index 53e13ba090f..6d8cfa560d3 100644 --- a/chromium/ui/events/keycodes/platform_key_map_win.h +++ b/chromium/ui/events/keycodes/platform_key_map_win.h @@ -9,12 +9,12 @@ #include <unordered_map> -#include "base/event_types.h" #include "base/hash.h" #include "ui/events/event.h" #include "ui/events/events_export.h" #include "ui/events/keycodes/dom/dom_key.h" #include "ui/events/keycodes/keyboard_codes_win.h" +#include "ui/events/platform_event.h" namespace ui { diff --git a/chromium/ui/events/mojo/BUILD.gn b/chromium/ui/events/mojo/BUILD.gn index 49165b33496..9515ba479c4 100644 --- a/chromium/ui/events/mojo/BUILD.gn +++ b/chromium/ui/events/mojo/BUILD.gn @@ -12,7 +12,7 @@ mojom("interfaces") { ] public_deps = [ - "//mojo/common:common_custom_types", + "//mojo/public/mojom/base", "//ui/gfx/mojo", "//ui/latency/mojo:interfaces", ] diff --git a/chromium/ui/events/mojo/event.mojom b/chromium/ui/events/mojo/event.mojom index 6670751117a..b68dff4550b 100644 --- a/chromium/ui/events/mojo/event.mojom +++ b/chromium/ui/events/mojo/event.mojom @@ -4,7 +4,7 @@ module ui.mojom; -import "mojo/common/time.mojom"; +import "mojo/public/mojom/base/time.mojom"; import "ui/events/mojo/event_constants.mojom"; import "ui/events/mojo/keyboard_codes.mojom"; import "ui/latency/mojo/latency_info.mojom"; @@ -122,6 +122,27 @@ struct GestureData { LocationData location; }; +// Data to support scroll events. +struct ScrollData { + LocationData location; + + // Potential accelerated offsets. + float x_offset; + float y_offset; + // Unaccelerated offsets. + float x_offset_ordinal; + float y_offset_ordinal; + // Number of fingers on the pad. + int32 finger_count; + + // For non-fling events, provides momentum information (e.g. for the case + // where the device provides continuous event updates during a fling). + EventMomentumPhase momentum_phase; + + // Provides phase information if device can provide. + ScrollEventPhase scroll_event_phase; +}; + struct Event { // TODO(sky): rename to type. EventType action; @@ -133,9 +154,10 @@ struct Event { // This value accurately orders events w.r.t. to each other but does not // position them at an absolute time since the TimeTicks origin is only // guaranteed to be fixed during one instance of the application. - mojo.common.mojom.TimeTicks time_stamp; + mojo_base.mojom.TimeTicks time_stamp; LatencyInfo latency; KeyData? key_data; PointerData? pointer_data; GestureData? gesture_data; + ScrollData? scroll_data; }; diff --git a/chromium/ui/events/mojo/event.typemap b/chromium/ui/events/mojo/event.typemap index d4a0fc65357..a294ca2a71d 100644 --- a/chromium/ui/events/mojo/event.typemap +++ b/chromium/ui/events/mojo/event.typemap @@ -7,7 +7,6 @@ public_headers = [ "//ui/events/event.h" ] traits_headers = [ "//ui/events/mojo/event_struct_traits.h" ] public_deps = [ "//mojo/common:common_custom_types", - "//mojo/common:struct_traits", "//ui/events", "//ui/events:dom_keycode_converter", "//ui/latency/mojo:interfaces", @@ -21,4 +20,8 @@ sources = [ ] # TODO(moshayedi): crbug.com/617167. Map mojom.Event directly to ui::Event. -type_mappings = [ "ui.mojom.Event=std::unique_ptr<ui::Event>[move_only]" ] +type_mappings = [ + "ui.mojom.Event=std::unique_ptr<ui::Event>[move_only]", + "ui.mojom.EventMomentumPhase=ui::EventMomentumPhase", + "ui.mojom.ScrollEventPhase=ui::ScrollEventPhase", +] diff --git a/chromium/ui/events/mojo/event_constants.mojom b/chromium/ui/events/mojo/event_constants.mojom index 74ba1dcfd50..f7b27bd9059 100644 --- a/chromium/ui/events/mojo/event_constants.mojom +++ b/chromium/ui/events/mojo/event_constants.mojom @@ -23,6 +23,9 @@ enum EventType { POINTER_WHEEL_CHANGED, MOUSE_EXIT, GESTURE_TAP, + SCROLL, + SCROLL_FLING_START, + SCROLL_FLING_CANCEL, }; // This mirrors ui::EventFlags @@ -63,3 +66,22 @@ enum WheelMode { PAGE, SCALING, }; + +// Phase information used for a ScrollEvent. +// These values match ui::ScrollEventPhase in ui/events/event_constants.h +enum ScrollEventPhase { + kNone, + kBegan, + kUpdate, + kEnd, +}; + +// Momentum phase information used for a ScrollEvent. +// These values match ui::EventMomentumPhase in ui/events/event_constants.h +enum EventMomentumPhase { + NONE, + BEGAN, + MAY_BEGIN, + INERTIAL_UPDATE, + END, +};
\ No newline at end of file diff --git a/chromium/ui/events/mojo/event_struct_traits.cc b/chromium/ui/events/mojo/event_struct_traits.cc index 0be3b783d48..1dd08b6f907 100644 --- a/chromium/ui/events/mojo/event_struct_traits.cc +++ b/chromium/ui/events/mojo/event_struct_traits.cc @@ -4,7 +4,7 @@ #include "ui/events/mojo/event_struct_traits.h" -#include "mojo/common/time_struct_traits.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" #include "ui/events/event.h" #include "ui/events/gesture_event_details.h" #include "ui/events/keycodes/dom/keycode_converter.h" @@ -43,6 +43,15 @@ ui::mojom::EventType UIEventTypeToMojo(ui::EventType type) { case ui::ET_GESTURE_TAP: return ui::mojom::EventType::GESTURE_TAP; + case ui::ET_SCROLL: + return ui::mojom::EventType::SCROLL; + + case ui::ET_SCROLL_FLING_START: + return ui::mojom::EventType::SCROLL_FLING_START; + + case ui::ET_SCROLL_FLING_CANCEL: + return ui::mojom::EventType::SCROLL_FLING_CANCEL; + default: NOTREACHED() << "This unsupported event type will close the connection"; break; @@ -70,6 +79,15 @@ ui::EventType MojoPointerEventTypeToUIEvent(ui::mojom::EventType action) { case ui::mojom::EventType::POINTER_WHEEL_CHANGED: return ui::ET_POINTER_WHEEL_CHANGED; + case ui::mojom::EventType::SCROLL: + return ui::ET_SCROLL; + + case ui::mojom::EventType::SCROLL_FLING_START: + return ui::ET_SCROLL_FLING_START; + + case ui::mojom::EventType::SCROLL_FLING_CANCEL: + return ui::ET_SCROLL_FLING_CANCEL; + default: NOTREACHED(); } @@ -77,7 +95,7 @@ ui::EventType MojoPointerEventTypeToUIEvent(ui::mojom::EventType action) { return ui::ET_UNKNOWN; } -ui::mojom::LocationDataPtr GetLocationData(ui::LocatedEvent* event) { +ui::mojom::LocationDataPtr GetLocationData(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(); @@ -319,6 +337,24 @@ StructTraits<ui::mojom::EventDataView, EventUniquePtr>::gesture_data( return gesture_data; } +ui::mojom::ScrollDataPtr +StructTraits<ui::mojom::EventDataView, EventUniquePtr>::scroll_data( + const EventUniquePtr& event) { + if (!event->IsScrollEvent()) + return nullptr; + + ui::mojom::ScrollDataPtr scroll_data(ui::mojom::ScrollData::New()); + scroll_data->location = GetLocationData(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(); + scroll_data->x_offset_ordinal = scroll_event->x_offset_ordinal(); + scroll_data->y_offset_ordinal = scroll_event->y_offset_ordinal(); + scroll_data->finger_count = scroll_event->finger_count(); + scroll_data->momentum_phase = scroll_event->momentum_phase(); + return scroll_data; +} + bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( ui::mojom::EventDataView event, EventUniquePtr* out) { @@ -386,6 +422,22 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( time_stamp, ui::GestureEventDetails(ui::ET_GESTURE_TAP)); break; } + case ui::mojom::EventType::SCROLL: + case ui::mojom::EventType::SCROLL_FLING_START: + case ui::mojom::EventType::SCROLL_FLING_CANCEL: { + ui::mojom::ScrollDataPtr scroll_data; + if (!event.ReadScrollData<ui::mojom::ScrollDataPtr>(&scroll_data)) + return false; + + *out = std::make_unique<ui::ScrollEvent>( + MojoPointerEventTypeToUIEvent(event.action()), + gfx::Point(scroll_data->location->x, scroll_data->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); + break; + } case ui::mojom::EventType::UNKNOWN: NOTREACHED() << "This unsupported event type will close the connection"; return false; diff --git a/chromium/ui/events/mojo/event_struct_traits.h b/chromium/ui/events/mojo/event_struct_traits.h index 474365cf258..0699a69ed45 100644 --- a/chromium/ui/events/mojo/event_struct_traits.h +++ b/chromium/ui/events/mojo/event_struct_traits.h @@ -5,7 +5,9 @@ #ifndef UI_EVENTS_MOJO_EVENT_STRUCT_TRAITS_H_ #define UI_EVENTS_MOJO_EVENT_STRUCT_TRAITS_H_ +#include "ui/events/event_constants.h" #include "ui/events/mojo/event.mojom.h" +#include "ui/events/mojo/event_constants.mojom.h" namespace ui { class Event; @@ -25,9 +27,91 @@ struct StructTraits<ui::mojom::EventDataView, EventUniquePtr> { static ui::mojom::KeyDataPtr key_data(const EventUniquePtr& event); 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 bool Read(ui::mojom::EventDataView r, EventUniquePtr* out); }; +template <> +struct EnumTraits<ui::mojom::EventMomentumPhase, ui::EventMomentumPhase> { + static ui::mojom::EventMomentumPhase ToMojom(ui::EventMomentumPhase input) { + switch (input) { + case ui::EventMomentumPhase::NONE: + return ui::mojom::EventMomentumPhase::NONE; + case ui::EventMomentumPhase::BEGAN: + return ui::mojom::EventMomentumPhase::BEGAN; + case ui::EventMomentumPhase::MAY_BEGIN: + return ui::mojom::EventMomentumPhase::MAY_BEGIN; + case ui::EventMomentumPhase::INERTIAL_UPDATE: + return ui::mojom::EventMomentumPhase::INERTIAL_UPDATE; + case ui::EventMomentumPhase::END: + return ui::mojom::EventMomentumPhase::END; + } + NOTREACHED(); + return ui::mojom::EventMomentumPhase::NONE; + } + + static bool FromMojom(ui::mojom::EventMomentumPhase input, + ui::EventMomentumPhase* out) { + switch (input) { + case ui::mojom::EventMomentumPhase::NONE: + *out = ui::EventMomentumPhase::NONE; + return true; + case ui::mojom::EventMomentumPhase::BEGAN: + *out = ui::EventMomentumPhase::BEGAN; + return true; + case ui::mojom::EventMomentumPhase::MAY_BEGIN: + *out = ui::EventMomentumPhase::MAY_BEGIN; + return true; + case ui::mojom::EventMomentumPhase::INERTIAL_UPDATE: + *out = ui::EventMomentumPhase::INERTIAL_UPDATE; + return true; + case ui::mojom::EventMomentumPhase::END: + *out = ui::EventMomentumPhase::END; + return true; + } + NOTREACHED(); + return false; + } +}; + +template <> +struct EnumTraits<ui::mojom::ScrollEventPhase, ui::ScrollEventPhase> { + static ui::mojom::ScrollEventPhase ToMojom(ui::ScrollEventPhase input) { + switch (input) { + case ui::ScrollEventPhase::kNone: + return ui::mojom::ScrollEventPhase::kNone; + case ui::ScrollEventPhase::kBegan: + return ui::mojom::ScrollEventPhase::kBegan; + case ui::ScrollEventPhase::kUpdate: + return ui::mojom::ScrollEventPhase::kUpdate; + case ui::ScrollEventPhase::kEnd: + return ui::mojom::ScrollEventPhase::kEnd; + } + NOTREACHED(); + return ui::mojom::ScrollEventPhase::kNone; + } + + static bool FromMojom(ui::mojom::ScrollEventPhase input, + ui::ScrollEventPhase* out) { + switch (input) { + case ui::mojom::ScrollEventPhase::kNone: + *out = ui::ScrollEventPhase::kNone; + return true; + case ui::mojom::ScrollEventPhase::kBegan: + *out = ui::ScrollEventPhase::kBegan; + return true; + case ui::mojom::ScrollEventPhase::kUpdate: + *out = ui::ScrollEventPhase::kUpdate; + return true; + case ui::mojom::ScrollEventPhase::kEnd: + *out = ui::ScrollEventPhase::kEnd; + return true; + } + NOTREACHED(); + return false; + } +}; + } // namespace mojo #endif // UI_EVENTS_MOJO_EVENT_STRUCT_TRAITS_H_ diff --git a/chromium/ui/events/mojo/struct_traits_unittest.cc b/chromium/ui/events/mojo/struct_traits_unittest.cc index 90c1eddfced..4d136690dca 100644 --- a/chromium/ui/events/mojo/struct_traits_unittest.cc +++ b/chromium/ui/events/mojo/struct_traits_unittest.cc @@ -5,7 +5,7 @@ #include <utility> #include "base/message_loop/message_loop.h" -#include "mojo/common/time_struct_traits.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event.h" @@ -250,4 +250,59 @@ TEST_F(StructTraitsTest, GestureEvent) { } } +TEST_F(StructTraitsTest, ScrollEvent) { + 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}, + {ET_SCROLL, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(501), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::NONE, ScrollEventPhase::kUpdate}, + {ET_SCROLL, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(501), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::NONE, ScrollEventPhase::kBegan}, + {ET_SCROLL, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(501), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::NONE, ScrollEventPhase::kEnd}, + {ET_SCROLL, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(501), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::BEGAN, ScrollEventPhase::kNone}, + {ET_SCROLL, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(501), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::INERTIAL_UPDATE, + ScrollEventPhase::kNone}, + {ET_SCROLL, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(501), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::END, ScrollEventPhase::kNone}, + {ET_SCROLL_FLING_START, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(502), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::MAY_BEGIN, ScrollEventPhase::kNone}, + {ET_SCROLL_FLING_CANCEL, gfx::Point(10, 20), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(502), EF_NONE, 1, + 2, 3, 4, 5, EventMomentumPhase::END, ScrollEventPhase::kNone}, + }; + + mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); + for (size_t i = 0; i < arraysize(kTestData); i++) { + std::unique_ptr<Event> output; + proxy->EchoEvent(Event::Clone(kTestData[i]), &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()); + 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(), + output_ptr_event->x_offset_ordinal()); + EXPECT_EQ(kTestData[i].y_offset_ordinal(), + output_ptr_event->y_offset_ordinal()); + EXPECT_EQ(kTestData[i].finger_count(), output_ptr_event->finger_count()); + EXPECT_EQ(kTestData[i].momentum_phase(), + output_ptr_event->momentum_phase()); + } +} + } // namespace ui diff --git a/chromium/ui/events/ozone/device/device_manager.cc b/chromium/ui/events/ozone/device/device_manager.cc index 59db5c5d94c..7952fcbb7f2 100644 --- a/chromium/ui/events/ozone/device/device_manager.cc +++ b/chromium/ui/events/ozone/device/device_manager.cc @@ -4,7 +4,6 @@ #include "ui/events/ozone/device/device_manager.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #if defined(USE_UDEV) diff --git a/chromium/ui/events/ozone/device/device_manager_manual.cc b/chromium/ui/events/ozone/device/device_manager_manual.cc index 8010870abd4..9a68ca01742 100644 --- a/chromium/ui/events/ozone/device/device_manager_manual.cc +++ b/chromium/ui/events/ozone/device/device_manager_manual.cc @@ -17,11 +17,11 @@ namespace ui { namespace { -const char kDevInput[] = "/dev/input"; +const base::FilePath::CharType kDevInput[] = FILE_PATH_LITERAL("/dev/input"); void ScanDevicesOnWorkerThread(std::vector<base::FilePath>* result) { - base::FileEnumerator file_enum(base::FilePath(FILE_PATH_LITERAL(kDevInput)), - false, base::FileEnumerator::FILES, + base::FileEnumerator file_enum(base::FilePath(kDevInput), false, + base::FileEnumerator::FILES, FILE_PATH_LITERAL("event*[0-9]")); for (base::FilePath path = file_enum.Next(); !path.empty(); path = file_enum.Next()) { @@ -58,7 +58,7 @@ void DeviceManagerManual::RemoveObserver(DeviceEventObserver* observer) { } void DeviceManagerManual::StartWatching() { - if (!watcher_.Watch(base::FilePath(FILE_PATH_LITERAL(kDevInput)), false, + if (!watcher_.Watch(base::FilePath(kDevInput), false, base::Bind(&DeviceManagerManual::OnWatcherEvent, weak_ptr_factory_.GetWeakPtr()))) { LOG(ERROR) << "Failed to start FilePathWatcher"; @@ -70,9 +70,9 @@ void DeviceManagerManual::InitiateScanDevices() { base::PostTaskWithTraitsAndReply( FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, - base::Bind(&ScanDevicesOnWorkerThread, result), - base::Bind(&DeviceManagerManual::OnDevicesScanned, - weak_ptr_factory_.GetWeakPtr(), base::Owned(result))); + base::BindOnce(&ScanDevicesOnWorkerThread, result), + base::BindOnce(&DeviceManagerManual::OnDevicesScanned, + weak_ptr_factory_.GetWeakPtr(), base::Owned(result))); } void DeviceManagerManual::OnDevicesScanned( diff --git a/chromium/ui/events/ozone/device/udev/device_manager_udev.cc b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc index 655285f6fd2..16421005e1e 100644 --- a/chromium/ui/events/ozone/device/udev/device_manager_udev.cc +++ b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc @@ -7,7 +7,6 @@ #include <stddef.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" diff --git a/chromium/ui/events/ozone/device/udev/device_manager_udev.h b/chromium/ui/events/ozone/device/udev/device_manager_udev.h index e7a3425c35c..3e1a6feabac 100644 --- a/chromium/ui/events/ozone/device/udev/device_manager_udev.h +++ b/chromium/ui/events/ozone/device/udev/device_manager_udev.h @@ -16,8 +16,8 @@ namespace ui { class DeviceEvent; class DeviceEventObserver; -class DeviceManagerUdev - : public DeviceManager, base::MessagePumpLibevent::Watcher { +class DeviceManagerUdev : public DeviceManager, + base::MessagePumpLibevent::FdWatcher { public: DeviceManagerUdev(); ~DeviceManagerUdev() override; @@ -33,14 +33,14 @@ class DeviceManagerUdev void AddObserver(DeviceEventObserver* observer) override; void RemoveObserver(DeviceEventObserver* observer) override; - // base::MessagePumpLibevent::Watcher overrides: + // base::MessagePumpLibevent::FdWatcher overrides: void OnFileCanReadWithoutBlocking(int fd) override; void OnFileCanWriteWithoutBlocking(int fd) override; device::ScopedUdevPtr udev_; device::ScopedUdevMonitorPtr monitor_; - base::MessagePumpLibevent::FileDescriptorWatcher controller_; + base::MessagePumpLibevent::FdWatchController controller_; base::ObserverList<DeviceEventObserver> observers_; diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/event_converter_evdev.cc index bf14bd57f40..dc766606cb8 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev.cc @@ -24,6 +24,7 @@ EventConverterEvdev::EventConverterEvdev(int fd, int id, InputDeviceType type, const std::string& name, + const std::string& phys, uint16_t vendor_id, uint16_t product_id) : fd_(fd), @@ -31,6 +32,7 @@ EventConverterEvdev::EventConverterEvdev(int fd, input_device_(id, type, name, + phys, GetInputPathInSys(path), vendor_id, product_id), diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev.h b/chromium/ui/events/ozone/evdev/event_converter_evdev.h index 76f63fb0b98..0b786379d0b 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev.h +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev.h @@ -24,13 +24,14 @@ namespace ui { enum class DomCode; class EVENTS_OZONE_EVDEV_EXPORT EventConverterEvdev - : public base::MessagePumpLibevent::Watcher { + : public base::MessagePumpLibevent::FdWatcher { public: EventConverterEvdev(int fd, const base::FilePath& path, int id, InputDeviceType type, const std::string& name, + const std::string& phys, uint16_t vendor_id, uint16_t product_id); ~EventConverterEvdev() override; @@ -115,7 +116,7 @@ class EVENTS_OZONE_EVDEV_EXPORT EventConverterEvdev static base::TimeTicks TimeTicksFromInputEvent(const input_event& event); protected: - // base::MessagePumpLibevent::Watcher: + // base::MessagePumpLibevent::FdWatcher: void OnFileCanWriteWithoutBlocking(int fd) override; // File descriptor to read. @@ -132,7 +133,7 @@ class EVENTS_OZONE_EVDEV_EXPORT EventConverterEvdev bool watching_ = false; // Controller for watching the input fd. - base::MessagePumpLibevent::FileDescriptorWatcher controller_; + base::MessagePumpLibevent::FdWatchController controller_; private: DISALLOW_COPY_AND_ASSIGN(EventConverterEvdev); diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc index 161065a0752..eb8923faf28 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.cc @@ -41,6 +41,7 @@ EventConverterEvdevImpl::EventConverterEvdevImpl( id, devinfo.device_type(), devinfo.name(), + devinfo.phys(), devinfo.vendor_id(), devinfo.product_id()), input_device_fd_(std::move(fd)), diff --git a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h index c2b5f8e4202..94e4a36a30c 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h +++ b/chromium/ui/events/ozone/evdev/event_converter_evdev_impl.h @@ -82,7 +82,7 @@ class EVENTS_OZONE_EVDEV_EXPORT EventConverterEvdevImpl int y_offset_ = 0; // Controller for watching the input fd. - base::MessagePumpLibevent::FileDescriptorWatcher controller_; + base::MessagePumpLibevent::FdWatchController controller_; // The evdev codes of the keys which should be blocked. std::bitset<KEY_CNT> blocked_keys_; diff --git a/chromium/ui/events/ozone/evdev/event_converter_test_util.cc b/chromium/ui/events/ozone/evdev/event_converter_test_util.cc index 9437c54cd3c..52d339cb8c2 100644 --- a/chromium/ui/events/ozone/evdev/event_converter_test_util.cc +++ b/chromium/ui/events/ozone/evdev/event_converter_test_util.cc @@ -6,7 +6,6 @@ #include <stdint.h> -#include "base/memory/ptr_util.h" #include "ui/events/ozone/device/device_manager.h" #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h" #include "ui/events/ozone/evdev/event_factory_evdev.h" @@ -110,8 +109,7 @@ class TestEventFactoryEvdev : public EventFactoryEvdev { ~TestEventFactoryEvdev() override {} private: - uint32_t DispatchEvent(PlatformEvent platform_event) override { - Event* event = static_cast<Event*>(platform_event); + uint32_t DispatchEvent(PlatformEvent event) override { callback_.Run(event); return POST_DISPATCH_NONE; } diff --git a/chromium/ui/events/ozone/evdev/event_device_info.cc b/chromium/ui/events/ozone/evdev/event_device_info.cc index 09bc96ff19c..c04cf9c51b4 100644 --- a/chromium/ui/events/ozone/evdev/event_device_info.cc +++ b/chromium/ui/events/ozone/evdev/event_device_info.cc @@ -87,6 +87,15 @@ bool GetDeviceIdentifiers(int fd, return true; } +void GetDevicePhysInfo(int fd, const base::FilePath& path, std::string* phys) { + char device_phys[kMaximumDeviceNameLength]; + if (ioctl(fd, EVIOCGPHYS(kMaximumDeviceNameLength - 1), &device_phys) < 0) { + PLOG(INFO) << "Failed EVIOCGPHYS (path=" << path.value() << ")"; + return; + } + *phys = device_phys; +} + // |request| needs to be the equivalent to: // struct input_mt_request_layout { // uint32_t code; @@ -184,6 +193,8 @@ bool EventDeviceInfo::Initialize(int fd, const base::FilePath& path) { if (!GetDeviceIdentifiers(fd, path, &vendor_id_, &product_id_)) return false; + GetDevicePhysInfo(fd, path, &phys_); + device_type_ = GetInputDeviceTypeFromPath(path); return true; diff --git a/chromium/ui/events/ozone/evdev/event_device_info.h b/chromium/ui/events/ozone/evdev/event_device_info.h index 24b79806751..e7624594c05 100644 --- a/chromium/ui/events/ozone/evdev/event_device_info.h +++ b/chromium/ui/events/ozone/evdev/event_device_info.h @@ -93,6 +93,7 @@ class EVENTS_OZONE_EVDEV_EXPORT EventDeviceInfo { // Device identification. const std::string& name() const { return name_; } + const std::string& phys() const { return phys_; } uint16_t vendor_id() const { return vendor_id_; } uint16_t product_id() const { return product_id_; } @@ -177,6 +178,10 @@ class EVENTS_OZONE_EVDEV_EXPORT EventDeviceInfo { uint16_t vendor_id_; uint16_t product_id_; + // Device evdev physical property containing the output for EVIOCGPHYS that is + // (supposed to be) stable between reboots and hotplugs. + std::string phys_; + // Whether this is an internal or external device. InputDeviceType device_type_ = 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 92c84d05be6..2d44b91a0ea 100644 --- a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc @@ -7,7 +7,6 @@ #include <utility> #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/task_runner.h" #include "base/threading/thread_task_runner_handle.h" @@ -44,97 +43,101 @@ class ProxyDeviceEventDispatcher : public DeviceEventDispatcherEvdev { // DeviceEventDispatcher: void DispatchKeyEvent(const KeyEventParams& params) override { - ui_thread_runner_->PostTask(FROM_HERE, - base::Bind(&EventFactoryEvdev::DispatchKeyEvent, - event_factory_evdev_, params)); + ui_thread_runner_->PostTask( + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchKeyEvent, + event_factory_evdev_, params)); } void DispatchMouseMoveEvent(const MouseMoveEventParams& params) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchMouseMoveEvent, - event_factory_evdev_, params)); + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchMouseMoveEvent, + event_factory_evdev_, params)); } void DispatchMouseButtonEvent(const MouseButtonEventParams& params) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchMouseButtonEvent, - event_factory_evdev_, params)); + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchMouseButtonEvent, + event_factory_evdev_, params)); } void DispatchMouseWheelEvent(const MouseWheelEventParams& params) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchMouseWheelEvent, - event_factory_evdev_, params)); + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchMouseWheelEvent, + event_factory_evdev_, params)); } void DispatchPinchEvent(const PinchEventParams& params) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchPinchEvent, - event_factory_evdev_, params)); + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchPinchEvent, + event_factory_evdev_, params)); } void DispatchScrollEvent(const ScrollEventParams& params) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchScrollEvent, - event_factory_evdev_, params)); + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchScrollEvent, + event_factory_evdev_, params)); } void DispatchTouchEvent(const TouchEventParams& params) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchTouchEvent, - event_factory_evdev_, params)); + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchTouchEvent, + event_factory_evdev_, params)); } void DispatchGamepadEvent(const GamepadEvent& event) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchGamepadEvent, - event_factory_evdev_, event)); + FROM_HERE, base::BindOnce(&EventFactoryEvdev::DispatchGamepadEvent, + event_factory_evdev_, event)); } void DispatchKeyboardDevicesUpdated( const std::vector<InputDevice>& devices) override { ui_thread_runner_->PostTask( FROM_HERE, - base::Bind(&EventFactoryEvdev::DispatchKeyboardDevicesUpdated, - event_factory_evdev_, devices)); + base::BindOnce(&EventFactoryEvdev::DispatchKeyboardDevicesUpdated, + event_factory_evdev_, devices)); } void DispatchTouchscreenDevicesUpdated( const std::vector<TouchscreenDevice>& devices) override { ui_thread_runner_->PostTask( FROM_HERE, - base::Bind(&EventFactoryEvdev::DispatchTouchscreenDevicesUpdated, - event_factory_evdev_, devices)); + base::BindOnce(&EventFactoryEvdev::DispatchTouchscreenDevicesUpdated, + event_factory_evdev_, devices)); } void DispatchMouseDevicesUpdated( const std::vector<InputDevice>& devices) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchMouseDevicesUpdated, - event_factory_evdev_, devices)); + FROM_HERE, + base::BindOnce(&EventFactoryEvdev::DispatchMouseDevicesUpdated, + event_factory_evdev_, devices)); } void DispatchTouchpadDevicesUpdated( const std::vector<InputDevice>& devices) override { ui_thread_runner_->PostTask( FROM_HERE, - base::Bind(&EventFactoryEvdev::DispatchTouchpadDevicesUpdated, - event_factory_evdev_, devices)); + base::BindOnce(&EventFactoryEvdev::DispatchTouchpadDevicesUpdated, + event_factory_evdev_, devices)); } void DispatchDeviceListsComplete() override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchDeviceListsComplete, - event_factory_evdev_)); + FROM_HERE, + base::BindOnce(&EventFactoryEvdev::DispatchDeviceListsComplete, + event_factory_evdev_)); } void DispatchStylusStateChanged(StylusState stylus_state) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchStylusStateChanged, - event_factory_evdev_, stylus_state)); + FROM_HERE, + base::BindOnce(&EventFactoryEvdev::DispatchStylusStateChanged, + event_factory_evdev_, stylus_state)); } void DispatchGamepadDevicesUpdated( const std::vector<InputDevice>& devices) override { ui_thread_runner_->PostTask( - FROM_HERE, base::Bind(&EventFactoryEvdev::DispatchGamepadDevicesUpdated, - event_factory_evdev_, devices)); + FROM_HERE, + base::BindOnce(&EventFactoryEvdev::DispatchGamepadDevicesUpdated, + event_factory_evdev_, devices)); } private: @@ -446,12 +449,12 @@ void EventFactoryEvdev::WarpCursorTo(gfx::AcceleratedWidget widget, base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&EventFactoryEvdev::DispatchMouseMoveEvent, - weak_ptr_factory_.GetWeakPtr(), - MouseMoveEventParams( - -1 /* device_id */, EF_NONE, cursor_->GetLocation(), - PointerDetails(EventPointerType::POINTER_TYPE_MOUSE), - EventTimeForNow()))); + base::BindOnce(&EventFactoryEvdev::DispatchMouseMoveEvent, + weak_ptr_factory_.GetWeakPtr(), + MouseMoveEventParams( + -1 /* device_id */, EF_NONE, cursor_->GetLocation(), + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE), + EventTimeForNow()))); } int EventFactoryEvdev::NextDeviceId() { diff --git a/chromium/ui/events/ozone/evdev/event_thread_evdev.cc b/chromium/ui/events/ozone/evdev/event_thread_evdev.cc index d0dc561aafd..1dcc69740ab 100644 --- a/chromium/ui/events/ozone/evdev/event_thread_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_thread_evdev.cc @@ -48,7 +48,7 @@ class EvdevThread : public base::Thread { cursor_->InitializeOnEvdev(); init_runner_->PostTask(FROM_HERE, - base::Bind(init_callback_, base::Passed(&proxy))); + base::BindOnce(init_callback_, std::move(proxy))); } void CleanUp() override { diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc index c1714879a51..3aec220b996 100644 --- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev.cc @@ -99,6 +99,7 @@ GamepadEventConverterEvdev::GamepadEventConverterEvdev( id, devinfo.device_type(), devinfo.name(), + devinfo.phys(), devinfo.vendor_id(), devinfo.product_id()), will_send_frame_(false), diff --git a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc index d3123c8900f..b768ad655ad 100644 --- a/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc +++ b/chromium/ui/events/ozone/evdev/gamepad_event_converter_evdev_unittest.cc @@ -18,7 +18,6 @@ #include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/posix/eintr_wrapper.h" #include "base/run_loop.h" #include "base/time/time.h" diff --git a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc index 86a65521954..3834a69b344 100644 --- a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc +++ b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc @@ -9,7 +9,6 @@ #include <algorithm> #include "base/callback.h" -#include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/events/devices/device_data_manager.h" #include "ui/events/ozone/evdev/input_device_factory_evdev_proxy.h" @@ -183,8 +182,8 @@ void InputControllerEvdev::ScheduleUpdateDeviceSettings() { if (!input_device_factory_ || settings_update_pending_) return; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&InputControllerEvdev::UpdateDeviceSettings, - weak_ptr_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&InputControllerEvdev::UpdateDeviceSettings, + weak_ptr_factory_.GetWeakPtr())); settings_update_pending_ = true; } diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc index 4cc71e34d38..28e0c5dffb5 100644 --- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc @@ -189,8 +189,8 @@ void InputDeviceFactoryEvdev::AddInputDevice(int id, base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&InputDeviceFactoryEvdev::AttachInputDevice, - weak_ptr_factory_.GetWeakPtr(), base::Passed(&converter))); + base::BindOnce(&InputDeviceFactoryEvdev::AttachInputDevice, + weak_ptr_factory_.GetWeakPtr(), std::move(converter))); ++pending_device_changes_; } diff --git a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc index 2696967a0cb..af6a0f7ed92 100644 --- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev_proxy.cc @@ -43,35 +43,36 @@ InputDeviceFactoryEvdevProxy::~InputDeviceFactoryEvdevProxy() { void InputDeviceFactoryEvdevProxy::AddInputDevice(int id, const base::FilePath& path) { - task_runner_->PostTask(FROM_HERE, - base::Bind(&InputDeviceFactoryEvdev::AddInputDevice, - input_device_factory_, id, path)); + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&InputDeviceFactoryEvdev::AddInputDevice, + input_device_factory_, id, path)); } void InputDeviceFactoryEvdevProxy::RemoveInputDevice( const base::FilePath& path) { - task_runner_->PostTask(FROM_HERE, - base::Bind(&InputDeviceFactoryEvdev::RemoveInputDevice, - input_device_factory_, path)); + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&InputDeviceFactoryEvdev::RemoveInputDevice, + input_device_factory_, path)); } void InputDeviceFactoryEvdevProxy::OnStartupScanComplete() { task_runner_->PostTask( - FROM_HERE, base::Bind(&InputDeviceFactoryEvdev::OnStartupScanComplete, - input_device_factory_)); + FROM_HERE, base::BindOnce(&InputDeviceFactoryEvdev::OnStartupScanComplete, + input_device_factory_)); } void InputDeviceFactoryEvdevProxy::SetCapsLockLed(bool enabled) { - task_runner_->PostTask(FROM_HERE, - base::Bind(&InputDeviceFactoryEvdev::SetCapsLockLed, - input_device_factory_, enabled)); + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&InputDeviceFactoryEvdev::SetCapsLockLed, + input_device_factory_, enabled)); } void InputDeviceFactoryEvdevProxy::UpdateInputDeviceSettings( const InputDeviceSettingsEvdev& settings) { task_runner_->PostTask( - FROM_HERE, base::Bind(&InputDeviceFactoryEvdev::UpdateInputDeviceSettings, - input_device_factory_, settings)); + FROM_HERE, + base::BindOnce(&InputDeviceFactoryEvdev::UpdateInputDeviceSettings, + input_device_factory_, settings)); } void InputDeviceFactoryEvdevProxy::GetTouchDeviceStatus( diff --git a/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc b/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc index 9ec3121dbb0..98c868de9ed 100644 --- a/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc +++ b/chromium/ui/events/ozone/evdev/libgestures_glue/event_reader_libevdev_cros.cc @@ -38,6 +38,7 @@ EventReaderLibevdevCros::EventReaderLibevdevCros( id, devinfo.device_type(), devinfo.name(), + devinfo.phys(), devinfo.vendor_id(), devinfo.product_id()), has_keyboard_(devinfo.HasKeyboard()), diff --git a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc index d4f5af789ad..f7597bb4b53 100644 --- a/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc +++ b/chromium/ui/events/ozone/evdev/libgestures_glue/gesture_interpreter_libevdev_cros.cc @@ -211,6 +211,14 @@ void GestureInterpreterLibevdevCros::OnLibEvdevCrosEvent(Evdev* evdev, hwstate.buttons_down |= GESTURES_BUTTON_FORWARD; } + // Check if this event has an MSC_TIMESTAMP field + if (EvdevBitIsSet(evdev->info.msc_bitmask, MSC_TIMESTAMP)) { + hwstate.msc_timestamp = static_cast<stime_t>(Event_Get_Timestamp(evdev)) / + base::Time::kMicrosecondsPerSecond; + } else { + hwstate.msc_timestamp = 0.0; + } + GestureInterpreterPushHardwareState(interpreter_, &hwstate); } diff --git a/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.cc index 531268241d8..b702ea1dceb 100644 --- a/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.cc @@ -42,6 +42,7 @@ TabletEventConverterEvdev::TabletEventConverterEvdev( id, info.device_type(), info.name(), + info.phys(), info.vendor_id(), info.product_id()), input_device_fd_(std::move(fd)), diff --git a/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.h b/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.h index 6d22b98d9db..f6bf158abaf 100644 --- a/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.h +++ b/chromium/ui/events/ozone/evdev/tablet_event_converter_evdev.h @@ -53,7 +53,7 @@ class EVENTS_OZONE_EVDEV_EXPORT TabletEventConverterEvdev base::ScopedFD input_device_fd_; // Controller for watching the input fd. - base::MessagePumpLibevent::FileDescriptorWatcher controller_; + base::MessagePumpLibevent::FdWatchController controller_; // Shared cursor state. CursorDelegateEvdev* cursor_; diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc index 7141819c6d4..6818808166c 100644 --- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc +++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.cc @@ -125,6 +125,7 @@ TouchEventConverterEvdev::TouchEventConverterEvdev( id, devinfo.device_type(), devinfo.name(), + devinfo.phys(), devinfo.vendor_id(), devinfo.product_id()), input_device_fd_(std::move(fd)), diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h index 7e5c6c0756e..02022f6ff08 100644 --- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h +++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev.h @@ -68,7 +68,7 @@ class EVENTS_OZONE_EVDEV_EXPORT TouchEventConverterEvdev private: friend class MockTouchEventConverterEvdev; - // Overidden from base::MessagePumpLibevent::Watcher. + // Overidden from base::MessagePumpLibevent::FdWatcher. void OnFileCanReadWithoutBlocking(int fd) override; virtual void Reinitialize(); diff --git a/chromium/ui/events/ozone/evdev/touch_filter/false_touch_finder.cc b/chromium/ui/events/ozone/evdev/touch_filter/false_touch_finder.cc index a5d1a483ddd..1b0d7121c12 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/false_touch_finder.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/false_touch_finder.cc @@ -4,7 +4,6 @@ #include "ui/events/ozone/evdev/touch_filter/false_touch_finder.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "ui/events/event_utils.h" #include "ui/events/ozone/evdev/touch_filter/edge_touch_filter.h" diff --git a/chromium/ui/events/ozone/events_ozone.cc b/chromium/ui/events/ozone/events_ozone.cc index 94c0427aba4..a9dae509d19 100644 --- a/chromium/ui/events/ozone/events_ozone.cc +++ b/chromium/ui/events/ozone/events_ozone.cc @@ -9,28 +9,31 @@ namespace ui { void DispatchEventFromNativeUiEvent( - const base::NativeEvent& native_event, + const PlatformEvent& event, base::OnceCallback<void(ui::Event*)> callback) { - ui::Event* native_ui_event = static_cast<ui::Event*>(native_event); - if (native_ui_event->IsKeyEvent()) { - ui::KeyEvent key_event(native_event); + // NB: ui::Events are constructed here using the overload that takes a + // const PlatformEvent& (here ui::Event* const&) rather than the copy + // constructor. This has side effects and cannot be changed to use the + // copy constructor or Event::Clone. + if (event->IsKeyEvent()) { + ui::KeyEvent key_event(event); std::move(callback).Run(&key_event); - } else if (native_ui_event->IsMouseWheelEvent()) { - ui::MouseWheelEvent wheel_event(native_event); + } else if (event->IsMouseWheelEvent()) { + ui::MouseWheelEvent wheel_event(event); std::move(callback).Run(&wheel_event); - } else if (native_ui_event->IsMouseEvent()) { - ui::MouseEvent mouse_event(native_event); + } else if (event->IsMouseEvent()) { + ui::MouseEvent mouse_event(event); std::move(callback).Run(&mouse_event); - } else if (native_ui_event->IsTouchEvent()) { - ui::TouchEvent touch_event(native_event); + } else if (event->IsTouchEvent()) { + ui::TouchEvent touch_event(event); std::move(callback).Run(&touch_event); - } else if (native_ui_event->IsScrollEvent()) { - ui::ScrollEvent scroll_event(native_event); + } else if (event->IsScrollEvent()) { + ui::ScrollEvent scroll_event(event); std::move(callback).Run(&scroll_event); - } else if (native_ui_event->IsGestureEvent()) { - std::move(callback).Run(native_ui_event); + } else if (event->IsGestureEvent()) { + std::move(callback).Run(event); // TODO(mohsen): Use the same pattern for scroll/touch/wheel events. - // Apparently, there is no need for them to wrap the |native_event|. + // Apparently, there is no need for them to wrap the |event|. } else { NOTREACHED(); } diff --git a/chromium/ui/events/ozone/events_ozone.h b/chromium/ui/events/ozone/events_ozone.h index 23935f35f1f..6a893e1e42b 100644 --- a/chromium/ui/events/ozone/events_ozone.h +++ b/chromium/ui/events/ozone/events_ozone.h @@ -6,8 +6,8 @@ #define UI_EVENTS_OZONE_EVENTS_OZONE_H_ #include "base/callback.h" -#include "base/event_types.h" #include "ui/events/events_export.h" +#include "ui/events/platform_event.h" namespace ui { @@ -31,7 +31,7 @@ class Event; // define NativeEvent == ui::Event. // EVENTS_EXPORT void DispatchEventFromNativeUiEvent( - const base::NativeEvent& native_event, + const PlatformEvent& native_event, base::OnceCallback<void(ui::Event*)> callback); } // namespace ui diff --git a/chromium/ui/events/ozone/gamepad/gamepad_mapping.cc b/chromium/ui/events/ozone/gamepad/gamepad_mapping.cc index 6e1d96cb331..26c6b73c5f4 100644 --- a/chromium/ui/events/ozone/gamepad/gamepad_mapping.cc +++ b/chromium/ui/events/ozone/gamepad/gamepad_mapping.cc @@ -6,7 +6,6 @@ #include <memory> -#include "base/memory/ptr_util.h" #include "ui/events/ozone/evdev/event_device_info.h" #include "ui/events/ozone/gamepad/generic_gamepad_mapping.h" #include "ui/events/ozone/gamepad/static_gamepad_mapping.h" diff --git a/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping.cc b/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping.cc index 3b0ef4d3557..d18ff455bfc 100644 --- a/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping.cc +++ b/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping.cc @@ -11,7 +11,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "ui/events/ozone/evdev/event_device_info.h" #include "ui/events/ozone/gamepad/generic_gamepad_mapping.h" #include "ui/events/ozone/gamepad/webgamepad_constants.h" diff --git a/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping_unittest.cc b/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping_unittest.cc index 7ac37b4d623..2f40517f93a 100644 --- a/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping_unittest.cc +++ b/chromium/ui/events/ozone/gamepad/generic_gamepad_mapping_unittest.cc @@ -17,7 +17,6 @@ #include "base/bind.h" #include "base/files/file_util.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/posix/eintr_wrapper.h" #include "base/run_loop.h" #include "base/time/time.h" diff --git a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc index fb89349e6dc..0830460bc98 100644 --- a/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc +++ b/chromium/ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine_unittest.cc @@ -8,7 +8,6 @@ #include <stdint.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/dom/dom_code.h" diff --git a/chromium/ui/events/platform/BUILD.gn b/chromium/ui/events/platform/BUILD.gn index 5055e6c0688..dfceb31d1de 100644 --- a/chromium/ui/events/platform/BUILD.gn +++ b/chromium/ui/events/platform/BUILD.gn @@ -15,7 +15,6 @@ jumbo_component("platform") { "platform_event_source.cc", "platform_event_source.h", "platform_event_source_stub.cc", - "platform_event_types.h", "scoped_event_dispatcher.cc", "scoped_event_dispatcher.h", ] @@ -24,6 +23,7 @@ jumbo_component("platform") { deps = [ "//base", + "//ui/events:platform_event", ] if (use_x11) { diff --git a/chromium/ui/events/platform/platform_event_dispatcher.h b/chromium/ui/events/platform/platform_event_dispatcher.h index 294bdb9a691..d54c5101acd 100644 --- a/chromium/ui/events/platform/platform_event_dispatcher.h +++ b/chromium/ui/events/platform/platform_event_dispatcher.h @@ -8,7 +8,7 @@ #include <stdint.h> #include "ui/events/events_export.h" -#include "ui/events/platform/platform_event_types.h" +#include "ui/events/platform_event.h" namespace ui { diff --git a/chromium/ui/events/platform/platform_event_observer.h b/chromium/ui/events/platform/platform_event_observer.h index 37aec638ea5..3b1cc9e4e64 100644 --- a/chromium/ui/events/platform/platform_event_observer.h +++ b/chromium/ui/events/platform/platform_event_observer.h @@ -6,7 +6,7 @@ #define UI_EVENTS_PLATFORM_PLATFORM_EVENT_OBSERVER_H_ #include "ui/events/events_export.h" -#include "ui/events/platform/platform_event_types.h" +#include "ui/events/platform_event.h" namespace ui { diff --git a/chromium/ui/events/platform/platform_event_source.cc b/chromium/ui/events/platform/platform_event_source.cc index 9e8324389f7..cdd9ce7319d 100644 --- a/chromium/ui/events/platform/platform_event_source.cc +++ b/chromium/ui/events/platform/platform_event_source.cc @@ -7,7 +7,6 @@ #include <algorithm> #include "base/lazy_instance.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/threading/thread_local.h" #include "ui/events/platform/platform_event_dispatcher.h" diff --git a/chromium/ui/events/platform/platform_event_source.h b/chromium/ui/events/platform/platform_event_source.h index b4e3084dd1f..dae150479b1 100644 --- a/chromium/ui/events/platform/platform_event_source.h +++ b/chromium/ui/events/platform/platform_event_source.h @@ -15,7 +15,7 @@ #include "base/macros.h" #include "base/observer_list.h" #include "ui/events/events_export.h" -#include "ui/events/platform/platform_event_types.h" +#include "ui/events/platform_event.h" namespace ui { @@ -78,6 +78,8 @@ class EVENTS_EXPORT PlatformEventSource { // current message-loop iteration. virtual uint32_t DispatchEvent(PlatformEvent platform_event); + base::ObserverList<PlatformEventObserver>& observers() { return observers_; } + private: friend class ScopedEventDispatcher; friend class test::PlatformEventSourceTestAPI; diff --git a/chromium/ui/events/platform/platform_event_source_unittest.cc b/chromium/ui/events/platform/platform_event_source_unittest.cc index ccaa3681499..d9a069e42cb 100644 --- a/chromium/ui/events/platform/platform_event_source_unittest.cc +++ b/chromium/ui/events/platform/platform_event_source_unittest.cc @@ -13,10 +13,10 @@ #include "base/bind.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/platform/platform_event_dispatcher.h" #include "ui/events/platform/platform_event_observer.h" @@ -499,8 +499,9 @@ class PlatformEventTestWithMessageLoop : public PlatformEventTest { void Run() { message_loop_.task_runner()->PostTask( - FROM_HERE, base::Bind(&PlatformEventTestWithMessageLoop::RunTestImpl, - base::Unretained(this))); + FROM_HERE, + base::BindOnce(&PlatformEventTestWithMessageLoop::RunTestImpl, + base::Unretained(this))); base::RunLoop().RunUntilIdle(); } @@ -646,11 +647,9 @@ class DestroyedNestedOverriddenDispatcherQuitsNestedLoopIteration list.clear(); overriding.SetScopedHandle(std::move(override_handle)); - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoopForUI::ScopedNestableTaskAllower allow_nested(loop); - loop->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind( + base::BindOnce( &DestroyedNestedOverriddenDispatcherQuitsNestedLoopIteration:: NestedTask, base::Unretained(this), base::Unretained(&list), @@ -665,7 +664,7 @@ class DestroyedNestedOverriddenDispatcherQuitsNestedLoopIteration } private: - base::RunLoop run_loop_; + base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; }; RUN_TEST_IN_MESSAGE_LOOP( @@ -709,14 +708,12 @@ class ConsecutiveOverriddenDispatcherInTheSameMessageLoopIteration second_overriding.SetScopedHandle(std::move(second_override_handle)); second_overriding.set_post_dispatch_action(POST_DISPATCH_NONE); - base::RunLoop run_loop; + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); second_overriding.set_callback(run_loop.QuitClosure()); - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoopForUI::ScopedNestableTaskAllower allow_nested(loop); - loop->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(base::IgnoreResult(&TestPlatformEventSource::Dispatch), - base::Unretained(source()), *event)); + base::BindOnce(base::IgnoreResult(&TestPlatformEventSource::Dispatch), + base::Unretained(source()), *event)); run_loop.Run(); ASSERT_EQ(2u, list->size()); EXPECT_EQ(15, (*list)[0]); @@ -748,14 +745,12 @@ class ConsecutiveOverriddenDispatcherInTheSameMessageLoopIteration // Start a nested message-loop, and destroy |override_handle| in the nested // loop. That should terminate the nested loop, restore the previous // dispatchers, and return control to this function. - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoopForUI::ScopedNestableTaskAllower allow_nested(loop); - loop->task_runner()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind( + base::BindOnce( &ConsecutiveOverriddenDispatcherInTheSameMessageLoopIteration:: NestedTask, - base::Unretained(this), base::Passed(&override_handle), + base::Unretained(this), std::move(override_handle), base::Unretained(&list))); run_loop_.Run(); @@ -767,7 +762,7 @@ class ConsecutiveOverriddenDispatcherInTheSameMessageLoopIteration } private: - base::RunLoop run_loop_; + base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed}; }; RUN_TEST_IN_MESSAGE_LOOP( diff --git a/chromium/ui/events/platform/platform_event_types.h b/chromium/ui/events/platform/platform_event_types.h deleted file mode 100644 index dedb38ff063..00000000000 --- a/chromium/ui/events/platform/platform_event_types.h +++ /dev/null @@ -1,14 +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_EVENTS_PLATFORM_PLATFORM_EVENT_TYPES_H_ -#define UI_EVENTS_PLATFORM_PLATFORM_EVENT_TYPES_H_ - -#include "base/event_types.h" - -namespace ui { -typedef base::NativeEvent PlatformEvent; -} // namespace ui - -#endif // UI_EVENTS_PLATFORM_PLATFORM_EVENT_TYPES_H_ diff --git a/chromium/ui/events/platform/x11/BUILD.gn b/chromium/ui/events/platform/x11/BUILD.gn index d4c7462d4db..31a20f03e55 100644 --- a/chromium/ui/events/platform/x11/BUILD.gn +++ b/chromium/ui/events/platform/x11/BUILD.gn @@ -38,6 +38,10 @@ jumbo_component("x11") { "//base", ] + if (is_chromeos) { + deps += [ "//ui/events/ozone:events_ozone" ] + } + if (use_glib) { sources += [ "x11_event_source_glib.cc", diff --git a/chromium/ui/events/platform/x11/x11_event_source.cc b/chromium/ui/events/platform/x11/x11_event_source.cc index ed3581a9c2b..897248a214a 100644 --- a/chromium/ui/events/platform/x11/x11_event_source.cc +++ b/chromium/ui/events/platform/x11/x11_event_source.cc @@ -183,6 +183,7 @@ Time X11EventSource::GetTimestamp() { return GetCurrentServerTime(); } +#if !defined(USE_OZONE) base::Optional<gfx::Point> X11EventSource::GetRootCursorLocationFromCurrentEvent() const { if (!dispatching_event_) @@ -218,6 +219,7 @@ X11EventSource::GetRootCursorLocationFromCurrentEvent() const { return ui::EventSystemLocationFromNative(event); return base::nullopt; } +#endif //////////////////////////////////////////////////////////////////////////////// // X11EventSource, protected diff --git a/chromium/ui/events/platform/x11/x11_event_source.h b/chromium/ui/events/platform/x11/x11_event_source.h index a0cd370181c..1df54b42367 100644 --- a/chromium/ui/events/platform/x11/x11_event_source.h +++ b/chromium/ui/events/platform/x11/x11_event_source.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/optional.h" +#include "build/build_config.h" #include "ui/events/events_export.h" #include "ui/gfx/x/x11_types.h" @@ -69,9 +70,11 @@ class EVENTS_EXPORT X11EventSource { // current event does not have a timestamp. Time GetTimestamp(); +#if !defined(USE_OZONE) // Returns the root pointer location only if there is an event being // dispatched that contains that information. base::Optional<gfx::Point> GetRootCursorLocationFromCurrentEvent() const; +#endif void StopCurrentEventStream(); void OnDispatcherListChanged(); diff --git a/chromium/ui/events/platform/x11/x11_event_source_glib.cc b/chromium/ui/events/platform/x11/x11_event_source_glib.cc index b5af868de64..e82e4c652be 100644 --- a/chromium/ui/events/platform/x11/x11_event_source_glib.cc +++ b/chromium/ui/events/platform/x11/x11_event_source_glib.cc @@ -7,7 +7,6 @@ #include <glib.h> #include "ui/gfx/x/x11.h" -#include "base/memory/ptr_util.h" namespace ui { diff --git a/chromium/ui/events/platform/x11/x11_event_source_libevent.cc b/chromium/ui/events/platform/x11/x11_event_source_libevent.cc index e5831579836..63826141757 100644 --- a/chromium/ui/events/platform/x11/x11_event_source_libevent.cc +++ b/chromium/ui/events/platform/x11/x11_event_source_libevent.cc @@ -6,7 +6,6 @@ #include <memory> -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" @@ -15,6 +14,10 @@ #include "ui/events/x/events_x_utils.h" #include "ui/gfx/x/x11.h" +#if defined(OS_CHROMEOS) +#include "ui/events/ozone/chromeos/cursor_controller.h" +#endif + namespace ui { namespace { @@ -177,6 +180,12 @@ void X11EventSourceLibevent::RemoveXEventDispatcher( void X11EventSourceLibevent::ProcessXEvent(XEvent* xevent) { std::unique_ptr<ui::Event> translated_event = TranslateXEventToEvent(*xevent); if (translated_event) { +#if defined(OS_CHROMEOS) + if (translated_event->IsLocatedEvent()) { + ui::CursorController::GetInstance()->SetCursorLocation( + translated_event->AsLocatedEvent()->location_f()); + } +#endif DispatchPlatformEvent(translated_event.get(), xevent); } else { // Only if we can't translate XEvent into ui::Event, try to dispatch XEvent diff --git a/chromium/ui/events/platform/x11/x11_event_source_libevent.h b/chromium/ui/events/platform/x11/x11_event_source_libevent.h index 6959e0235e9..b193db0d137 100644 --- a/chromium/ui/events/platform/x11/x11_event_source_libevent.h +++ b/chromium/ui/events/platform/x11/x11_event_source_libevent.h @@ -51,7 +51,7 @@ class EVENTS_EXPORT XEventDispatcher { class EVENTS_EXPORT X11EventSourceLibevent : public X11EventSourceDelegate, public PlatformEventSource, - public base::MessagePumpLibevent::Watcher { + public base::MessagePumpLibevent::FdWatcher { public: explicit X11EventSourceLibevent(XDisplay* display); ~X11EventSourceLibevent() override; @@ -94,7 +94,7 @@ class EVENTS_EXPORT X11EventSourceLibevent void StopCurrentEventStream() override; void OnDispatcherListChanged() override; - // base::MessagePumpLibevent::Watcher: + // base::MessagePumpLibevent::FdWatcher: void OnFileCanReadWithoutBlocking(int fd) override; void OnFileCanWriteWithoutBlocking(int fd) override; @@ -103,7 +103,7 @@ class EVENTS_EXPORT X11EventSourceLibevent // Keep track of all XEventDispatcher to send XEvents directly to. base::ObserverList<XEventDispatcher> dispatchers_xevent_; - base::MessagePumpLibevent::FileDescriptorWatcher watcher_controller_; + base::MessagePumpLibevent::FdWatchController watcher_controller_; bool initialized_ = false; DISALLOW_COPY_AND_ASSIGN(X11EventSourceLibevent); diff --git a/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc b/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc index 077f181f82c..4686c60bda0 100644 --- a/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc +++ b/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc @@ -108,13 +108,9 @@ struct TouchClassInfo { struct DeviceInfo { DeviceInfo(const XIDeviceInfo& device, DeviceType type, - const base::FilePath& path, - uint16_t vendor, - uint16_t product) + const base::FilePath& path) : id(device.deviceid), name(device.name), - vendor_id(vendor), - product_id(product), use(device.use), type(type), path(path) { @@ -143,10 +139,6 @@ struct DeviceInfo { // Internal device name. std::string name; - // USB-style device identifiers. - uint16_t vendor_id; - uint16_t product_id; - // Device type (ie: XIMasterPointer) int use; @@ -248,7 +240,7 @@ void HandleKeyboardDevicesInWorker( devices.push_back(keyboard); } - reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); + reply_runner->PostTask(FROM_HERE, base::BindOnce(callback, devices)); } // Helper used to parse mouse information. When it is done it uses @@ -267,7 +259,7 @@ void HandleMouseDevicesInWorker(const std::vector<DeviceInfo>& device_infos, devices.push_back(InputDevice(device_info.id, type, device_info.name)); } - reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); + reply_runner->PostTask(FROM_HERE, base::BindOnce(callback, devices)); } // Helper used to parse touchpad information. When it is done it uses @@ -286,7 +278,7 @@ void HandleTouchpadDevicesInWorker(const std::vector<DeviceInfo>& device_infos, devices.push_back(InputDevice(device_info.id, type, device_info.name)); } - reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); + reply_runner->PostTask(FROM_HERE, base::BindOnce(callback, devices)); } // Helper used to parse touchscreen information. When it is done it uses @@ -345,7 +337,7 @@ void HandleTouchscreenDevicesInWorker( } } - reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); + reply_runner->PostTask(FROM_HERE, base::BindOnce(callback, devices)); } // Called on a worker thread to parse the device information. @@ -433,31 +425,8 @@ void X11HotplugEventHandler::OnHotplugEvent() { (device.deviceid >= 0 && device.deviceid < kMaxDeviceNum) ? device_types[device.deviceid] : DEVICE_TYPE_OTHER; - - // Obtain the USB-style vendor and product identifiers. - // (On Linux, XI2 makes this available for all evdev devices. - uint32_t* product_info; - Atom type; - int format_return; - unsigned long num_items_return; - unsigned long bytes_after_return; - uint16_t vendor = 0; - uint16_t product = 0; - if (XIGetProperty(gfx::GetXDisplay(), device.deviceid, - gfx::GetAtom(XI_PROP_PRODUCT_ID), 0, 2, 0, XA_INTEGER, - &type, &format_return, &num_items_return, - &bytes_after_return, - reinterpret_cast<unsigned char**>(&product_info)) == 0 && - product_info) { - if (num_items_return == 2) { - vendor = product_info[0]; - product = product_info[1]; - } - XFree(product_info); - } - - device_infos.push_back(DeviceInfo( - device, device_type, GetDevicePath(display, device), vendor, product)); + device_infos.push_back( + DeviceInfo(device, device_type, GetDevicePath(display, device))); } // X11 is not thread safe, so first get all the required state. diff --git a/chromium/ui/events/platform_event.h b/chromium/ui/events/platform_event.h new file mode 100644 index 00000000000..817f4310fb3 --- /dev/null +++ b/chromium/ui/events/platform_event.h @@ -0,0 +1,43 @@ +// 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_EVENTS_PLATFORM_EVENT_H_ +#define UI_EVENTS_PLATFORM_EVENT_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#elif defined(USE_X11) +typedef union _XEvent XEvent; +#elif defined(OS_MACOSX) +#if defined(__OBJC__) +@class NSEvent; +#else // __OBJC__ +class NSEvent; +#endif // __OBJC__ +#endif + +namespace ui { +class Event; +} + +namespace ui { + +// Cross platform typedefs for native event types. +#if defined(OS_WIN) +using PlatformEvent = MSG; +#elif defined(USE_X11) +using PlatformEvent = XEvent*; +#elif defined(OS_MACOSX) +using PlatformEvent = NSEvent*; +#elif defined(USE_OZONE) +using PlatformEvent = ui::Event*; +#else +using PlatformEvent = void*; +#endif + +} // namespace ui + +#endif // UI_EVENTS_PLATFORM_EVENT_H_ diff --git a/chromium/ui/events/system_input_injector.cc b/chromium/ui/events/system_input_injector.cc index 3d9207dd432..bbeb84dccbf 100644 --- a/chromium/ui/events/system_input_injector.cc +++ b/chromium/ui/events/system_input_injector.cc @@ -4,7 +4,6 @@ #include "ui/events/system_input_injector.h" -#include "base/memory/ptr_util.h" namespace ui { diff --git a/chromium/ui/events/win/events_win.cc b/chromium/ui/events/win/events_win.cc index 543741d6081..d7ee6f0c0e7 100644 --- a/chromium/ui/events/win/events_win.cc +++ b/chromium/ui/events/win/events_win.cc @@ -28,7 +28,7 @@ namespace { #define SIGNATURE_MASK 0xFFFFFF00 // Get the native mouse key state from the native event message type. -int GetNativeMouseKey(const base::NativeEvent& native_event) { +int GetNativeMouseKey(const PlatformEvent& native_event) { switch (native_event.message) { case WM_LBUTTONDBLCLK: case WM_LBUTTONDOWN: @@ -62,36 +62,36 @@ int GetNativeMouseKey(const base::NativeEvent& native_event) { return 0; } -bool IsButtonDown(const base::NativeEvent& native_event) { +bool IsButtonDown(const PlatformEvent& native_event) { return ((MK_LBUTTON | MK_MBUTTON | MK_RBUTTON | MK_XBUTTON1 | MK_XBUTTON2) & native_event.wParam) != 0; } -bool IsClientMouseEvent(const base::NativeEvent& native_event) { +bool IsClientMouseEvent(const PlatformEvent& native_event) { return native_event.message == WM_MOUSELEAVE || native_event.message == WM_MOUSEHOVER || (native_event.message >= WM_MOUSEFIRST && native_event.message <= WM_MOUSELAST); } -bool IsNonClientMouseEvent(const base::NativeEvent& native_event) { +bool IsNonClientMouseEvent(const PlatformEvent& native_event) { return native_event.message == WM_NCMOUSELEAVE || native_event.message == WM_NCMOUSEHOVER || (native_event.message >= WM_NCMOUSEMOVE && native_event.message <= WM_NCXBUTTONDBLCLK); } -bool IsMouseEvent(const base::NativeEvent& native_event) { +bool IsMouseEvent(const PlatformEvent& native_event) { return IsClientMouseEvent(native_event) || IsNonClientMouseEvent(native_event); } -bool IsMouseWheelEvent(const base::NativeEvent& native_event) { +bool IsMouseWheelEvent(const PlatformEvent& native_event) { return native_event.message == WM_MOUSEWHEEL || native_event.message == WM_MOUSEHWHEEL; } -bool IsKeyEvent(const base::NativeEvent& native_event) { +bool IsKeyEvent(const PlatformEvent& native_event) { return native_event.message == WM_KEYDOWN || native_event.message == WM_SYSKEYDOWN || native_event.message == WM_CHAR || @@ -100,14 +100,14 @@ bool IsKeyEvent(const base::NativeEvent& native_event) { native_event.message == WM_SYSKEYUP; } -bool IsScrollEvent(const base::NativeEvent& native_event) { +bool IsScrollEvent(const PlatformEvent& native_event) { return native_event.message == WM_VSCROLL || native_event.message == WM_HSCROLL; } // Returns a mask corresponding to the set of pressed modifier keys. // Checks the current global state and the state sent by client mouse messages. -int KeyStateFlagsFromNative(const base::NativeEvent& native_event) { +int KeyStateFlagsFromNative(const PlatformEvent& native_event) { int flags = GetModifiersFromKeyState(); // Check key messages for the extended key flag. @@ -126,7 +126,7 @@ int KeyStateFlagsFromNative(const base::NativeEvent& native_event) { // Returns a mask corresponding to the set of pressed mouse buttons. // This includes the button of the given message, even if it is being released. -int MouseStateFlagsFromNative(const base::NativeEvent& native_event) { +int MouseStateFlagsFromNative(const PlatformEvent& native_event) { int win_flags = GetNativeMouseKey(native_event); // Client mouse messages provide key states in their WPARAMs. @@ -143,7 +143,7 @@ int MouseStateFlagsFromNative(const base::NativeEvent& native_event) { } // namespace -EventType EventTypeFromNative(const base::NativeEvent& native_event) { +EventType EventTypeFromNative(const PlatformEvent& native_event) { switch (native_event.message) { case WM_KEYDOWN: case WM_SYSKEYDOWN: @@ -208,7 +208,7 @@ EventType EventTypeFromNative(const base::NativeEvent& native_event) { return ET_UNKNOWN; } -int EventFlagsFromNative(const base::NativeEvent& native_event) { +int EventFlagsFromNative(const PlatformEvent& native_event) { int flags = KeyStateFlagsFromNative(native_event); if (IsMouseEvent(native_event)) flags |= MouseStateFlagsFromNative(native_event); @@ -216,7 +216,7 @@ int EventFlagsFromNative(const base::NativeEvent& native_event) { return flags; } -base::TimeTicks EventTimeFromNative(const base::NativeEvent& native_event) { +base::TimeTicks EventTimeFromNative(const PlatformEvent& native_event) { // On Windows, the native input event timestamp (|native_event.time|) is // coming from |GetTickCount()| clock [1], while in platform independent code // path we get timestamps by calling |TimeTicks::Now()|, which, if using high- @@ -230,7 +230,7 @@ base::TimeTicks EventTimeFromNative(const base::NativeEvent& native_event) { return EventTimeForNow(); } -gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { +gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) { POINT native_point; if ((native_event.message == WM_MOUSELEAVE || native_event.message == WM_NCMOUSELEAVE) || @@ -256,8 +256,7 @@ gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { return gfx::PointF(gfx::Point(native_point)); } -gfx::Point EventSystemLocationFromNative( - const base::NativeEvent& native_event) { +gfx::Point EventSystemLocationFromNative(const PlatformEvent& native_event) { POINT global_point = {GET_X_LPARAM(native_event.lParam), GET_Y_LPARAM(native_event.lParam)}; // Wheel events have position in screen coordinates. @@ -266,21 +265,20 @@ gfx::Point EventSystemLocationFromNative( return gfx::Point(global_point); } -KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { +KeyboardCode KeyboardCodeFromNative(const PlatformEvent& native_event) { return KeyboardCodeForWindowsKeyCode(static_cast<WORD>(native_event.wParam)); } -DomCode CodeFromNative(const base::NativeEvent& native_event) { +DomCode CodeFromNative(const PlatformEvent& native_event) { const uint16_t scan_code = GetScanCodeFromLParam(native_event.lParam); return CodeForWindowsScanCode(scan_code); } -bool IsCharFromNative(const base::NativeEvent& native_event) { +bool IsCharFromNative(const PlatformEvent& native_event) { return native_event.message == WM_CHAR || native_event.message == WM_SYSCHAR; } -int GetChangedMouseButtonFlagsFromNative( - const base::NativeEvent& native_event) { +int GetChangedMouseButtonFlagsFromNative(const PlatformEvent& native_event) { switch (GetNativeMouseKey(native_event)) { case MK_LBUTTON: return EF_LEFT_MOUSE_BUTTON; @@ -296,7 +294,7 @@ int GetChangedMouseButtonFlagsFromNative( } PointerDetails GetMousePointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { // We should filter out all the mouse events Synthesized from touch events. // TODO(lanwei): Will set the pointer ID, see https://crbug.com/616771. if ((GetMessageExtraInfo() & SIGNATURE_MASK) != MOUSEEVENTF_FROMTOUCHPEN) @@ -305,7 +303,7 @@ PointerDetails GetMousePointerDetailsFromNative( return PointerDetails(EventPointerType::POINTER_TYPE_PEN); } -gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) { +gfx::Vector2d GetMouseWheelOffset(const PlatformEvent& native_event) { DCHECK(native_event.message == WM_MOUSEWHEEL || native_event.message == WM_MOUSEHWHEEL); if (native_event.message == WM_MOUSEWHEEL) @@ -313,24 +311,23 @@ gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) { return gfx::Vector2d(GET_WHEEL_DELTA_WPARAM(native_event.wParam), 0); } -base::NativeEvent CopyNativeEvent(const base::NativeEvent& event) { +PlatformEvent CopyNativeEvent(const PlatformEvent& event) { return event; } -void ReleaseCopiedNativeEvent(const base::NativeEvent& event) { -} +void ReleaseCopiedNativeEvent(const PlatformEvent& event) {} -void ClearTouchIdIfReleased(const base::NativeEvent& xev) { +void ClearTouchIdIfReleased(const PlatformEvent& xev) { NOTIMPLEMENTED(); } -int GetTouchId(const base::NativeEvent& xev) { +int GetTouchId(const PlatformEvent& xev) { NOTIMPLEMENTED(); return 0; } PointerDetails GetTouchPointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { NOTIMPLEMENTED(); return PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 0, @@ -339,7 +336,7 @@ PointerDetails GetTouchPointerDetailsFromNative( /* force */ 0.f); } -bool GetScrollOffsets(const base::NativeEvent& native_event, +bool GetScrollOffsets(const PlatformEvent& native_event, float* x_offset, float* y_offset, float* x_offset_ordinal, @@ -353,7 +350,7 @@ bool GetScrollOffsets(const base::NativeEvent& native_event, return false; } -bool GetFlingData(const base::NativeEvent& native_event, +bool GetFlingData(const PlatformEvent& native_event, float* vx, float* vy, float* vx_ordinal, diff --git a/chromium/ui/events/win/keyboard_hook_win.cc b/chromium/ui/events/win/keyboard_hook_win.cc index 8f92cd5ec72..bc7048ba656 100644 --- a/chromium/ui/events/win/keyboard_hook_win.cc +++ b/chromium/ui/events/win/keyboard_hook_win.cc @@ -102,7 +102,7 @@ LRESULT CALLBACK KeyboardHookWin::ProcessKeyEvent(int code, KBDLLHOOKSTRUCT* ll_hooks = reinterpret_cast<KBDLLHOOKSTRUCT*>(l_param); if (!IsOSReservedKey(ll_hooks->vkCode) && - instance_->ShouldCaptureKeyEvent(ll_hooks->vkCode)) { + instance_->ShouldCaptureKeyEvent(ll_hooks->scanCode)) { MSG msg = {nullptr, w_param, ll_hooks->vkCode, (ll_hooks->scanCode << 16) | (ll_hooks->flags & 0xFFFF), ll_hooks->time}; diff --git a/chromium/ui/events/x/events_x.cc b/chromium/ui/events/x/events_x.cc index 4a22e65a872..b8df7b02091 100644 --- a/chromium/ui/events/x/events_x.cc +++ b/chromium/ui/events/x/events_x.cc @@ -72,60 +72,58 @@ unsigned int UpdateX11EventButton(int ui_flag, unsigned int old_x_button) { namespace ui { -EventType EventTypeFromNative(const base::NativeEvent& native_event) { +EventType EventTypeFromNative(const PlatformEvent& native_event) { return EventTypeFromXEvent(*native_event); } -int EventFlagsFromNative(const base::NativeEvent& native_event) { +int EventFlagsFromNative(const PlatformEvent& native_event) { return EventFlagsFromXEvent(*native_event); } -base::TimeTicks EventTimeFromNative(const base::NativeEvent& native_event) { +base::TimeTicks EventTimeFromNative(const PlatformEvent& native_event) { base::TimeTicks timestamp = EventTimeFromXEvent(*native_event); ValidateEventTimeClock(×tamp); return timestamp; } -gfx::PointF EventLocationFromNative(const base::NativeEvent& native_event) { +gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) { return gfx::PointF(EventLocationFromXEvent(*native_event)); } -gfx::Point EventSystemLocationFromNative( - const base::NativeEvent& native_event) { +gfx::Point EventSystemLocationFromNative(const PlatformEvent& native_event) { return EventSystemLocationFromXEvent(*native_event); } -int EventButtonFromNative(const base::NativeEvent& native_event) { +int EventButtonFromNative(const PlatformEvent& native_event) { return EventButtonFromXEvent(*native_event); } -KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { +KeyboardCode KeyboardCodeFromNative(const PlatformEvent& native_event) { return KeyboardCodeFromXKeyEvent(native_event); } -DomCode CodeFromNative(const base::NativeEvent& native_event) { +DomCode CodeFromNative(const PlatformEvent& native_event) { return CodeFromXEvent(native_event); } -bool IsCharFromNative(const base::NativeEvent& native_event) { +bool IsCharFromNative(const PlatformEvent& native_event) { return false; } -int GetChangedMouseButtonFlagsFromNative( - const base::NativeEvent& native_event) { +int GetChangedMouseButtonFlagsFromNative(const PlatformEvent& native_event) { return GetChangedMouseButtonFlagsFromXEvent(*native_event); } PointerDetails GetMousePointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { return PointerDetails(EventPointerType::POINTER_TYPE_MOUSE); } -gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) { +gfx::Vector2d GetMouseWheelOffset(const PlatformEvent& native_event) { return GetMouseWheelOffsetFromXEvent(*native_event); } -base::NativeEvent CopyNativeEvent(const base::NativeEvent& native_event) { +PlatformEvent CopyNativeEvent(const PlatformEvent& native_event) { if (!native_event || native_event->type == GenericEvent) return NULL; XEvent* copy = new XEvent; @@ -133,20 +131,20 @@ base::NativeEvent CopyNativeEvent(const base::NativeEvent& native_event) { return copy; } -void ReleaseCopiedNativeEvent(const base::NativeEvent& native_event) { +void ReleaseCopiedNativeEvent(const PlatformEvent& native_event) { delete native_event; } -void ClearTouchIdIfReleased(const base::NativeEvent& native_event) { +void ClearTouchIdIfReleased(const PlatformEvent& native_event) { ClearTouchIdIfReleasedFromXEvent(*native_event); } -int GetTouchId(const base::NativeEvent& native_event) { +int GetTouchId(const PlatformEvent& native_event) { return GetTouchIdFromXEvent(*native_event); } PointerDetails GetTouchPointerDetailsFromNative( - const base::NativeEvent& native_event) { + const PlatformEvent& native_event) { return PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, GetTouchIdFromXEvent(*native_event), GetTouchRadiusXFromXEvent(*native_event), @@ -155,7 +153,7 @@ PointerDetails GetTouchPointerDetailsFromNative( GetTouchAngleFromXEvent(*native_event)); } -bool GetScrollOffsets(const base::NativeEvent& native_event, +bool GetScrollOffsets(const PlatformEvent& native_event, float* x_offset, float* y_offset, float* x_offset_ordinal, @@ -167,7 +165,7 @@ bool GetScrollOffsets(const base::NativeEvent& native_event, finger_count); } -bool GetFlingData(const base::NativeEvent& native_event, +bool GetFlingData(const PlatformEvent& native_event, float* vx, float* vy, float* vx_ordinal, diff --git a/chromium/ui/events/x/events_x_unittest.cc b/chromium/ui/events/x/events_x_unittest.cc index 0022b7f180f..4ca7049243c 100644 --- a/chromium/ui/events/x/events_x_unittest.cc +++ b/chromium/ui/events/x/events_x_unittest.cc @@ -11,7 +11,6 @@ #include <utility> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/test/simple_test_tick_clock.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -382,7 +381,7 @@ TEST_F(EventsXTest, TouchEventNotRemovingFromNativeMapping) { // Copied events should not remove native touch id mappings, as this causes a // crash (crbug.com/467102). Copied events do not contain a proper -// base::NativeEvent and should not attempt to access it. +// PlatformEvent and should not attempt to access it. TEST_F(EventsXTest, CopiedTouchEventNotRemovingFromNativeMapping) { std::vector<int> devices; devices.push_back(0); diff --git a/chromium/ui/file_manager/file_manager/background/js/compiled_resources2.gyp b/chromium/ui/file_manager/file_manager/background/js/compiled_resources2.gyp index 171eff0feb0..28942957372 100644 --- a/chromium/ui/file_manager/file_manager/background/js/compiled_resources2.gyp +++ b/chromium/ui/file_manager/file_manager/background/js/compiled_resources2.gyp @@ -209,10 +209,20 @@ # 'target_name': 'mock_progress_center', # 'includes': ['../../../compile_js2.gypi'], # }, -# { -# 'target_name': 'mock_volume_manager', -# 'includes': ['../../../compile_js2.gypi'], -# }, + { + 'target_name': 'mock_volume_manager', + 'dependencies': [ + '../../../externs/compiled_resources2.gyp:entry_location', + '../../../externs/compiled_resources2.gyp:volume_info_list', + '../../../externs/compiled_resources2.gyp:volume_info', + '../../../externs/compiled_resources2.gyp:volume_manager', + '../../common/js/compiled_resources2.gyp:mock_entry', + 'volume_info_impl', + 'volume_info_list_impl', + 'volume_manager_factory', + ], + 'includes': ['../../../compile_js2.gypi'], + }, { 'target_name': 'progress_center', 'dependencies': [ diff --git a/chromium/ui/file_manager/file_manager/common/js/compiled_resources2.gyp b/chromium/ui/file_manager/file_manager/common/js/compiled_resources2.gyp index a539058b7e8..370faaf31f3 100644 --- a/chromium/ui/file_manager/file_manager/common/js/compiled_resources2.gyp +++ b/chromium/ui/file_manager/file_manager/common/js/compiled_resources2.gyp @@ -59,6 +59,14 @@ 'includes': ['../../../compile_js2.gypi'], }, { + 'target_name': 'mock_entry', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + 'util', + ], + 'includes': ['../../../compile_js2.gypi'], + }, + { 'target_name': 'progress_center_common', 'includes': ['../../../compile_js2.gypi'], }, diff --git a/chromium/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp b/chromium/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp index 039fe8c86f3..645b490d7c6 100644 --- a/chromium/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp +++ b/chromium/ui/file_manager/file_manager/foreground/js/ui/compiled_resources2.gyp @@ -268,6 +268,7 @@ 'dependencies': [ '../../../../externs/compiled_resources2.gyp:platform', '../../../../externs/compiled_resources2.gyp:volume_manager', + '../../../common/js/compiled_resources2.gyp:metrics', '../../../common/js/compiled_resources2.gyp:util', '../../../common/js/compiled_resources2.gyp:volume_manager_common', '../compiled_resources2.gyp:volume_manager_wrapper', @@ -309,6 +310,7 @@ 'dependencies': [ '../../../../externs/compiled_resources2.gyp:search_item', '../../../common/js/compiled_resources2.gyp:file_type', + '../../../common/js/compiled_resources2.gyp:metrics', '../../../common/js/compiled_resources2.gyp:util', '../../elements/compiled_resources2.gyp:files_toggle_ripple', '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', diff --git a/chromium/ui/file_manager/file_manager/test/BUILD.gn b/chromium/ui/file_manager/file_manager/test/BUILD.gn new file mode 100644 index 00000000000..d37ac91f365 --- /dev/null +++ b/chromium/ui/file_manager/file_manager/test/BUILD.gn @@ -0,0 +1,13 @@ +# 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. + +action("create_test_main") { + script = "//ui/file_manager/file_manager/test/scripts/create_test_main.py" + output = "$target_gen_dir/../test.html" + deps = ["//ui/file_manager:resources"] + args = [ "--output=" + rebase_path(output, root_build_dir) ] + outputs = [ + output, + ] +} diff --git a/chromium/ui/gfx/BUILD.gn b/chromium/ui/gfx/BUILD.gn index 9c38caf3ba8..f99576cf60f 100644 --- a/chromium/ui/gfx/BUILD.gn +++ b/chromium/ui/gfx/BUILD.gn @@ -4,7 +4,7 @@ import("//build/config/jumbo.gni") import("//build/config/ui.gni") -import("//device/vr/features/features.gni") +import("//device/vr/buildflags/buildflags.gni") import("//testing/test.gni") import("//testing/libfuzzer/fuzzer_test.gni") @@ -59,6 +59,8 @@ jumbo_component("gfx") { "color_utils.h", "decorated_text.cc", "decorated_text.h", + "decorated_text_mac.h", + "decorated_text_mac.mm", "favicon_size.cc", "favicon_size.h", "font.cc", @@ -123,6 +125,7 @@ jumbo_component("gfx") { "mac/nswindow_frame_controls.h", "mac/nswindow_frame_controls.mm", "mac/scoped_cocoa_disable_screen_updates.h", + "mac/scoped_cocoa_disable_screen_updates.mm", "nine_image_painter.cc", "nine_image_painter.h", "path.cc", @@ -250,7 +253,7 @@ jumbo_component("gfx") { "//base:base_static", "//base:i18n", "//base/third_party/dynamic_annotations", - "//device/vr/features", + "//device/vr/buildflags", "//skia", "//third_party/zlib", ] @@ -601,8 +604,6 @@ static_library("test_support") { "image/image_unittest_util.h", "image/image_unittest_util_ios.mm", "image/image_unittest_util_mac.mm", - "test/fontconfig_util_linux.cc", - "test/fontconfig_util_linux.h", "test/gfx_util.cc", "test/gfx_util.h", "test/icc_profiles.cc", @@ -627,10 +628,6 @@ static_library("test_support") { deps += [ "//third_party:freetype_harfbuzz" ] } - - if (is_linux) { - deps += [ "//third_party/fontconfig" ] - } } test("gfx_unittests") { @@ -793,7 +790,7 @@ test("gfx_unittests") { if (!is_ios) { deps += [ "//cc/paint", - "//mojo/edk/system", + "//mojo/edk", "//mojo/public/cpp/bindings", "//ui/gfx/geometry/mojo:unit_test", "//ui/gfx/image/mojo:unit_test", diff --git a/chromium/ui/gfx/DEPS b/chromium/ui/gfx/DEPS index 808d12b3ae0..1fa634730b4 100644 --- a/chromium/ui/gfx/DEPS +++ b/chromium/ui/gfx/DEPS @@ -2,7 +2,7 @@ include_rules = [ "+base", "+cc/base", "+cc/paint", - "+device/vr/features/features.h", + "+device/vr/buildflags/buildflags.h", "+skia/ext", "+third_party/harfbuzz-ng", "+third_party/skia", diff --git a/chromium/ui/gfx/OWNERS b/chromium/ui/gfx/OWNERS index ad40b66f057..e73caf84d43 100644 --- a/chromium/ui/gfx/OWNERS +++ b/chromium/ui/gfx/OWNERS @@ -20,6 +20,9 @@ per-file transform*=vollick@chromium.org per-file interpolated_transform*=danakj@chromium.org per-file interpolated_transform*=vollick@chromium.org +# Skia geometry helpers. +per-file skia_util*=danakj@chromium.org + # GPU memory buffer and GpuFence interfaces. per-file gpu_fence*=reveman@chromium.org per-file gpu_memory_buffer*=reveman@chromium.org diff --git a/chromium/ui/gfx/animation/animation.cc b/chromium/ui/gfx/animation/animation.cc index 469b5260ce0..90342df9b79 100644 --- a/chromium/ui/gfx/animation/animation.cc +++ b/chromium/ui/gfx/animation/animation.cc @@ -4,7 +4,6 @@ #include "ui/gfx/animation/animation.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "ui/gfx/animation/animation_container.h" #include "ui/gfx/animation/animation_delegate.h" diff --git a/chromium/ui/gfx/animation/animation_test_api.cc b/chromium/ui/gfx/animation/animation_test_api.cc index b1c345dfedb..4f3c99d93df 100644 --- a/chromium/ui/gfx/animation/animation_test_api.cc +++ b/chromium/ui/gfx/animation/animation_test_api.cc @@ -4,7 +4,6 @@ #include "ui/gfx/animation/animation_test_api.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "ui/gfx/animation/animation.h" diff --git a/chromium/ui/gfx/break_list.h b/chromium/ui/gfx/break_list.h index 97d23c52955..cb3de2b76de 100644 --- a/chromium/ui/gfx/break_list.h +++ b/chromium/ui/gfx/break_list.h @@ -89,7 +89,7 @@ void BreakList<T>::ApplyValue(T value, const Range& range) { return; DCHECK(!breaks_.empty()); DCHECK(!range.is_reversed()); - DCHECK(Range(0, max_).Contains(range)); + DCHECK(Range(0, static_cast<uint32_t>(max_)).Contains(range)); // Erase any breaks in |range|, then add start and end breaks as needed. typename std::vector<Break>::iterator start = GetBreak(range.start()); diff --git a/chromium/ui/gfx/client_native_pixmap_factory.cc b/chromium/ui/gfx/client_native_pixmap_factory.cc index f88b4fdeef3..7a4354f2507 100644 --- a/chromium/ui/gfx/client_native_pixmap_factory.cc +++ b/chromium/ui/gfx/client_native_pixmap_factory.cc @@ -6,30 +6,6 @@ namespace gfx { -namespace { - -ClientNativePixmapFactory* g_instance = nullptr; - -} // namespace - -// static -ClientNativePixmapFactory* ClientNativePixmapFactory::GetInstance() { - return g_instance; -} - -// static -void ClientNativePixmapFactory::ResetInstance() { - g_instance = nullptr; -} - -// static -void ClientNativePixmapFactory::SetInstance( - ClientNativePixmapFactory* instance) { - DCHECK(!g_instance); - DCHECK(instance); - g_instance = instance; -} - ClientNativePixmapFactory::ClientNativePixmapFactory() {} ClientNativePixmapFactory::~ClientNativePixmapFactory() {} diff --git a/chromium/ui/gfx/client_native_pixmap_factory.h b/chromium/ui/gfx/client_native_pixmap_factory.h index 8268566d5b0..fff5b4cd179 100644 --- a/chromium/ui/gfx/client_native_pixmap_factory.h +++ b/chromium/ui/gfx/client_native_pixmap_factory.h @@ -23,10 +23,6 @@ class Size; // provide a client pixmap for non-GPU processes. class GFX_EXPORT ClientNativePixmapFactory { public: - static ClientNativePixmapFactory* GetInstance(); - static void SetInstance(ClientNativePixmapFactory* instance); - static void ResetInstance(); - virtual ~ClientNativePixmapFactory(); // Returns true if format/usage configuration is supported. diff --git a/chromium/ui/gfx/color_palette.h b/chromium/ui/gfx/color_palette.h index 3c77e6a68c9..317174734d7 100644 --- a/chromium/ui/gfx/color_palette.h +++ b/chromium/ui/gfx/color_palette.h @@ -13,8 +13,6 @@ namespace gfx { // as a visual flag for misbehaving code. constexpr SkColor kPlaceholderColor = SK_ColorRED; -constexpr SkColor kChromeIconGrey = SkColorSetRGB(0x5A, 0x5A, 0x5A); - // The number refers to the shade of darkness. Each color in the MD // palette ranges from 100-900. constexpr SkColor kGoogleBlue300 = SkColorSetRGB(0x8A, 0xB4, 0xF8); @@ -22,6 +20,7 @@ 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 kGoogleBlueDark600 = SkColorSetRGB(0x25, 0x81, 0xDF); constexpr SkColor kGoogleRed300 = SkColorSetRGB(0xF2, 0x8B, 0xB2); constexpr SkColor kGoogleRed600 = SkColorSetRGB(0xD9, 0x30, 0x25); constexpr SkColor kGoogleRed700 = SkColorSetRGB(0xC5, 0x22, 0x1F); @@ -29,7 +28,9 @@ constexpr SkColor kGoogleRed800 = SkColorSetRGB(0xB3, 0x14, 0x12); 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 kGoogleGreenDark600 = SkColorSetRGB(0x28, 0x99, 0x4F); constexpr SkColor kGoogleYellow300 = SkColorSetRGB(0xFD, 0xD6, 0x63); constexpr SkColor kGoogleYellow700 = SkColorSetRGB(0xF2, 0x99, 0x00); constexpr SkColor kGoogleYellow900 = SkColorSetRGB(0xE3, 0x74, 0x00); @@ -39,6 +40,12 @@ constexpr SkColor kGoogleGrey700 = SkColorSetRGB(0x5F, 0x63, 0x68); constexpr SkColor kGoogleGrey800 = SkColorSetRGB(0x3C, 0x40, 0x43); constexpr SkColor kGoogleGrey900 = SkColorSetRGB(0x20, 0x21, 0x24); +// kChromeIconGrey is subject to change in the future, kGoogleGrey700 is set in +// stone. If you're semantically looking for "the icon color Chrome uses" then +// use kChromeIconGrey, if you're looking for GG700 grey specifically, use the +// Google-grey constant directly. +constexpr SkColor kChromeIconGrey = kGoogleGrey700; + // An alpha value for designating a control's disabled state. In specs this is // sometimes listed as 0.38a. constexpr SkAlpha kDisabledControlAlpha = 0x61; diff --git a/chromium/ui/gfx/color_space.cc b/chromium/ui/gfx/color_space.cc index 77458c930c6..0161da04c6a 100644 --- a/chromium/ui/gfx/color_space.cc +++ b/chromium/ui/gfx/color_space.cc @@ -38,6 +38,10 @@ base::LazyInstance<SkColorSpaceCache>::Leaky g_sk_color_space_cache = base::LazyInstance<base::Lock>::Leaky g_sk_color_space_cache_lock = LAZY_INSTANCE_INITIALIZER; +static bool IsAlmostZero(float value) { + return std::abs(value) < std::numeric_limits<float>::epsilon(); +} + } // namespace ColorSpace::ColorSpace() {} @@ -306,7 +310,8 @@ size_t ColorSpace::GetHash() const { std::string ColorSpace::ToString() const { std::stringstream ss; ss << std::fixed << std::setprecision(4); - ss << "{primaries:"; + if (primaries_ != PrimaryID::CUSTOM) + ss << "{primaries:"; switch (primaries_) { PRINT_ENUM_CASE(PrimaryID, INVALID) PRINT_ENUM_CASE(PrimaryID, BT709) @@ -324,20 +329,29 @@ std::string ColorSpace::ToString() const { PRINT_ENUM_CASE(PrimaryID, APPLE_GENERIC_RGB) PRINT_ENUM_CASE(PrimaryID, WIDE_GAMUT_COLOR_SPIN) case PrimaryID::CUSTOM: - ss << "["; - for (size_t i = 0; i < 3; ++i) { - ss << "["; - for (size_t j = 0; j < 3; ++j) - ss << custom_primary_matrix_[3 * i + j] << ","; - ss << "],"; - } - ss << "]"; + // |custom_primary_matrix_| is in column-major order. + const float sum_X = custom_primary_matrix_[0] + + custom_primary_matrix_[3] + custom_primary_matrix_[6]; + const float sum_Y = custom_primary_matrix_[1] + + custom_primary_matrix_[4] + custom_primary_matrix_[7]; + const float sum_Z = custom_primary_matrix_[2] + + custom_primary_matrix_[5] + custom_primary_matrix_[8]; + if (IsAlmostZero(sum_X) || IsAlmostZero(sum_Y) || IsAlmostZero(sum_Z)) + break; + + ss << "{primaries_d50_referred: [[" << (custom_primary_matrix_[0] / sum_X) + << ", " << (custom_primary_matrix_[3] / sum_X) << "], " + << " [" << (custom_primary_matrix_[1] / sum_Y) << ", " + << (custom_primary_matrix_[4] / sum_Y) << "], " + << " [" << (custom_primary_matrix_[2] / sum_Z) << ", " + << (custom_primary_matrix_[5] / sum_Z) << "]]"; break; } ss << ", transfer:"; switch (transfer_) { PRINT_ENUM_CASE(TransferID, INVALID) PRINT_ENUM_CASE(TransferID, BT709) + PRINT_ENUM_CASE(TransferID, BT709_APPLE) PRINT_ENUM_CASE(TransferID, GAMMA18) PRINT_ENUM_CASE(TransferID, GAMMA22) PRINT_ENUM_CASE(TransferID, GAMMA24) @@ -759,6 +773,9 @@ bool ColorSpace::GetTransferFunction(SkColorSpaceTransferFn* fn) const { fn->fD = 0.040449937172f; fn->fG = 2.400000000000f; return true; + case ColorSpace::TransferID::BT709_APPLE: + fn->fG = 1.961000000000f; + return true; case ColorSpace::TransferID::SMPTEST428_1: fn->fA = 0.225615407568f; fn->fE = -1.091041666667f; @@ -835,18 +852,16 @@ void ColorSpace::GetTransferMatrix(SkMatrix44* matrix) const { } // BT2020_CL is a special case. - // Basically we return a matrix that transforms RGB values - // to RYB values. (We replace the green component with the - // the luminance.) Later steps will compute the Cb & Cr values. + // Basically we return a matrix that transforms RYB values + // to YUV values. (Note that the green component have been replaced + // with the luminance.) case ColorSpace::MatrixID::BT2020_CL: { Kr = 0.2627f; Kb = 0.0593f; - float data[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, // R - Kr, 1.0f - Kr - Kb, Kb, 0.0f, // Y - 0.0f, 0.0f, 1.0f, 0.0f, // B - 0.0f, 0.0f, 0.0f, 1.0f - }; + float data[16] = {1.0f, 0.0f, 0.0f, 0.0f, // R + Kr, 1.0f - Kr - Kb, Kb, 0.0f, // Y + 0.0f, 0.0f, 1.0f, 0.0f, // B + 0.0f, 0.0f, 0.0f, 1.0f}; matrix->setRowMajorf(data); return; } @@ -912,6 +927,28 @@ void ColorSpace::GetRangeAdjustMatrix(SkMatrix44* matrix) const { } } +bool ColorSpace::ToSkYUVColorSpace(SkYUVColorSpace* out) { + if (range_ == RangeID::FULL) { + *out = kJPEG_SkYUVColorSpace; + return true; + } + switch (matrix_) { + case MatrixID::BT709: + *out = kRec709_SkYUVColorSpace; + return true; + + case MatrixID::BT470BG: + case MatrixID::SMPTE170M: + case MatrixID::SMPTE240M: + *out = kRec601_SkYUVColorSpace; + return true; + + default: + break; + } + return false; +} + std::ostream& operator<<(std::ostream& out, const ColorSpace& color_space) { return out << color_space.ToString(); } diff --git a/chromium/ui/gfx/color_space.h b/chromium/ui/gfx/color_space.h index 561cbc74e2a..3c30511ec9e 100644 --- a/chromium/ui/gfx/color_space.h +++ b/chromium/ui/gfx/color_space.h @@ -13,6 +13,7 @@ #include "base/macros.h" #include "build/build_config.h" #include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkImageInfo.h" #include "ui/gfx/color_space_export.h" namespace IPC { @@ -56,6 +57,9 @@ class COLOR_SPACE_EXPORT ColorSpace { enum class TransferID : uint8_t { INVALID, BT709, + // On macOS, BT709 hardware decoded video frames, when displayed as + // overlays, will have a transfer function of gamma=1.961. + BT709_APPLE, GAMMA18, GAMMA22, GAMMA24, @@ -187,6 +191,10 @@ class COLOR_SPACE_EXPORT ColorSpace { // range, and unspecified spaces. sk_sp<SkColorSpace> ToSkColorSpace() const; + // For YUV color spaces, return the closest SkYUVColorSpace. + // Returns true if a close match is found. + bool ToSkYUVColorSpace(SkYUVColorSpace* out); + void GetPrimaryMatrix(SkMatrix44* to_XYZD50) const; bool GetTransferFunction(SkColorSpaceTransferFn* fn) const; bool GetInverseTransferFunction(SkColorSpaceTransferFn* fn) const; diff --git a/chromium/ui/gfx/color_space_unittest.cc b/chromium/ui/gfx/color_space_unittest.cc index 96dd37d8228..c4a955787fe 100644 --- a/chromium/ui/gfx/color_space_unittest.cc +++ b/chromium/ui/gfx/color_space_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <cmath> +#include <tuple> #include "base/logging.h" #include "testing/gtest/include/gtest/gtest.h" @@ -80,13 +81,13 @@ TEST(ColorSpace, RGBToYUV) { } } -typedef std::tr1::tuple<ColorSpace::TransferID, size_t> TableTestData; +typedef std::tuple<ColorSpace::TransferID, size_t> TableTestData; class ColorSpaceTableTest : public testing::TestWithParam<TableTestData> {}; TEST_P(ColorSpaceTableTest, ApproximateTransferFn) { - ColorSpace::TransferID transfer_id = std::tr1::get<0>(GetParam()); - const size_t table_size = std::tr1::get<1>(GetParam()); + ColorSpace::TransferID transfer_id = std::get<0>(GetParam()); + const size_t table_size = std::get<1>(GetParam()); gfx::ColorSpace color_space(ColorSpace::PrimaryID::BT709, transfer_id); SkColorSpaceTransferFn tr_fn; diff --git a/chromium/ui/gfx/color_space_win.cc b/chromium/ui/gfx/color_space_win.cc index f35f2eba010..84507a6bc6c 100644 --- a/chromium/ui/gfx/color_space_win.cc +++ b/chromium/ui/gfx/color_space_win.cc @@ -118,6 +118,7 @@ DXVA2_ExtendedFormat ColorSpaceWin::GetExtendedFormat( case gfx::ColorSpace::TransferID::SMPTEST2084: case gfx::ColorSpace::TransferID::SMPTEST428_1: case gfx::ColorSpace::TransferID::ARIB_STD_B67: + case gfx::ColorSpace::TransferID::BT709_APPLE: case gfx::ColorSpace::TransferID::GAMMA18: case gfx::ColorSpace::TransferID::GAMMA24: case gfx::ColorSpace::TransferID::SMPTEST2084_NON_HDR: diff --git a/chromium/ui/gfx/color_transform.cc b/chromium/ui/gfx/color_transform.cc index cf3aa80546e..fb64a68d04e 100644 --- a/chromium/ui/gfx/color_transform.cc +++ b/chromium/ui/gfx/color_transform.cc @@ -11,7 +11,6 @@ #include <sstream> #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColorSpaceXform.h" @@ -275,8 +274,9 @@ class ColorTransformInternal : public ColorTransform { gfx::ColorSpace GetDstColorSpace() const override { return dst_; }; void Transform(TriStim* colors, size_t num) const override { - for (const auto& step : steps_) + for (const auto& step : steps_) { step->Transform(colors, num); + } } bool CanGetShaderSource() const override; std::string GetShaderSource() const override; @@ -443,8 +443,6 @@ class ColorTransformSkTransferFn : public ColorTransformPerChannelTransferFn { // ColorTransformPerChannelTransferFn implementation: float Evaluate(float v) const override { // Note that the sign-extension is performed by the caller. - if (v < 0.f) - return 0.f; return SkTransferFnEvalUnclamped(fn_, v); } void AppendTransferShaderSource(std::stringstream* result) const override { @@ -473,14 +471,9 @@ class ColorTransformSkTransferFn : public ColorTransformPerChannelTransferFn { if (std::abs(fn_.fE) > kEpsilon) nonlinear = nonlinear + " + " + Str(fn_.fE); - // Add both parts, skipping the if clause if possible. - if (fn_.fD > kEpsilon) { - *result << " if (v < " << Str(fn_.fD) << ")" << endl; - *result << " return " << linear << ";" << endl; - *result << " return " << nonlinear << ";" << endl; - } else { - *result << " return " << nonlinear << ";" << endl; - } + *result << " if (v < " << Str(fn_.fD) << ")" << endl; + *result << " return " << linear << ";" << endl; + *result << " return " << nonlinear << ";" << endl; } private: @@ -733,7 +726,7 @@ class ColorTransformToBT2020CL : public ColorTransformStep { } else { V = R_Y / (2.0 * 0.4969); } - RYB[i] = ColorTransform::TriStim(RYB[i].y(), U, V); + RYB[i] = ColorTransform::TriStim(RYB[i].y(), U + 0.5, V + 0.5); } } @@ -761,11 +754,11 @@ class ColorTransformFromBT2020CL : public ColorTransformStep { return; for (size_t i = 0; i < num; i++) { float Y = YUV[i].x(); - float U = YUV[i].y(); - float V = YUV[i].z(); + float U = YUV[i].y() - 0.5; + float V = YUV[i].z() - 0.5; float B_Y, R_Y; if (U <= 0) { - B_Y = Y * (-2.0 * -0.9702); + B_Y = U * (-2.0 * -0.9702); } else { B_Y = U * (2.0 * 0.7910); } @@ -775,7 +768,7 @@ class ColorTransformFromBT2020CL : public ColorTransformStep { R_Y = V * (2.0 * 0.4969); } // Return an RYB value, later steps will fix it. - YUV[i] = ColorTransform::TriStim(R_Y + Y, YUV[i].x(), B_Y + Y); + YUV[i] = ColorTransform::TriStim(R_Y + Y, Y, B_Y + Y); } } bool CanAppendShaderSource() override { return true; } @@ -785,12 +778,12 @@ class ColorTransformFromBT2020CL : public ColorTransformStep { *hdr << "vec3 BT2020_YUV_to_RYB_Step" << step_index << "(vec3 color) {" << endl; *hdr << " float Y = color.x;" << endl; - *hdr << " float U = color.y;" << endl; - *hdr << " float V = color.z;" << endl; + *hdr << " float U = color.y - 0.5;" << endl; + *hdr << " float V = color.z - 0.5;" << endl; *hdr << " float B_Y = 0.0;" << endl; *hdr << " float R_Y = 0.0;" << endl; *hdr << " if (U <= 0.0) {" << endl; - *hdr << " B_Y = Y * (-2.0 * -0.9702);" << endl; + *hdr << " B_Y = U * (-2.0 * -0.9702);" << endl; *hdr << " } else {" << endl; *hdr << " B_Y = U * (2.0 * 0.7910);" << endl; *hdr << " }" << endl; @@ -843,8 +836,13 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( steps_.push_back( std::make_unique<ColorTransformMatrix>(GetRangeAdjustMatrix(src))); - steps_.push_back( - std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src)))); + if (src.matrix_ == ColorSpace::MatrixID::BT2020_CL) { + // BT2020 CL is a special case. + steps_.push_back(std::make_unique<ColorTransformFromBT2020CL>()); + } else { + steps_.push_back( + std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src)))); + } // If the target color space is not defined, just apply the adjust and // tranfer matrices. This path is used by YUV to RGB color conversion @@ -865,7 +863,8 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( if (src.matrix_ == ColorSpace::MatrixID::BT2020_CL) { // BT2020 CL is a special case. - steps_.push_back(std::make_unique<ColorTransformFromBT2020CL>()); + steps_.push_back( + std::make_unique<ColorTransformMatrix>(Invert(GetTransferMatrix(src)))); } steps_.push_back( std::make_unique<ColorTransformMatrix>(GetPrimaryTransform(src))); @@ -874,7 +873,8 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( std::make_unique<ColorTransformMatrix>(Invert(GetPrimaryTransform(dst)))); if (dst.matrix_ == ColorSpace::MatrixID::BT2020_CL) { // BT2020 CL is a special case. - steps_.push_back(std::make_unique<ColorTransformToBT2020CL>()); + steps_.push_back( + std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst))); } SkColorSpaceTransferFn dst_from_linear_fn; @@ -885,8 +885,12 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( steps_.push_back(std::make_unique<ColorTransformFromLinear>(dst.transfer_)); } - steps_.push_back( - std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst))); + if (dst.matrix_ == ColorSpace::MatrixID::BT2020_CL) { + steps_.push_back(std::make_unique<ColorTransformToBT2020CL>()); + } else { + steps_.push_back( + std::make_unique<ColorTransformMatrix>(GetTransferMatrix(dst))); + } steps_.push_back(std::make_unique<ColorTransformMatrix>( Invert(GetRangeAdjustMatrix(dst)))); diff --git a/chromium/ui/gfx/color_transform_unittest.cc b/chromium/ui/gfx/color_transform_unittest.cc index 34cd62aba6c..aa5a3de5d1d 100644 --- a/chromium/ui/gfx/color_transform_unittest.cc +++ b/chromium/ui/gfx/color_transform_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <tuple> + #include "base/logging.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/color_space.h" @@ -98,6 +100,36 @@ TEST(SimpleColorSpace, BT709toSRGB) { EXPECT_GT(tmp.z(), tmp.y()); } +TEST(SimpleColorSpace, BT2020CLtoBT2020RGB) { + ColorSpace bt2020cl( + ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::BT2020_10, + ColorSpace::MatrixID::BT2020_CL, ColorSpace::RangeID::LIMITED); + 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)); + + ColorTransform::TriStim tmp(16.0f / 255.0f, 0.5f, 0.5f); + t->Transform(&tmp, 1); + EXPECT_NEAR(tmp.x(), 0.0f, 0.001f); + EXPECT_NEAR(tmp.y(), 0.0f, 0.001f); + EXPECT_NEAR(tmp.z(), 0.0f, 0.001f); + + tmp = ColorTransform::TriStim(235.0f / 255.0f, 0.5f, 0.5f); + t->Transform(&tmp, 1); + EXPECT_NEAR(tmp.x(), 1.0f, 0.001f); + EXPECT_NEAR(tmp.y(), 1.0f, 0.001f); + EXPECT_NEAR(tmp.z(), 1.0f, 0.001f); + + // Test a blue color + tmp = ColorTransform::TriStim(128.0f / 255.0f, 240.0f / 255.0f, 0.5f); + t->Transform(&tmp, 1); + EXPECT_GT(tmp.z(), tmp.x()); + EXPECT_GT(tmp.z(), tmp.y()); +} + TEST(SimpleColorSpace, TransferFnCancel) { ColorSpace::PrimaryID primary = ColorSpace::PrimaryID::BT709; ColorSpace::MatrixID matrix = ColorSpace::MatrixID::RGB; @@ -380,55 +412,32 @@ TEST(SimpleColorSpace, DefaultToSRGB) { EXPECT_EQ(t2->NumberOfStepsForTesting(), 0u); } -// This tests to make sure that we don't emit the "if" or "pow" parts of a +// This tests to make sure that we don't emit "pow" parts of a // transfer function unless necessary. TEST(SimpleColorSpace, ShaderSourceTrFnOptimizations) { SkMatrix44 primaries; gfx::ColorSpace::CreateSRGB().GetPrimaryMatrix(&primaries); - SkColorSpaceTransferFn fn_no_pow_no_if = { + SkColorSpaceTransferFn fn_no_pow = { 1.f, 2.f, 0.f, 1.f, 0.f, 0.f, 0.f, }; - SkColorSpaceTransferFn fn_no_pow_yes_if = { - 1.f, 2.f, 0.f, 1.f, 0.5f, 0.f, 0.f, - }; - SkColorSpaceTransferFn fn_yes_pow_no_if = { + SkColorSpaceTransferFn fn_yes_pow = { 2.f, 2.f, 0.f, 1.f, 0.f, 0.f, 0.f, }; - SkColorSpaceTransferFn fn_yes_pow_yes_if = { - 2.f, 2.f, 0.f, 1.f, 0.5f, 0.f, 0.f, - }; - gfx::ColorSpace src; gfx::ColorSpace dst = gfx::ColorSpace::CreateXYZD50(); std::string shader_string; - src = gfx::ColorSpace::CreateCustom(primaries, fn_no_pow_no_if); - shader_string = ColorTransform::NewColorTransform( - src, dst, ColorTransform::Intent::INTENT_PERCEPTUAL) - ->GetShaderSource(); - EXPECT_EQ(shader_string.find("if ("), std::string::npos); - EXPECT_EQ(shader_string.find("pow("), std::string::npos); - - src = gfx::ColorSpace::CreateCustom(primaries, fn_no_pow_yes_if); + src = gfx::ColorSpace::CreateCustom(primaries, fn_no_pow); shader_string = ColorTransform::NewColorTransform( src, dst, ColorTransform::Intent::INTENT_PERCEPTUAL) ->GetShaderSource(); - EXPECT_NE(shader_string.find("if ("), std::string::npos); EXPECT_EQ(shader_string.find("pow("), std::string::npos); - src = gfx::ColorSpace::CreateCustom(primaries, fn_yes_pow_no_if); - shader_string = ColorTransform::NewColorTransform( - src, dst, ColorTransform::Intent::INTENT_PERCEPTUAL) - ->GetShaderSource(); - EXPECT_EQ(shader_string.find("if ("), std::string::npos); - EXPECT_NE(shader_string.find("pow("), std::string::npos); - - src = gfx::ColorSpace::CreateCustom(primaries, fn_yes_pow_yes_if); + src = gfx::ColorSpace::CreateCustom(primaries, fn_yes_pow); shader_string = ColorTransform::NewColorTransform( src, dst, ColorTransform::Intent::INTENT_PERCEPTUAL) ->GetShaderSource(); - EXPECT_NE(shader_string.find("if ("), std::string::npos); EXPECT_NE(shader_string.find("pow("), std::string::npos); } @@ -456,6 +465,8 @@ TEST(SimpleColorSpace, MAYBE_SampleShaderSource) { " return pow(9.47867334e-01 * v + 5.21326549e-02, 2.40000010e+00);\n" "}\n" "float TransferFn3(float v) {\n" + " if (v < 0.00000000e+00)\n" + " return v;\n" " return pow(v, 3.57142866e-01);\n" "}\n" "vec3 DoColorConversion(vec3 color) {\n" @@ -577,21 +588,21 @@ INSTANTIATE_TEST_CASE_P(ColorSpace, ExtendedTransferTest, testing::ValuesIn(extended_transfers)); -typedef std::tr1::tuple<ColorSpace::PrimaryID, - ColorSpace::TransferID, - ColorSpace::MatrixID, - ColorSpace::RangeID, - ColorTransform::Intent> +typedef std::tuple<ColorSpace::PrimaryID, + ColorSpace::TransferID, + ColorSpace::MatrixID, + ColorSpace::RangeID, + ColorTransform::Intent> ColorSpaceTestData; class ColorSpaceTest : public testing::TestWithParam<ColorSpaceTestData> { public: ColorSpaceTest() - : color_space_(std::tr1::get<0>(GetParam()), - std::tr1::get<1>(GetParam()), - std::tr1::get<2>(GetParam()), - std::tr1::get<3>(GetParam())), - intent_(std::tr1::get<4>(GetParam())) {} + : color_space_(std::get<0>(GetParam()), + std::get<1>(GetParam()), + std::get<2>(GetParam()), + std::get<3>(GetParam())), + intent_(std::get<4>(GetParam())) {} protected: ColorSpace color_space_; diff --git a/chromium/ui/gfx/color_utils.cc b/chromium/ui/gfx/color_utils.cc index e6f537eda2a..c67023bd1cb 100644 --- a/chromium/ui/gfx/color_utils.cc +++ b/chromium/ui/gfx/color_utils.cc @@ -336,15 +336,9 @@ bool IsInvertedColorScheme() { #endif // !defined(OS_WIN) SkColor DeriveDefaultIconColor(SkColor text_color) { - // Lighten a dark color but leave it fully opaque. - if (IsDark(text_color)) { - // For black text, this comes out to kChromeIconGrey. - return color_utils::AlphaBlend(SK_ColorWHITE, text_color, - SkColorGetR(gfx::kChromeIconGrey)); - } - // For a light color, just reduce opacity. - return SkColorSetA(text_color, - static_cast<int>(0.8f * SkColorGetA(text_color))); + // Lighten dark colors and brighten light colors. The alpha value here (0x4c) + // is chosen to generate a value close to GoogleGrey700 from GoogleGrey900. + return BlendTowardOppositeLuma(text_color, 0x4c); } std::string SkColorToRgbaString(SkColor color) { diff --git a/chromium/ui/gfx/decorated_text_mac.h b/chromium/ui/gfx/decorated_text_mac.h new file mode 100644 index 00000000000..ad1aa59b1d7 --- /dev/null +++ b/chromium/ui/gfx/decorated_text_mac.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_GFX_DECORATED_TEXT_MAC_H_ +#define UI_GFX_DECORATED_TEXT_MAC_H_ + +#include "ui/gfx/gfx_export.h" + +@class NSAttributedString; + +namespace gfx { + +struct DecoratedText; + +// Returns a NSAttributedString from |decorated_text|. +GFX_EXPORT NSAttributedString* GetAttributedStringFromDecoratedText( + const DecoratedText& decorated_text); + +} // namespace gfx + +#endif // UI_GFX_DECORATED_TEXT_MAC_H_
\ No newline at end of file diff --git a/chromium/ui/gfx/decorated_text_mac.mm b/chromium/ui/gfx/decorated_text_mac.mm new file mode 100644 index 00000000000..80942a61f8f --- /dev/null +++ b/chromium/ui/gfx/decorated_text_mac.mm @@ -0,0 +1,51 @@ +// 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 "ui/gfx/decorated_text_mac.h" + +#import <Cocoa/Cocoa.h> + +#import "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" +#include "ui/gfx/decorated_text.h" + +namespace gfx { + +NSAttributedString* GetAttributedStringFromDecoratedText( + const DecoratedText& decorated_text) { + base::scoped_nsobject<NSMutableAttributedString> str( + [[NSMutableAttributedString alloc] + initWithString:base::SysUTF16ToNSString(decorated_text.text)]); + [str beginEditing]; + + NSValue* const line_style = + @(NSUnderlineStyleSingle | NSUnderlinePatternSolid); + + for (const auto& attribute : decorated_text.attributes) { + DCHECK(!attribute.range.is_reversed()); + DCHECK_LE(attribute.range.end(), [str length]); + + NSMutableDictionary* attrs = [NSMutableDictionary dictionary]; + NSRange range = attribute.range.ToNSRange(); + + if (attribute.font.GetNativeFont()) + attrs[NSFontAttributeName] = attribute.font.GetNativeFont(); + + // NSFont does not have underline as an attribute. Hence handle it + // separately. + const bool underline = attribute.font.GetStyle() & gfx::Font::UNDERLINE; + if (underline) + attrs[NSUnderlineStyleAttributeName] = line_style; + + if (attribute.strike) + attrs[NSStrikethroughStyleAttributeName] = line_style; + + [str setAttributes:attrs range:range]; + } + + [str endEditing]; + return str.autorelease(); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/font_list_unittest.cc b/chromium/ui/gfx/font_list_unittest.cc index e2bcf215ecd..68e6c40c155 100644 --- a/chromium/ui/gfx/font_list_unittest.cc +++ b/chromium/ui/gfx/font_list_unittest.cc @@ -299,9 +299,10 @@ TEST(FontListTest, MAYBE_Fonts_DeriveWithSizeDelta) { #endif TEST(FontListTest, MAYBE_Fonts_GetHeight_GetBaseline) { // If a font list has only one font, the height and baseline must be the same. - Font font1("Verdana", 16); - ASSERT_EQ("verdana", base::ToLowerASCII(font1.GetActualFontNameForTesting())); - FontList font_list1("Verdana, 16px"); + Font font1(kTestFontName, 16); + ASSERT_EQ(base::ToLowerASCII(kTestFontName), + base::ToLowerASCII(font1.GetActualFontNameForTesting())); + FontList font_list1(std::string(kTestFontName) + ", 16px"); EXPECT_EQ(font1.GetHeight(), font_list1.GetHeight()); EXPECT_EQ(font1.GetBaseline(), font_list1.GetBaseline()); diff --git a/chromium/ui/gfx/font_names_testing.cc b/chromium/ui/gfx/font_names_testing.cc index 09ec558cd7b..8385fe39c3b 100644 --- a/chromium/ui/gfx/font_names_testing.cc +++ b/chromium/ui/gfx/font_names_testing.cc @@ -9,13 +9,19 @@ namespace gfx { #if defined(OS_LINUX) +const char kTestFontName[] = "Arimo"; +#else +const char kTestFontName[] = "Arial"; +#endif + +#if defined(OS_LINUX) const char kSymbolFontName[] = "DejaVu Sans"; #else const char kSymbolFontName[] = "Symbol"; #endif #if defined(OS_LINUX) -const char kCJKFontName[] = "IPAMincho"; +const char kCJKFontName[] = "Noto Sans CJK JP"; #elif defined(OS_MACOSX) const char kCJKFontName[] = "Heiti SC"; #else diff --git a/chromium/ui/gfx/font_names_testing.h b/chromium/ui/gfx/font_names_testing.h index 34c96bef6ad..85a66c3323e 100644 --- a/chromium/ui/gfx/font_names_testing.h +++ b/chromium/ui/gfx/font_names_testing.h @@ -7,6 +7,7 @@ namespace gfx { +extern const char kTestFontName[]; extern const char kSymbolFontName[]; extern const char kCJKFontName[]; diff --git a/chromium/ui/gfx/font_render_params.h b/chromium/ui/gfx/font_render_params.h index 39d72e35203..8cda96de768 100644 --- a/chromium/ui/gfx/font_render_params.h +++ b/chromium/ui/gfx/font_render_params.h @@ -9,7 +9,7 @@ #include <vector> #include "build/build_config.h" -#include "device/vr/features/features.h" +#include "device/vr/buildflags/buildflags.h" #include "third_party/skia/include/core/SkFontLCDConfig.h" #include "ui/gfx/font.h" #include "ui/gfx/gfx_export.h" diff --git a/chromium/ui/gfx/font_render_params_linux_unittest.cc b/chromium/ui/gfx/font_render_params_linux_unittest.cc index 34732edf7d7..32db9e82aa2 100644 --- a/chromium/ui/gfx/font_render_params_linux_unittest.cc +++ b/chromium/ui/gfx/font_render_params_linux_unittest.cc @@ -4,14 +4,16 @@ #include "ui/gfx/font_render_params.h" +#include <fontconfig/fontconfig.h> + #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/macros.h" +#include "base/test/fontconfig_util_linux.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/font.h" #include "ui/gfx/linux_font_delegate.h" -#include "ui/gfx/test/fontconfig_util_linux.h" namespace gfx { @@ -60,7 +62,6 @@ class TestFontDelegate : public LinuxFontDelegate { class FontRenderParamsTest : public testing::Test { public: FontRenderParamsTest() { - SetUpFontconfig(); CHECK(temp_dir_.CreateUniqueTempDir()); original_font_delegate_ = LinuxFontDelegate::instance(); LinuxFontDelegate::SetInstance(&test_font_delegate_); @@ -70,7 +71,17 @@ class FontRenderParamsTest : public testing::Test { ~FontRenderParamsTest() override { LinuxFontDelegate::SetInstance( const_cast<LinuxFontDelegate*>(original_font_delegate_)); - TearDownFontconfig(); + } + + void SetUp() override { + // Fontconfig should already be set up by the test runner. + DCHECK(FcConfigGetCurrent()); + } + + void TearDown() override { + // We might have made a mess introducing new fontconfig settings. Reset the + // state of fontconfig for the next test. + base::SetUpFontconfig(); } protected: @@ -83,7 +94,6 @@ class FontRenderParamsTest : public testing::Test { }; TEST_F(FontRenderParamsTest, Default) { - ASSERT_TRUE(LoadSystemFontIntoFontconfig("arial.ttf")); ASSERT_TRUE(LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + @@ -91,36 +101,36 @@ TEST_F(FontRenderParamsTest, Default) { // match (since this is the style generally used in // /etc/fonts/conf.d). kFontconfigMatchFontHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("autohint", "bool", "true") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + + base::CreateFontconfigEditStanza("antialias", "bool", "true") + + base::CreateFontconfigEditStanza("autohint", "bool", "true") + + base::CreateFontconfigEditStanza("hinting", "bool", "true") + + base::CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + + base::CreateFontconfigEditStanza("rgba", "const", "rgb") + kFontconfigMatchFooter + - // Add a font match for Arial. Since it specifies a family, it - // shouldn't - // take effect when querying default settings. + // Add a font match for Arimo. Since it specifies a family, it + // shouldn't take effect when querying default settings. kFontconfigMatchFontHeader + - CreateFontconfigTestStanza("family", "eq", "string", "Arial") + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("autohint", "bool", "false") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + - CreateFontconfigEditStanza("rgba", "const", "none") + + base::CreateFontconfigTestStanza("family", "eq", "string", "Arimo") + + base::CreateFontconfigEditStanza("antialias", "bool", "true") + + base::CreateFontconfigEditStanza("autohint", "bool", "false") + + base::CreateFontconfigEditStanza("hinting", "bool", "true") + + base::CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + + base::CreateFontconfigEditStanza("rgba", "const", "none") + kFontconfigMatchFooter + // Add font matches for fonts between 10 and 20 points or pixels. - // Since - // they specify sizes, they also should not affect the defaults. + // Since they specify sizes, they also should not affect the defaults. kFontconfigMatchFontHeader + - CreateFontconfigTestStanza("size", "more_eq", "double", "10.0") + - CreateFontconfigTestStanza("size", "less_eq", "double", "20.0") + - CreateFontconfigEditStanza("antialias", "bool", "false") + + base::CreateFontconfigTestStanza("size", "more_eq", "double", + "10.0") + + base::CreateFontconfigTestStanza("size", "less_eq", "double", + "20.0") + + base::CreateFontconfigEditStanza("antialias", "bool", "false") + kFontconfigMatchFooter + kFontconfigMatchFontHeader + - CreateFontconfigTestStanza("pixel_size", "more_eq", "double", - "10.0") + - CreateFontconfigTestStanza("pixel_size", "less_eq", "double", - "20.0") + - CreateFontconfigEditStanza("antialias", "bool", "false") + + base::CreateFontconfigTestStanza("pixel_size", "more_eq", "double", + "10.0") + + base::CreateFontconfigTestStanza("pixel_size", "less_eq", "double", + "20.0") + + base::CreateFontconfigEditStanza("antialias", "bool", "false") + kFontconfigMatchFooter + kFontconfigFileFooter)); FontRenderParams params = GetFontRenderParams( @@ -135,21 +145,21 @@ TEST_F(FontRenderParamsTest, Default) { } TEST_F(FontRenderParamsTest, Size) { - ASSERT_TRUE(LoadSystemFontIntoFontconfig("arial.ttf")); - ASSERT_TRUE(LoadConfigDataIntoFontconfig( + ASSERT_TRUE(base::LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + - CreateFontconfigEditStanza("rgba", "const", "none") + + base::CreateFontconfigEditStanza("antialias", "bool", "true") + + base::CreateFontconfigEditStanza("hinting", "bool", "true") + + base::CreateFontconfigEditStanza("hintstyle", "const", "hintfull") + + base::CreateFontconfigEditStanza("rgba", "const", "none") + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") + - CreateFontconfigEditStanza("antialias", "bool", "false") + + base::CreateFontconfigTestStanza("pixelsize", "less_eq", "double", + "10") + + base::CreateFontconfigEditStanza("antialias", "bool", "false") + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("size", "more_eq", "double", "20") + - CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + + base::CreateFontconfigTestStanza("size", "more_eq", "double", "20") + + base::CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + + base::CreateFontconfigEditStanza("rgba", "const", "rgb") + kFontconfigMatchFooter + kFontconfigFileFooter)); // The defaults should be used when the supplied size isn't matched by the @@ -179,22 +189,21 @@ TEST_F(FontRenderParamsTest, Size) { } TEST_F(FontRenderParamsTest, Style) { - ASSERT_TRUE(LoadSystemFontIntoFontconfig("arial.ttf")); // Load a config that disables subpixel rendering for bold text and disables // hinting for italic text. ASSERT_TRUE(LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + - CreateFontconfigEditStanza("hinting", "bool", "true") + - CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + + base::CreateFontconfigEditStanza("antialias", "bool", "true") + + base::CreateFontconfigEditStanza("hinting", "bool", "true") + + base::CreateFontconfigEditStanza("hintstyle", "const", "hintslight") + + base::CreateFontconfigEditStanza("rgba", "const", "rgb") + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("weight", "eq", "const", "bold") + - CreateFontconfigEditStanza("rgba", "const", "none") + + base::CreateFontconfigTestStanza("weight", "eq", "const", "bold") + + base::CreateFontconfigEditStanza("rgba", "const", "none") + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("slant", "eq", "const", "italic") + - CreateFontconfigEditStanza("hinting", "bool", "false") + + base::CreateFontconfigTestStanza("slant", "eq", "const", "italic") + + base::CreateFontconfigEditStanza("hinting", "bool", "false") + kFontconfigMatchFooter + kFontconfigFileFooter)); FontRenderParamsQuery query; @@ -230,10 +239,10 @@ TEST_F(FontRenderParamsTest, Scalable) { ASSERT_TRUE(LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "false") + + base::CreateFontconfigEditStanza("antialias", "bool", "false") + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("scalable", "eq", "bool", "true") + - CreateFontconfigEditStanza("antialias", "bool", "true") + + base::CreateFontconfigTestStanza("scalable", "eq", "bool", "true") + + base::CreateFontconfigEditStanza("antialias", "bool", "true") + kFontconfigMatchFooter + kFontconfigFileFooter)); // Check that we specifically ask how scalable fonts should be rendered. @@ -243,15 +252,15 @@ TEST_F(FontRenderParamsTest, Scalable) { } TEST_F(FontRenderParamsTest, UseBitmaps) { - ASSERT_TRUE(LoadSystemFontIntoFontconfig("arial.ttf")); // Load a config that enables embedded bitmaps for fonts <= 10 pixels. ASSERT_TRUE(LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("embeddedbitmap", "bool", "false") + + base::CreateFontconfigEditStanza("embeddedbitmap", "bool", "false") + kFontconfigMatchFooter + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("pixelsize", "less_eq", "double", "10") + - CreateFontconfigEditStanza("embeddedbitmap", "bool", "true") + + base::CreateFontconfigTestStanza("pixelsize", "less_eq", "double", + "10") + + base::CreateFontconfigEditStanza("embeddedbitmap", "bool", "true") + kFontconfigMatchFooter + kFontconfigFileFooter)); FontRenderParamsQuery query; @@ -269,10 +278,10 @@ TEST_F(FontRenderParamsTest, ForceFullHintingWhenAntialiasingIsDisabled) { ASSERT_TRUE(LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "false") + - CreateFontconfigEditStanza("hinting", "bool", "false") + - CreateFontconfigEditStanza("hintstyle", "const", "hintnone") + - CreateFontconfigEditStanza("rgba", "const", "rgb") + + base::CreateFontconfigEditStanza("antialias", "bool", "false") + + base::CreateFontconfigEditStanza("hinting", "bool", "false") + + base::CreateFontconfigEditStanza("hintstyle", "const", "hintnone") + + base::CreateFontconfigEditStanza("rgba", "const", "rgb") + kFontconfigMatchFooter + kFontconfigFileFooter)); // Full hinting should be forced. See the comment in GetFontRenderParams() for @@ -318,7 +327,7 @@ TEST_F(FontRenderParamsTest, OnlySetConfiguredValues) { ASSERT_TRUE(LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + kFontconfigMatchPatternHeader + - CreateFontconfigEditStanza("antialias", "bool", "true") + + base::CreateFontconfigEditStanza("antialias", "bool", "true") + kFontconfigMatchFooter + kFontconfigFileFooter)); // The subpixel rendering setting from the delegate should make it through. @@ -328,7 +337,9 @@ TEST_F(FontRenderParamsTest, OnlySetConfiguredValues) { } TEST_F(FontRenderParamsTest, NoFontconfigMatch) { - // Don't load a Fontconfig configuration. + // A default configuration was set up globally. Reset it to a blank config. + FcConfigSetCurrent(FcConfigCreate()); + FontRenderParams system_params; system_params.antialiasing = true; system_params.hinting = FontRenderParams::HINTING_MEDIUM; @@ -336,7 +347,7 @@ TEST_F(FontRenderParamsTest, NoFontconfigMatch) { test_font_delegate_.set_params(system_params); FontRenderParamsQuery query; - query.families.push_back("Arial"); + query.families.push_back("Arimo"); query.families.push_back("Times New Roman"); query.pixel_size = 10; std::string suggested_family; @@ -350,43 +361,39 @@ TEST_F(FontRenderParamsTest, NoFontconfigMatch) { } TEST_F(FontRenderParamsTest, MissingFamily) { - // With Arial and Verdana installed, request (in order) Helvetica, Arial, and - // Verdana and check that Arial is returned. - ASSERT_TRUE(LoadSystemFontIntoFontconfig("arial.ttf")); - ASSERT_TRUE(LoadSystemFontIntoFontconfig("verdana.ttf")); + // With Arimo and Verdana installed, request (in order) Helvetica, Arimo, and + // Verdana and check that Arimo is returned. FontRenderParamsQuery query; query.families.push_back("Helvetica"); - query.families.push_back("Arial"); + query.families.push_back("Arimo"); query.families.push_back("Verdana"); std::string suggested_family; GetFontRenderParams(query, &suggested_family); - EXPECT_EQ("Arial", suggested_family); + EXPECT_EQ("Arimo", suggested_family); } TEST_F(FontRenderParamsTest, SubstituteFamily) { - // Configure Fontconfig to use Verdana for both Helvetica and Arial. - ASSERT_TRUE(LoadSystemFontIntoFontconfig("arial.ttf")); - ASSERT_TRUE(LoadSystemFontIntoFontconfig("verdana.ttf")); + // Configure Fontconfig to use Tinos for both Helvetica and Arimo. ASSERT_TRUE(LoadConfigDataIntoFontconfig( temp_dir_.GetPath(), std::string(kFontconfigFileHeader) + - CreateFontconfigAliasStanza("Helvetica", "Verdana") + + base::CreateFontconfigAliasStanza("Helvetica", "Tinos") + kFontconfigMatchPatternHeader + - CreateFontconfigTestStanza("family", "eq", "string", "Arial") + - CreateFontconfigEditStanza("family", "string", "Verdana") + + base::CreateFontconfigTestStanza("family", "eq", "string", "Arimo") + + base::CreateFontconfigEditStanza("family", "string", "Tinos") + kFontconfigMatchFooter + kFontconfigFileFooter)); FontRenderParamsQuery query; query.families.push_back("Helvetica"); std::string suggested_family; GetFontRenderParams(query, &suggested_family); - EXPECT_EQ("Verdana", suggested_family); + EXPECT_EQ("Tinos", suggested_family); query.families.clear(); - query.families.push_back("Arial"); + query.families.push_back("Arimo"); suggested_family.clear(); GetFontRenderParams(query, &suggested_family); - EXPECT_EQ("Verdana", suggested_family); + EXPECT_EQ("Tinos", suggested_family); } } // namespace gfx diff --git a/chromium/ui/gfx/font_unittest.cc b/chromium/ui/gfx/font_unittest.cc index c74aae18f14..9b9eae0194d 100644 --- a/chromium/ui/gfx/font_unittest.cc +++ b/chromium/ui/gfx/font_unittest.cc @@ -54,14 +54,15 @@ int ScopedMinimumFontSizeCallback::minimum_size_ = 0; #define MAYBE_LoadArial LoadArial #endif TEST_F(FontTest, MAYBE_LoadArial) { - Font cf("Arial", 16); + Font cf(kTestFontName, 16); #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_IOS) EXPECT_TRUE(cf.GetNativeFont()); #endif EXPECT_EQ(cf.GetStyle(), Font::NORMAL); EXPECT_EQ(cf.GetFontSize(), 16); - EXPECT_EQ(cf.GetFontName(), "Arial"); - EXPECT_EQ("arial", base::ToLowerASCII(cf.GetActualFontNameForTesting())); + EXPECT_EQ(cf.GetFontName(), kTestFontName); + EXPECT_EQ(base::ToLowerASCII(kTestFontName), + base::ToLowerASCII(cf.GetActualFontNameForTesting())); } #if defined(OS_ANDROID) @@ -70,14 +71,15 @@ TEST_F(FontTest, MAYBE_LoadArial) { #define MAYBE_LoadArialBold LoadArialBold #endif TEST_F(FontTest, MAYBE_LoadArialBold) { - Font cf("Arial", 16); + Font cf(kTestFontName, 16); Font bold(cf.Derive(0, Font::NORMAL, Font::Weight::BOLD)); #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_IOS) EXPECT_TRUE(bold.GetNativeFont()); #endif EXPECT_EQ(bold.GetStyle(), Font::NORMAL); EXPECT_EQ(bold.GetWeight(), Font::Weight::BOLD); - EXPECT_EQ("arial", base::ToLowerASCII(cf.GetActualFontNameForTesting())); + EXPECT_EQ(base::ToLowerASCII(kTestFontName), + base::ToLowerASCII(cf.GetActualFontNameForTesting())); } #if defined(OS_ANDROID) @@ -86,7 +88,7 @@ TEST_F(FontTest, MAYBE_LoadArialBold) { #define MAYBE_Ascent Ascent #endif TEST_F(FontTest, MAYBE_Ascent) { - Font cf("Arial", 16); + Font cf(kTestFontName, 16); EXPECT_GT(cf.GetBaseline(), 2); EXPECT_LE(cf.GetBaseline(), 22); } @@ -97,7 +99,7 @@ TEST_F(FontTest, MAYBE_Ascent) { #define MAYBE_Height Height #endif TEST_F(FontTest, MAYBE_Height) { - Font cf("Arial", 16); + Font cf(kTestFontName, 16); EXPECT_GE(cf.GetHeight(), 16); // TODO(akalin): Figure out why height is so large on Linux. EXPECT_LE(cf.GetHeight(), 26); @@ -109,7 +111,7 @@ TEST_F(FontTest, MAYBE_Height) { #define MAYBE_CapHeight CapHeight #endif TEST_F(FontTest, MAYBE_CapHeight) { - Font cf("Arial", 16); + Font cf(kTestFontName, 16); EXPECT_GT(cf.GetCapHeight(), 0); EXPECT_GT(cf.GetCapHeight(), cf.GetHeight() / 2); EXPECT_LT(cf.GetCapHeight(), cf.GetBaseline()); @@ -121,7 +123,7 @@ TEST_F(FontTest, MAYBE_CapHeight) { #define MAYBE_AvgWidths AvgWidths #endif TEST_F(FontTest, MAYBE_AvgWidths) { - Font cf("Arial", 16); + Font cf(kTestFontName, 16); EXPECT_EQ(cf.GetExpectedTextWidth(0), 0); EXPECT_GT(cf.GetExpectedTextWidth(1), cf.GetExpectedTextWidth(0)); EXPECT_GT(cf.GetExpectedTextWidth(2), cf.GetExpectedTextWidth(1)); @@ -140,8 +142,9 @@ TEST_F(FontTest, MAYBE_AvgWidths) { // fonts may be installed but still need enabling in Font Book.app. // http://crbug.com/347429 TEST_F(FontTest, MAYBE_GetActualFontNameForTesting) { - Font arial("Arial", 16); - EXPECT_EQ("arial", base::ToLowerASCII(arial.GetActualFontNameForTesting())) + Font arial(kTestFontName, 16); + EXPECT_EQ(base::ToLowerASCII(kTestFontName), + base::ToLowerASCII(arial.GetActualFontNameForTesting())) << "********\n" << "Your test environment seems to be missing Arial font, which is " << "needed for unittests. Check if Arial font is installed.\n" @@ -169,7 +172,7 @@ TEST_F(FontTest, MAYBE_GetActualFontNameForTesting) { #define MAYBE_DeriveFont DeriveFont #endif TEST_F(FontTest, MAYBE_DeriveFont) { - Font cf("Arial", 8); + Font cf(kTestFontName, 8); const int kSizeDelta = 2; Font cf_underlined = cf.Derive(0, cf.GetStyle() | gfx::Font::UNDERLINE, cf.GetWeight()); @@ -183,7 +186,7 @@ TEST_F(FontTest, MAYBE_DeriveFont) { #if defined(OS_WIN) TEST_F(FontTest, DeriveResizesIfSizeTooSmall) { - Font cf("Arial", 8); + Font cf(kTestFontName, 8); // The minimum font size is set to 5 in browser_main.cc. ScopedMinimumFontSizeCallback minimum_size(5); @@ -192,7 +195,7 @@ TEST_F(FontTest, DeriveResizesIfSizeTooSmall) { } TEST_F(FontTest, DeriveKeepsOriginalSizeIfHeightOk) { - Font cf("Arial", 8); + Font cf(kTestFontName, 8); // The minimum font size is set to 5 in browser_main.cc. ScopedMinimumFontSizeCallback minimum_size(5); diff --git a/chromium/ui/gfx/gpu_fence_handle.h b/chromium/ui/gfx/gpu_fence_handle.h index 093626c89f7..29a9c097a40 100644 --- a/chromium/ui/gfx/gpu_fence_handle.h +++ b/chromium/ui/gfx/gpu_fence_handle.h @@ -5,7 +5,6 @@ #ifndef UI_GFX_GPU_FENCE_HANDLE_H_ #define UI_GFX_GPU_FENCE_HANDLE_H_ -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "ui/gfx/gfx_export.h" diff --git a/chromium/ui/gfx/image/canvas_image_source.cc b/chromium/ui/gfx/image/canvas_image_source.cc index 728977c0ef3..4d68d315bde 100644 --- a/chromium/ui/gfx/image/canvas_image_source.cc +++ b/chromium/ui/gfx/image/canvas_image_source.cc @@ -6,22 +6,52 @@ #include "base/logging.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/insets.h" #include "ui/gfx/image/image_skia.h" namespace gfx { +namespace { + +class PaddedImageSource : public CanvasImageSource { + public: + PaddedImageSource(const ImageSkia& image, const Insets& insets) + : CanvasImageSource(Size(image.width() + insets.width(), + image.height() + insets.height()), + false), + image_(image), + insets_(insets) {} + + // CanvasImageSource: + void Draw(Canvas* canvas) override { + canvas->DrawImageInt(image_, insets_.left(), insets_.top()); + } + + private: + const ImageSkia image_; + const Insets insets_; + + DISALLOW_COPY_AND_ASSIGN(PaddedImageSource); +}; + +} // namespace + //////////////////////////////////////////////////////////////////////////////// // CanvasImageSource -CanvasImageSource::CanvasImageSource(const gfx::Size& size, bool is_opaque) - : size_(size), - is_opaque_(is_opaque) { +// static +ImageSkia CanvasImageSource::CreatePadded(const ImageSkia& image, + const Insets& insets) { + return MakeImageSkia<PaddedImageSource>(image, insets); } -gfx::ImageSkiaRep CanvasImageSource::GetImageForScale(float scale) { - gfx::Canvas canvas(size_, scale, is_opaque_); +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_); Draw(&canvas); - return gfx::ImageSkiaRep(canvas.GetBitmap(), scale); + return ImageSkiaRep(canvas.GetBitmap(), scale); } } // namespace gfx diff --git a/chromium/ui/gfx/image/canvas_image_source.h b/chromium/ui/gfx/image/canvas_image_source.h index 558648d3ce7..20e13accfa8 100644 --- a/chromium/ui/gfx/image/canvas_image_source.h +++ b/chromium/ui/gfx/image/canvas_image_source.h @@ -9,7 +9,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/gfx_export.h" #include "ui/gfx/image/image_skia.h" @@ -18,37 +17,41 @@ namespace gfx { class Canvas; class ImageSkiaRep; +class Insets; -// CanvasImageSource is useful if you need to generate an image for -// a scale factor using gfx::Canvas. It creates a new Canvas -// with target scale factor and generates ImageSkiaRep when drawing is -// completed. -class GFX_EXPORT CanvasImageSource : public gfx::ImageSkiaSource { +// CanvasImageSource is useful if you need to generate an image for a scale +// factor using Canvas. It creates a new Canvas with target scale factor and +// generates ImageSkiaRep when drawing is completed. +class GFX_EXPORT CanvasImageSource : public ImageSkiaSource { public: // Factory function to create an ImageSkia from a CanvasImageSource. Example: - // gfx::ImageSkia my_image = + // ImageSkia my_image = // CanvasImageSource::MakeImageSkia<MySource>(param1, param2); template <typename T, typename... Args> static ImageSkia MakeImageSkia(Args&&... args) { auto source = std::make_unique<T>(std::forward<Args>(args)...); - gfx::Size size = source->size(); - return gfx::ImageSkia(std::move(source), size); + Size size = source->size(); + return ImageSkia(std::move(source), size); } - CanvasImageSource(const gfx::Size& size, bool is_opaque); + // Creates a Image containing |image| with transparent padding around the + // edges as specified by |insets|. + static ImageSkia CreatePadded(const ImageSkia& image, const Insets& insets); + + CanvasImageSource(const Size& size, bool is_opaque); ~CanvasImageSource() override {} // Called when a new image needs to be drawn for a scale factor. - virtual void Draw(gfx::Canvas* canvas) = 0; + virtual void Draw(Canvas* canvas) = 0; // Returns the size of images in DIP that this source will generate. - const gfx::Size& size() const { return size_; }; + const Size& size() const { return size_; }; - // Overridden from gfx::ImageSkiaSource. - gfx::ImageSkiaRep GetImageForScale(float scale) override; + // Overridden from ImageSkiaSource. + ImageSkiaRep GetImageForScale(float scale) override; protected: - const gfx::Size size_; + const Size size_; const bool is_opaque_; DISALLOW_COPY_AND_ASSIGN(CanvasImageSource); }; diff --git a/chromium/ui/gfx/image/mojo/image_traits_unittest.cc b/chromium/ui/gfx/image/mojo/image_traits_unittest.cc index bca8f37c04f..de87d867765 100644 --- a/chromium/ui/gfx/image/mojo/image_traits_unittest.cc +++ b/chromium/ui/gfx/image/mojo/image_traits_unittest.cc @@ -6,7 +6,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "mojo/public/cpp/bindings/binding_set.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ui/gfx/interpolated_transform.cc b/chromium/ui/gfx/interpolated_transform.cc index f5bb4f40569..5771a1e3e95 100644 --- a/chromium/ui/gfx/interpolated_transform.cc +++ b/chromium/ui/gfx/interpolated_transform.cc @@ -6,7 +6,6 @@ #include <cmath> -#include "base/memory/ptr_util.h" #include "base/logging.h" #include "ui/gfx/animation/tween.h" diff --git a/chromium/ui/gfx/interpolated_transform_unittest.cc b/chromium/ui/gfx/interpolated_transform_unittest.cc index 40ed89a74f3..6856c2c057b 100644 --- a/chromium/ui/gfx/interpolated_transform_unittest.cc +++ b/chromium/ui/gfx/interpolated_transform_unittest.cc @@ -4,7 +4,6 @@ #include "ui/gfx/interpolated_transform.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" diff --git a/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc b/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc index a9fd8622ef5..ea246a986e8 100644 --- a/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc +++ b/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc @@ -63,7 +63,9 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { format == gfx::BufferFormat::YVU_420; case gfx::BufferUsage::SCANOUT: return format == gfx::BufferFormat::BGRX_8888 || - format == gfx::BufferFormat::RGBX_8888; + format == gfx::BufferFormat::RGBX_8888 || + format == gfx::BufferFormat::RGBA_8888 || + format == gfx::BufferFormat::BGRA_8888; case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE: return #if defined(ARCH_CPU_X86_FAMILY) diff --git a/chromium/ui/gfx/linux/native_pixmap_dmabuf.cc b/chromium/ui/gfx/linux/native_pixmap_dmabuf.cc index 8502ccb227e..e03e8f615a8 100644 --- a/chromium/ui/gfx/linux/native_pixmap_dmabuf.cc +++ b/chromium/ui/gfx/linux/native_pixmap_dmabuf.cc @@ -75,7 +75,8 @@ bool NativePixmapDmaBuf::ScheduleOverlayPlane( int plane_z_order, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return false; } diff --git a/chromium/ui/gfx/linux/native_pixmap_dmabuf.h b/chromium/ui/gfx/linux/native_pixmap_dmabuf.h index d01868ad2a2..f32389e7318 100644 --- a/chromium/ui/gfx/linux/native_pixmap_dmabuf.h +++ b/chromium/ui/gfx/linux/native_pixmap_dmabuf.h @@ -42,7 +42,8 @@ class GFX_EXPORT NativePixmapDmaBuf : public gfx::NativePixmap { int plane_z_order, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; gfx::NativePixmapHandle ExportHandle() override; protected: diff --git a/chromium/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h b/chromium/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h index b20492cf197..d67d1b60865 100644 --- a/chromium/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h +++ b/chromium/ui/gfx/mac/scoped_cocoa_disable_screen_updates.h @@ -5,10 +5,8 @@ #ifndef UI_GFX_MAC_SCOPED_COCOA_DISABLE_SCREEN_UPDATES_H_ #define UI_GFX_MAC_SCOPED_COCOA_DISABLE_SCREEN_UPDATES_H_ -#import <Cocoa/Cocoa.h> - -#include "base/mac/mac_util.h" #include "base/macros.h" +#include "ui/gfx/gfx_export.h" namespace gfx { @@ -17,25 +15,10 @@ namespace gfx { // can be nested, and there is a time-maximum (about 1 second) after which // Cocoa will automatically re-enable updating. This class doesn't attempt to // overrule that. -class ScopedCocoaDisableScreenUpdates { +class GFX_EXPORT ScopedCocoaDisableScreenUpdates { public: - ScopedCocoaDisableScreenUpdates() { - if (base::mac::IsAtLeastOS10_11()) { - // Beginning with OS X 10.11, [NSAnimationContext beginGrouping] is the - // preferred way of disabling screen updates. Use of - // NSDisableScreenUpdates() is discouraged. - [NSAnimationContext beginGrouping]; - } else { - NSDisableScreenUpdates(); - } - } - ~ScopedCocoaDisableScreenUpdates() { - if (base::mac::IsAtLeastOS10_11()) { - [NSAnimationContext endGrouping]; - } else { - NSEnableScreenUpdates(); - } - } + ScopedCocoaDisableScreenUpdates(); + ~ScopedCocoaDisableScreenUpdates(); private: DISALLOW_COPY_AND_ASSIGN(ScopedCocoaDisableScreenUpdates); diff --git a/chromium/ui/gfx/mac/scoped_cocoa_disable_screen_updates.mm b/chromium/ui/gfx/mac/scoped_cocoa_disable_screen_updates.mm new file mode 100644 index 00000000000..981a6eff43d --- /dev/null +++ b/chromium/ui/gfx/mac/scoped_cocoa_disable_screen_updates.mm @@ -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. + +#include "ui/gfx/mac/scoped_cocoa_disable_screen_updates.h" + +#import <Cocoa/Cocoa.h> + +#include "base/mac/mac_util.h" + +namespace gfx { + +ScopedCocoaDisableScreenUpdates::ScopedCocoaDisableScreenUpdates() { + if (base::mac::IsAtLeastOS10_11()) { + // Beginning with OS X 10.11, [NSAnimationContext beginGrouping] is the + // preferred way of disabling screen updates. Use of + // NSDisableScreenUpdates() is discouraged. + [NSAnimationContext beginGrouping]; + } else { + NSDisableScreenUpdates(); + } +} + +ScopedCocoaDisableScreenUpdates::~ScopedCocoaDisableScreenUpdates() { + if (base::mac::IsAtLeastOS10_11()) { + [NSAnimationContext endGrouping]; + } else { + NSEnableScreenUpdates(); + } +} + +} // namespace gfx diff --git a/chromium/ui/gfx/native_pixmap.h b/chromium/ui/gfx/native_pixmap.h index c168001700c..abf442c6dcc 100644 --- a/chromium/ui/gfx/native_pixmap.h +++ b/chromium/ui/gfx/native_pixmap.h @@ -52,11 +52,14 @@ class NativePixmap : public base::RefCountedThreadSafe<NativePixmap> { // |crop_rect| specifies the region within the buffer to be placed // inside |display_bounds|. This is specified in texture coordinates, in the // range of [0,1]. + // |enable_blend| specifies if the plane should be alpha blended, with premul + // apha, when scanned out. virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int plane_z_order, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, - const gfx::RectF& crop_rect) = 0; + const gfx::RectF& crop_rect, + bool enable_blend) = 0; // Export the buffer for sharing across processes. // Any file descriptors in the exported handle are owned by the caller. diff --git a/chromium/ui/gfx/paint_vector_icon.cc b/chromium/ui/gfx/paint_vector_icon.cc index b9832d0f231..d2968ffe32f 100644 --- a/chromium/ui/gfx/paint_vector_icon.cc +++ b/chromium/ui/gfx/paint_vector_icon.cc @@ -10,7 +10,6 @@ #include "base/i18n/rtl.h" #include "base/lazy_instance.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/trace_event/trace_event.h" @@ -42,12 +41,14 @@ struct CompareIconDescription { // Helper that simplifies iterating over a sequence of PathElements. class PathParser { public: - PathParser(const PathElement* path_elements) - : path_elements_(path_elements) {} + PathParser(const PathElement* path_elements, size_t path_size) + : path_elements_(path_elements), path_size_(path_size) {} ~PathParser() {} void Advance() { command_index_ += GetArgumentCount() + 1; } + bool HasCommandsRemaining() const { return command_index_ < path_size_; } + CommandType CurrentCommand() const { return path_elements_[command_index_].command; } @@ -103,7 +104,6 @@ class PathParser { case FLIPS_IN_RTL: case TRANSITION_FROM: case TRANSITION_TO: - case END: return 0; } @@ -112,7 +112,8 @@ class PathParser { } const PathElement* path_elements_; - int command_index_ = 0; + size_t path_size_; + size_t command_index_ = 0; DISALLOW_COPY_AND_ASSIGN(PathParser); }; @@ -149,7 +150,6 @@ CommandType CommandFromString(const std::string& source) { RETURN_IF_IS(CLIP); RETURN_IF_IS(DISABLE_AA); RETURN_IF_IS(FLIPS_IN_RTL); - RETURN_IF_IS(END); #undef RETURN_IF_IS NOTREACHED() << "Unrecognized command: " << source; @@ -175,6 +175,7 @@ std::vector<PathElement> PathFromSource(const std::string& source) { void PaintPath(Canvas* canvas, const PathElement* path_elements, + size_t path_size, int dip_size, SkColor color, const base::TimeDelta& elapsed_time) { @@ -188,8 +189,8 @@ void PaintPath(Canvas* canvas, bool flips_in_rtl = false; CommandType previous_command_type = NEW_PATH; - for (PathParser parser(path_elements); parser.CurrentCommand() != END; - parser.Advance()) { + for (PathParser parser(path_elements, path_size); + parser.HasCommandsRemaining(); parser.Advance()) { auto arg = [&parser](int i) { return parser.GetArgument(i); }; const CommandType command_type = parser.CurrentCommand(); auto start_new_path = [&paths]() { @@ -430,10 +431,6 @@ void PaintPath(Canvas* canvas, flags_array.pop_back(); break; } - - case END: - NOTREACHED(); - break; } previous_command_type = command_type; @@ -481,7 +478,7 @@ class VectorIconSource : public CanvasImageSource { if (!data_.badge_icon.is_empty()) PaintVectorIcon(canvas, data_.badge_icon, size_.width(), data_.color); } else { - PaintPath(canvas, path_.data(), size_.width(), data_.color, + PaintPath(canvas, path_.data(), path_.size(), size_.width(), data_.color, base::TimeDelta()); } } @@ -540,7 +537,7 @@ IconDescription::IconDescription(const VectorIcon& icon, // If an icon has a .1x.icon version, it should only be rendered at the size // specified in that definition. - if (icon.path_1x) + if (icon.rep_1x) DCHECK_EQ(this->dip_size, GetDefaultSizeOfVectorIcon(icon)); } @@ -562,17 +559,13 @@ void PaintVectorIcon(Canvas* canvas, SkColor color, const base::TimeDelta& elapsed_time) { DCHECK(!icon.is_empty()); - if (icon.path) { - DCHECK(icon.path_size > 0); - DCHECK_EQ(END, icon.path[icon.path_size - 1].command) << icon.name; - } - if (icon.path_1x) { - DCHECK(icon.path_1x_size > 0); - DCHECK_EQ(END, icon.path_1x[icon.path_1x_size - 1].command) << icon.name; - } - const PathElement* path = - (canvas->image_scale() == 1.f && icon.path_1x) ? icon.path_1x : icon.path; - PaintPath(canvas, path, dip_size, color, elapsed_time); + if (icon.rep) + DCHECK(icon.rep->path_size > 0); + if (icon.rep_1x) + DCHECK(icon.rep_1x->path_size > 0); + const VectorIconRep* rep = + (canvas->image_scale() == 1.f && icon.rep_1x) ? icon.rep_1x : icon.rep; + PaintPath(canvas, rep->path, rep->path_size, dip_size, color, elapsed_time); } ImageSkia CreateVectorIcon(const IconDescription& params) { @@ -609,15 +602,16 @@ ImageSkia CreateVectorIconFromSource(const std::string& source, } int GetDefaultSizeOfVectorIcon(const VectorIcon& icon) { - const PathElement* one_x_path = icon.path_1x ? icon.path_1x : icon.path; + const PathElement* one_x_path = + icon.rep_1x ? icon.rep_1x->path : icon.rep->path; return one_x_path[0].command == CANVAS_DIMENSIONS ? one_x_path[1].arg : kReferenceSizeDip; } base::TimeDelta GetDurationOfAnimation(const VectorIcon& icon) { base::TimeDelta last_motion; - for (PathParser parser(icon.path); parser.CurrentCommand() != END; - parser.Advance()) { + for (PathParser parser(icon.rep->path, icon.rep->path_size); + parser.HasCommandsRemaining(); parser.Advance()) { if (parser.CurrentCommand() != TRANSITION_END) continue; diff --git a/chromium/ui/gfx/paint_vector_icon_unittest.cc b/chromium/ui/gfx/paint_vector_icon_unittest.cc index 6570dc52d8b..1188c35e23f 100644 --- a/chromium/ui/gfx/paint_vector_icon_unittest.cc +++ b/chromium/ui/gfx/paint_vector_icon_unittest.cc @@ -43,15 +43,11 @@ TEST(VectorIconTest, RelativeMoveToAfterClose) { Canvas canvas(recorder.beginRecording(100, 100), 1.0f); const PathElement elements[] = { - MOVE_TO, 4, 5, - LINE_TO, 10, 11, - CLOSE, + MOVE_TO, 4, 5, LINE_TO, 10, 11, CLOSE, // This move should use (4, 5) as the start point rather than (10, 11). - R_MOVE_TO, 20, 21, - R_LINE_TO, 50, 51, - END, - }; - const VectorIcon icon = {elements, arraysize(elements)}; + R_MOVE_TO, 20, 21, R_LINE_TO, 50, 51}; + const VectorIconRep icon_rep = {elements, arraysize(elements)}; + const VectorIcon icon = {&icon_rep}; PaintVectorIcon(&canvas, icon, 100, SK_ColorMAGENTA); sk_sp<cc::PaintRecord> record = recorder.finishRecordingAsPicture(); @@ -78,17 +74,21 @@ TEST(VectorIconTest, FlipsInRtl) { // Create a 20x20 square icon which has FLIPS_IN_RTL, and CANVAS_DIMENSIONS // are twice as large as |canvas|. - const PathElement elements[] = { - CANVAS_DIMENSIONS, 2 * canvas_size, - FLIPS_IN_RTL, - MOVE_TO, 10, 10, - R_H_LINE_TO, 20, - R_V_LINE_TO, 20, - R_H_LINE_TO, -20, - CLOSE, - END, - }; - const VectorIcon icon = {elements, arraysize(elements)}; + const PathElement elements[] = {CANVAS_DIMENSIONS, + 2 * canvas_size, + FLIPS_IN_RTL, + MOVE_TO, + 10, + 10, + R_H_LINE_TO, + 20, + R_V_LINE_TO, + 20, + R_H_LINE_TO, + -20, + CLOSE}; + const VectorIconRep icon_rep = {elements, arraysize(elements)}; + const VectorIcon icon = {&icon_rep}; PaintVectorIcon(&canvas, icon, canvas_size, color); // Count the number of pixels in the canvas. diff --git a/chromium/ui/gfx/platform_font_linux_unittest.cc b/chromium/ui/gfx/platform_font_linux_unittest.cc index f677ba4dda9..4d73bee9bf6 100644 --- a/chromium/ui/gfx/platform_font_linux_unittest.cc +++ b/chromium/ui/gfx/platform_font_linux_unittest.cc @@ -13,7 +13,6 @@ #include "ui/gfx/font.h" #include "ui/gfx/font_render_params.h" #include "ui/gfx/linux_font_delegate.h" -#include "ui/gfx/test/fontconfig_util_linux.h" namespace gfx { @@ -62,7 +61,6 @@ class TestFontDelegate : public LinuxFontDelegate { class PlatformFontLinuxTest : public testing::Test { public: PlatformFontLinuxTest() { - SetUpFontconfig(); original_font_delegate_ = LinuxFontDelegate::instance(); LinuxFontDelegate::SetInstance(&test_font_delegate_); } @@ -71,7 +69,6 @@ class PlatformFontLinuxTest : public testing::Test { LinuxFontDelegate::SetInstance( const_cast<LinuxFontDelegate*>(original_font_delegate_)); PlatformFontLinux::ReloadDefaultFont(); - TearDownFontconfig(); } protected: @@ -87,13 +84,10 @@ class PlatformFontLinuxTest : public testing::Test { // Test that PlatformFontLinux's default constructor initializes the instance // with the correct parameters. TEST_F(PlatformFontLinuxTest, DefaultFont) { - ASSERT_TRUE(LoadSystemFontIntoFontconfig("arial.ttf")); - ASSERT_TRUE(LoadSystemFontIntoFontconfig("times_new_roman.ttf")); - #if defined(OS_CHROMEOS) - PlatformFontLinux::SetDefaultFontDescription("Arial,Times New Roman,13px"); + PlatformFontLinux::SetDefaultFontDescription("Arimo,Tinos,13px"); #else - test_font_delegate_.set_family("Arial"); + test_font_delegate_.set_family("Arimo"); test_font_delegate_.set_size_pixels(13); test_font_delegate_.set_style(Font::NORMAL); FontRenderParams params; @@ -102,7 +96,7 @@ TEST_F(PlatformFontLinuxTest, DefaultFont) { test_font_delegate_.set_params(params); #endif scoped_refptr<gfx::PlatformFontLinux> font(new gfx::PlatformFontLinux()); - EXPECT_EQ("Arial", font->GetFontName()); + EXPECT_EQ("Arimo", font->GetFontName()); EXPECT_EQ(13, font->GetFontSize()); EXPECT_EQ(gfx::Font::NORMAL, font->GetStyle()); #if !defined(OS_CHROMEOS) @@ -114,16 +108,16 @@ TEST_F(PlatformFontLinuxTest, DefaultFont) { // Drop the old default font and check that new settings are loaded. #if defined(OS_CHROMEOS) PlatformFontLinux::SetDefaultFontDescription( - "Times New Roman,Arial,Bold Italic 15px"); + "Tinos,Arimo,Bold Italic 15px"); #else - test_font_delegate_.set_family("Times New Roman"); + test_font_delegate_.set_family("Tinos"); test_font_delegate_.set_size_pixels(15); test_font_delegate_.set_style(gfx::Font::ITALIC); test_font_delegate_.set_weight(gfx::Font::Weight::BOLD); #endif PlatformFontLinux::ReloadDefaultFont(); scoped_refptr<gfx::PlatformFontLinux> font2(new gfx::PlatformFontLinux()); - EXPECT_EQ("Times New Roman", font2->GetFontName()); + EXPECT_EQ("Tinos", font2->GetFontName()); EXPECT_EQ(15, font2->GetFontSize()); EXPECT_NE(font2->GetStyle() & Font::ITALIC, 0); EXPECT_EQ(gfx::Font::Weight::BOLD, font2->GetWeight()); diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc index 3d706df56ee..39702db77da 100644 --- a/chromium/ui/gfx/render_text.cc +++ b/chromium/ui/gfx/render_text.cc @@ -13,7 +13,6 @@ #include "base/feature_list.h" #include "base/i18n/break_iterator.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/numerics/ranges.h" #include "base/stl_util.h" #include "base/strings/string_util.h" @@ -278,14 +277,17 @@ void SkiaTextRenderer::DrawStrike(int x, StyleIterator::StyleIterator(const BreakList<SkColor>& colors, const BreakList<BaselineStyle>& baselines, + const BreakList<int>& font_size_overrides, const BreakList<Font::Weight>& weights, const std::vector<BreakList<bool>>& styles) : colors_(colors), baselines_(baselines), + font_size_overrides_(font_size_overrides), weights_(weights), styles_(styles) { color_ = colors_.breaks().begin(); baseline_ = baselines_.breaks().begin(); + font_size_override_ = font_size_overrides_.breaks().begin(); weight_ = weights_.breaks().begin(); for (size_t i = 0; i < styles_.size(); ++i) style_.push_back(styles_[i].breaks().begin()); @@ -296,6 +298,7 @@ StyleIterator::~StyleIterator() {} Range StyleIterator::GetRange() const { Range range(colors_.GetRange(color_)); range = range.Intersect(baselines_.GetRange(baseline_)); + range = range.Intersect(font_size_overrides_.GetRange(font_size_override_)); range = range.Intersect(weights_.GetRange(weight_)); for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) range = range.Intersect(styles_[i].GetRange(style_[i])); @@ -305,6 +308,7 @@ Range StyleIterator::GetRange() const { void StyleIterator::UpdatePosition(size_t position) { color_ = colors_.GetBreak(position); baseline_ = baselines_.GetBreak(position); + font_size_override_ = font_size_overrides_.GetBreak(position); weight_ = weights_.GetBreak(position); for (size_t i = 0; i < NUM_TEXT_STYLES; ++i) style_[i] = styles_[i].GetBreak(position); @@ -380,6 +384,7 @@ std::unique_ptr<RenderText> RenderText::CreateInstanceOfSameStyle( render_text->set_truncate_length(truncate_length_); render_text->styles_ = styles_; render_text->baselines_ = baselines_; + render_text->font_size_overrides_ = font_size_overrides_; render_text->colors_ = colors_; render_text->weights_ = weights_; return render_text; @@ -396,6 +401,7 @@ void RenderText::SetText(const base::string16& text) { // the first style to the whole text instead. colors_.SetValue(colors_.breaks().begin()->second); baselines_.SetValue(baselines_.breaks().begin()->second); + font_size_overrides_.SetValue(font_size_overrides_.breaks().begin()->second); weights_.SetValue(weights_.breaks().begin()->second); for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) styles_[style].SetValue(styles_[style].breaks().begin()->second); @@ -709,6 +715,11 @@ void RenderText::ApplyBaselineStyle(BaselineStyle value, const Range& range) { baselines_.ApplyValue(value, range); } +void RenderText::ApplyFontSizeOverride(int font_size_override, + const Range& range) { + font_size_overrides_.ApplyValue(font_size_override, range); +} + void RenderText::SetStyle(TextStyle style, bool value) { styles_[style].SetValue(value); @@ -973,9 +984,9 @@ Vector2d RenderText::GetLineOffset(size_t line_number) { return offset; } -bool RenderText::GetDecoratedWordAtPoint(const Point& point, - DecoratedText* decorated_word, - Point* baseline_point) { +bool RenderText::GetWordLookupDataAtPoint(const Point& point, + DecoratedText* decorated_word, + Point* baseline_point) { if (obscured()) return false; @@ -990,9 +1001,16 @@ bool RenderText::GetDecoratedWordAtPoint(const Point& point, DCHECK(!word_range.is_reversed()); DCHECK(!word_range.is_empty()); - const std::vector<Rect> word_bounds = GetSubstringBounds(word_range); - if (word_bounds.empty() || - !GetDecoratedTextForRange(word_range, decorated_word)) { + return GetLookupDataForRange(word_range, decorated_word, baseline_point); +} + +bool RenderText::GetLookupDataForRange(const Range& range, + DecoratedText* decorated_text, + Point* baseline_point) { + EnsureLayout(); + + const std::vector<Rect> word_bounds = GetSubstringBounds(range); + if (word_bounds.empty() || !GetDecoratedTextForRange(range, decorated_text)) { return false; } @@ -1027,6 +1045,7 @@ RenderText::RenderText() composition_range_(Range::InvalidRange()), colors_(kDefaultColor), baselines_(NORMAL_BASELINE), + font_size_overrides_(0), weights_(Font::Weight::NORMAL), styles_(NUM_TEXT_STYLES), composition_and_selection_styles_applied_(false), @@ -1357,6 +1376,7 @@ void RenderText::UpdateStyleLengths() { const size_t text_length = text_.length(); colors_.SetMax(text_length); baselines_.SetMax(text_length); + font_size_overrides_.SetMax(text_length); weights_.SetMax(text_length); for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) styles_[style].SetMax(text_length); @@ -1561,6 +1581,7 @@ base::string16 RenderText::Elide(const base::string16& text, for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) RestoreBreakList(render_text.get(), &render_text->styles_[style]); RestoreBreakList(render_text.get(), &render_text->baselines_); + RestoreBreakList(render_text.get(), &render_text->font_size_overrides_); render_text->weights_ = weights_; RestoreBreakList(render_text.get(), &render_text->weights_); diff --git a/chromium/ui/gfx/render_text.h b/chromium/ui/gfx/render_text.h index 47fa4c605ce..4cc2bdc85fe 100644 --- a/chromium/ui/gfx/render_text.h +++ b/chromium/ui/gfx/render_text.h @@ -88,6 +88,7 @@ class StyleIterator { public: StyleIterator(const BreakList<SkColor>& colors, const BreakList<BaselineStyle>& baselines, + const BreakList<int>& font_size_overrides, const BreakList<Font::Weight>& weights, const std::vector<BreakList<bool>>& styles); ~StyleIterator(); @@ -95,6 +96,7 @@ class StyleIterator { // Get the colors and styles at the current iterator position. SkColor color() const { return color_->second; } BaselineStyle baseline() const { return baseline_->second; } + int font_size_override() const { return font_size_override_->second; } bool style(TextStyle s) const { return style_[s]->second; } Font::Weight weight() const { return weight_->second; } @@ -107,11 +109,13 @@ class StyleIterator { private: BreakList<SkColor> colors_; BreakList<BaselineStyle> baselines_; + BreakList<int> font_size_overrides_; BreakList<Font::Weight> weights_; std::vector<BreakList<bool> > styles_; BreakList<SkColor>::const_iterator color_; BreakList<BaselineStyle>::const_iterator baseline_; + BreakList<int>::const_iterator font_size_override_; BreakList<Font::Weight>::const_iterator weight_; std::vector<BreakList<bool>::const_iterator> style_; @@ -365,11 +369,17 @@ class GFX_EXPORT RenderText { void SetColor(SkColor value); void ApplyColor(SkColor value, const Range& range); + // DEPRECATED. // Set the baseline style over the entire text or a logical character range. // The |range| should be valid, non-reversed, and within [0, text().length()]. + // TODO(tapted): Remove this. The only client is moving to + // ApplyFontSizeOverride. void SetBaselineStyle(BaselineStyle value); void ApplyBaselineStyle(BaselineStyle value, const Range& range); + // Alters the font size in |range|. + void ApplyFontSizeOverride(int font_size_override, const Range& range); + // Set various text styles over the entire text or a logical character range. // The respective |style| is applied if |value| is true, or removed if false. // The |range| should be valid, non-reversed, and within [0, text().length()]. @@ -507,9 +517,19 @@ class GFX_EXPORT RenderText { // at the point, returns a nearby word. |baseline_point| should correspond to // the baseline point of the leftmost glyph of the |word| in the view's // coordinates. Returns false, if no word can be retrieved. - bool GetDecoratedWordAtPoint(const Point& point, - DecoratedText* decorated_word, - Point* baseline_point); + bool GetWordLookupDataAtPoint(const Point& point, + DecoratedText* decorated_word, + Point* baseline_point); + + // Retrieves the text at |range| along with its styling information. + // |baseline_point| should correspond to the baseline point of + // the leftmost glyph of the text in the view's coordinates. If the text + // spans multiple lines, |baseline_point| will correspond with the leftmost + // glyph on the first line in the range. Returns false, if no text can be + // retrieved. + bool GetLookupDataForRange(const Range& range, + DecoratedText* decorated_text, + Point* baseline_point); // Retrieves the text in the given |range|. base::string16 GetTextFromRange(const Range& range) const; @@ -527,6 +547,9 @@ class GFX_EXPORT RenderText { const BreakList<SkColor>& colors() const { return colors_; } const BreakList<BaselineStyle>& baselines() const { return baselines_; } + const BreakList<int>& font_size_overrides() const { + return font_size_overrides_; + } const BreakList<Font::Weight>& weights() const { return weights_; } const std::vector<BreakList<bool> >& styles() const { return styles_; } SkScalar strike_thickness_factor() const { return strike_thickness_factor_; } @@ -767,6 +790,7 @@ class GFX_EXPORT RenderText { // TODO(msw): Expand to support cursor, selection, background, etc. colors. BreakList<SkColor> colors_; BreakList<BaselineStyle> baselines_; + BreakList<int> font_size_overrides_; BreakList<Font::Weight> weights_; std::vector<BreakList<bool> > styles_; diff --git a/chromium/ui/gfx/render_text_harfbuzz.cc b/chromium/ui/gfx/render_text_harfbuzz.cc index 9178adbe330..838ee6215ea 100644 --- a/chromium/ui/gfx/render_text_harfbuzz.cc +++ b/chromium/ui/gfx/render_text_harfbuzz.cc @@ -1415,7 +1415,8 @@ void RenderTextHarfBuzz::ItemizeTextToRuns( DCHECK_LE(text.size(), baselines().max()); for (const BreakList<bool>& style : styles()) DCHECK_LE(text.size(), style.max()); - internal::StyleIterator style(empty_colors, baselines(), weights(), styles()); + internal::StyleIterator style(empty_colors, baselines(), + font_size_overrides(), weights(), styles()); for (size_t run_break = 0; run_break < text.length();) { auto run = std::make_unique<internal::TextRunHarfBuzz>( @@ -1423,6 +1424,7 @@ void RenderTextHarfBuzz::ItemizeTextToRuns( run->range.set_start(run_break); run->italic = style.style(ITALIC); run->baseline_type = style.baseline(); + run->font_size = style.font_size_override(); run->strike = style.style(STRIKE); run->underline = style.style(UNDERLINE); run->heavy_underline = style.style(HEAVY_UNDERLINE); @@ -1496,7 +1498,8 @@ void RenderTextHarfBuzz::ShapeRun(const base::string16& text, internal::TextRunHarfBuzz* run) { const Font& primary_font = font_list().GetPrimaryFont(); const std::string primary_family = primary_font.GetFontName(); - run->font_size = primary_font.GetFontSize(); + if (run->font_size == 0) + run->font_size = primary_font.GetFontSize(); run->baseline_offset = 0; if (run->baseline_type != NORMAL_BASELINE) { // Calculate a slightly smaller font. The ratio here is somewhat arbitrary. diff --git a/chromium/ui/gfx/render_text_mac.mm b/chromium/ui/gfx/render_text_mac.mm index bf162b3ddfc..41139a49c0f 100644 --- a/chromium/ui/gfx/render_text_mac.mm +++ b/chromium/ui/gfx/render_text_mac.mm @@ -349,7 +349,8 @@ base::ScopedCFTypeRef<CFMutableArrayRef> RenderTextMac::ApplyStyles( CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); // https://developer.apple.com/library/mac/#documentation/Carbon/Reference/CoreText_StringAttributes_Ref/Reference/reference.html - internal::StyleIterator style(colors(), baselines(), weights(), styles()); + internal::StyleIterator style(colors(), baselines(), font_size_overrides(), + weights(), styles()); const size_t layout_text_length = CFAttributedStringGetLength(attr_string); for (size_t i = 0, end = 0; i < layout_text_length; i = end) { end = TextIndexToGivenTextIndex(text, style.GetRange().end()); diff --git a/chromium/ui/gfx/render_text_test_api.h b/chromium/ui/gfx/render_text_test_api.h index dbb6c4436e6..00981c3cd76 100644 --- a/chromium/ui/gfx/render_text_test_api.h +++ b/chromium/ui/gfx/render_text_test_api.h @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifndef UI_GFX_RENDER_TEXT_TEST_API_H_ +#define UI_GFX_RENDER_TEXT_TEST_API_H_ + #include "base/macros.h" #include "ui/gfx/break_list.h" #include "ui/gfx/geometry/vector2d.h" @@ -37,6 +40,10 @@ class RenderTextTestApi { return render_text_->baselines(); } + const BreakList<int>& font_size_overrides() const { + return render_text_->font_size_overrides(); + } + const BreakList<Font::Weight>& weights() const { return render_text_->weights(); } @@ -85,3 +92,5 @@ class RenderTextTestApi { } // namespace test } // namespace gfx + +#endif // UI_GFX_RENDER_TEXT_TEST_API_H_ diff --git a/chromium/ui/gfx/render_text_unittest.cc b/chromium/ui/gfx/render_text_unittest.cc index 5a47cee1adf..b1fb9063920 100644 --- a/chromium/ui/gfx/render_text_unittest.cc +++ b/chromium/ui/gfx/render_text_unittest.cc @@ -15,7 +15,6 @@ #include "base/format_macros.h" #include "base/i18n/break_iterator.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -597,6 +596,7 @@ TEST_P(RenderTextTest, DefaultStyles) { for (size_t i = 0; i < arraysize(cases); ++i) { EXPECT_TRUE(test_api()->colors().EqualsValueForTesting(SK_ColorBLACK)); EXPECT_TRUE(test_api()->baselines().EqualsValueForTesting(NORMAL_BASELINE)); + EXPECT_TRUE(test_api()->font_size_overrides().EqualsValueForTesting(0)); for (size_t style = 0; style < NUM_TEXT_STYLES; ++style) EXPECT_TRUE(test_api()->styles()[style].EqualsValueForTesting(false)); render_text->SetText(UTF8ToUTF16(cases[i])); @@ -632,32 +632,27 @@ TEST_P(RenderTextTest, ApplyStyles) { RenderText* render_text = GetRenderText(); render_text->SetText(UTF8ToUTF16("012345678")); + constexpr int kTestFontSizeOverride = 20; + // Apply a ranged color and style and check the resulting breaks. render_text->ApplyColor(SK_ColorRED, Range(1, 4)); render_text->ApplyBaselineStyle(SUPERIOR, Range(2, 4)); render_text->ApplyWeight(Font::Weight::BOLD, Range(2, 5)); - std::vector<std::pair<size_t, SkColor> > expected_color; - expected_color.push_back(std::pair<size_t, SkColor>(0, SK_ColorBLACK)); - expected_color.push_back(std::pair<size_t, SkColor>(1, SK_ColorRED)); - expected_color.push_back(std::pair<size_t, SkColor>(4, SK_ColorBLACK)); - EXPECT_TRUE(test_api()->colors().EqualsForTesting(expected_color)); - std::vector<std::pair<size_t, BaselineStyle>> expected_baseline_style; - expected_baseline_style.push_back( - std::pair<size_t, BaselineStyle>(0, NORMAL_BASELINE)); - expected_baseline_style.push_back( - std::pair<size_t, BaselineStyle>(2, SUPERIOR)); - expected_baseline_style.push_back( - std::pair<size_t, BaselineStyle>(4, NORMAL_BASELINE)); + render_text->ApplyFontSizeOverride(kTestFontSizeOverride, Range(5, 7)); + + EXPECT_TRUE(test_api()->colors().EqualsForTesting( + {{0, SK_ColorBLACK}, {1, SK_ColorRED}, {4, SK_ColorBLACK}})); + + EXPECT_TRUE(test_api()->baselines().EqualsForTesting( + {{0, NORMAL_BASELINE}, {2, SUPERIOR}, {4, NORMAL_BASELINE}})); + + EXPECT_TRUE(test_api()->font_size_overrides().EqualsForTesting( + {{0, 0}, {5, kTestFontSizeOverride}, {7, 0}})); + EXPECT_TRUE( - test_api()->baselines().EqualsForTesting(expected_baseline_style)); - std::vector<std::pair<size_t, Font::Weight>> expected_weight; - expected_weight.push_back( - std::pair<size_t, Font::Weight>(0, Font::Weight::NORMAL)); - expected_weight.push_back( - std::pair<size_t, Font::Weight>(2, Font::Weight::BOLD)); - expected_weight.push_back( - std::pair<size_t, Font::Weight>(5, Font::Weight::NORMAL)); - EXPECT_TRUE(test_api()->weights().EqualsForTesting(expected_weight)); + test_api()->weights().EqualsForTesting({{0, Font::Weight::NORMAL}, + {2, Font::Weight::BOLD}, + {5, Font::Weight::NORMAL}})); // Ensure that setting a value overrides the ranged values. render_text->SetColor(SK_ColorBLUE); @@ -674,30 +669,18 @@ TEST_P(RenderTextTest, ApplyStyles) { render_text->ApplyColor(SK_ColorRED, Range(0, text_length)); render_text->ApplyBaselineStyle(SUPERIOR, Range(0, text_length)); render_text->ApplyWeight(Font::Weight::BOLD, Range(2, text_length)); - std::vector<std::pair<size_t, SkColor> > expected_color_end; - expected_color_end.push_back(std::pair<size_t, SkColor>(0, SK_ColorRED)); - EXPECT_TRUE(test_api()->colors().EqualsForTesting(expected_color_end)); - std::vector<std::pair<size_t, BaselineStyle>> expected_baseline_end; - expected_baseline_end.push_back( - std::pair<size_t, BaselineStyle>(0, SUPERIOR)); - EXPECT_TRUE(test_api()->baselines().EqualsForTesting(expected_baseline_end)); - std::vector<std::pair<size_t, Font::Weight>> expected_weight_end; - expected_weight_end.push_back( - std::pair<size_t, Font::Weight>(0, Font::Weight::NORMAL)); - expected_weight_end.push_back( - std::pair<size_t, Font::Weight>(2, Font::Weight::BOLD)); - EXPECT_TRUE(test_api()->weights().EqualsForTesting(expected_weight_end)); + + EXPECT_TRUE(test_api()->colors().EqualsForTesting({{0, SK_ColorRED}})); + EXPECT_TRUE(test_api()->baselines().EqualsForTesting({{0, SUPERIOR}})); + EXPECT_TRUE(test_api()->weights().EqualsForTesting( + {{0, Font::Weight::NORMAL}, {2, Font::Weight::BOLD}})); // Ensure ranged values adjust to accommodate text length changes. render_text->ApplyStyle(ITALIC, true, Range(0, 2)); render_text->ApplyStyle(ITALIC, true, Range(3, 6)); render_text->ApplyStyle(ITALIC, true, Range(7, text_length)); - std::vector<std::pair<size_t, bool> > expected_italic; - expected_italic.push_back(std::pair<size_t, bool>(0, true)); - expected_italic.push_back(std::pair<size_t, bool>(2, false)); - expected_italic.push_back(std::pair<size_t, bool>(3, true)); - expected_italic.push_back(std::pair<size_t, bool>(6, false)); - expected_italic.push_back(std::pair<size_t, bool>(7, true)); + std::vector<std::pair<size_t, bool>> expected_italic = { + {0, true}, {2, false}, {3, true}, {6, false}, {7, true}}; EXPECT_TRUE(test_api()->styles()[ITALIC].EqualsForTesting(expected_italic)); // Changing the text should clear any breaks except for the first one. @@ -721,44 +704,42 @@ TEST_P(RenderTextTest, ApplyStyles) { // Styles shouldn't be changed mid-grapheme. render_text->SetText(UTF8ToUTF16("0\u0915\u093f1\u0915\u093f2")); render_text->ApplyStyle(UNDERLINE, true, Range(2, 5)); - std::vector<std::pair<size_t, bool>> expected_underline; - expected_underline.push_back(std::pair<size_t, bool>(0, false)); - expected_underline.push_back(std::pair<size_t, bool>(1, true)); - expected_underline.push_back(std::pair<size_t, bool>(6, false)); - EXPECT_TRUE( - test_api()->styles()[UNDERLINE].EqualsForTesting(expected_underline)); + EXPECT_TRUE(test_api()->styles()[UNDERLINE].EqualsForTesting( + {{0, false}, {1, true}, {6, false}})); } } TEST_P(RenderTextTest, AppendTextKeepsStyles) { RenderText* render_text = GetRenderText(); // Setup basic functionality. - render_text->SetText(UTF8ToUTF16("abc")); + render_text->SetText(UTF8ToUTF16("abcd")); render_text->ApplyColor(SK_ColorRED, Range(0, 1)); render_text->ApplyBaselineStyle(SUPERSCRIPT, Range(1, 2)); render_text->ApplyStyle(UNDERLINE, true, Range(2, 3)); + render_text->ApplyFontSizeOverride(20, Range(3, 4)); // Verify basic functionality. - std::vector<std::pair<size_t, SkColor>> expected_color; - expected_color.push_back(std::pair<size_t, SkColor>(0, SK_ColorRED)); - expected_color.push_back(std::pair<size_t, SkColor>(1, SK_ColorBLACK)); + const std::vector<std::pair<size_t, SkColor>> expected_color = { + {0, SK_ColorRED}, {1, SK_ColorBLACK}}; EXPECT_TRUE(test_api()->colors().EqualsForTesting(expected_color)); - std::vector<std::pair<size_t, BaselineStyle>> expected_baseline; - expected_baseline.push_back( - std::pair<size_t, BaselineStyle>(0, NORMAL_BASELINE)); - expected_baseline.push_back(std::pair<size_t, BaselineStyle>(1, SUPERSCRIPT)); - expected_baseline.push_back( - std::pair<size_t, BaselineStyle>(2, NORMAL_BASELINE)); + const std::vector<std::pair<size_t, BaselineStyle>> expected_baseline = { + {0, NORMAL_BASELINE}, {1, SUPERSCRIPT}, {2, NORMAL_BASELINE}}; EXPECT_TRUE(test_api()->baselines().EqualsForTesting(expected_baseline)); - std::vector<std::pair<size_t, bool>> expected_style; - expected_style.push_back(std::pair<size_t, bool>(0, false)); - expected_style.push_back(std::pair<size_t, bool>(2, true)); + const std::vector<std::pair<size_t, bool>> expected_style = { + {0, false}, {2, true}, {3, false}}; EXPECT_TRUE(test_api()->styles()[UNDERLINE].EqualsForTesting(expected_style)); + const std::vector<std::pair<size_t, int>> expected_font_size = {{0, 0}, + {3, 20}}; + EXPECT_TRUE( + test_api()->font_size_overrides().EqualsForTesting(expected_font_size)); + // Ensure AppendText maintains current text styles. - render_text->AppendText(UTF8ToUTF16("def")); - EXPECT_EQ(render_text->GetDisplayText(), UTF8ToUTF16("abcdef")); + render_text->AppendText(UTF8ToUTF16("efg")); + EXPECT_EQ(render_text->GetDisplayText(), UTF8ToUTF16("abcdefg")); EXPECT_TRUE(test_api()->colors().EqualsForTesting(expected_color)); EXPECT_TRUE(test_api()->baselines().EqualsForTesting(expected_baseline)); EXPECT_TRUE(test_api()->styles()[UNDERLINE].EqualsForTesting(expected_style)); + EXPECT_TRUE( + test_api()->font_size_overrides().EqualsForTesting(expected_font_size)); } void TestVisualCursorMotionInObscuredField( @@ -962,10 +943,6 @@ TEST_P(RenderTextTest, ObscuredEmoji) { render_text->Draw(canvas()); } -// TODO(PORT): Fails for RenderTextMac. -// Crashes on Mac with RenderTextHarfBuzz. See http://crbug.com/640068. -#if !defined(OS_MACOSX) - TEST_P(RenderTextTest, ElidedText) { // TODO(skanuj) : Add more test cases for following // - RenderText styles. @@ -1067,8 +1044,6 @@ TEST_P(RenderTextTest, ElidedObscuredText) { EXPECT_EQ(elided_obscured_text, render_text->GetDisplayText()); } -#endif // !defined(OS_MACOSX) - // TODO(PORT): Fails for RenderTextMac. TEST_P(RenderTextHarfBuzzTest, MultilineElide) { RenderText* render_text = GetRenderText(); @@ -1814,9 +1789,6 @@ TEST_P(RenderTextHarfBuzzTest, MoveCursorLeftRight_ComplexScript) { EXPECT_EQ(0U, render_text->cursor_position()); } -// TODO(asvitkine): RenderTextMac cursor movements. http://crbug.com/131618 -// Crashes on Mac with RenderTextHarfBuzz. See http://crbug.com/640068. -#if !defined(OS_MACOSX) TEST_P(RenderTextHarfBuzzTest, MoveCursorLeftRight_MeiryoUILigatures) { RenderText* render_text = GetRenderText(); // Meiryo UI uses single-glyph ligatures for 'ff' and 'ffi', but each letter @@ -1841,7 +1813,6 @@ TEST_P(RenderTextHarfBuzzTest, MoveCursorLeftRight_MeiryoUILigatures) { } EXPECT_EQ(6U, render_text->cursor_position()); } -#endif // !defined(OS_MACOSX) TEST_P(RenderTextHarfBuzzTest, GraphemePositions) { // LTR कि (DEVANAGARI KA with VOWEL I) (2-char grapheme), LTR abc, and LTR कि. @@ -2502,26 +2473,26 @@ TEST_P(RenderTextTest, StringSizeEmptyString) { } TEST_P(RenderTextTest, StringSizeRespectsFontListMetrics) { - // Check that Verdana and the CJK font have different font metrics. - Font verdana_font("Verdana", 16); - ASSERT_EQ("verdana", - base::ToLowerASCII(verdana_font.GetActualFontNameForTesting())); + // Check that the test font and the CJK font have different font metrics. + Font test_font(kTestFontName, 16); + ASSERT_EQ(base::ToLowerASCII(kTestFontName), + base::ToLowerASCII(test_font.GetActualFontNameForTesting())); Font cjk_font(kCJKFontName, 16); ASSERT_EQ(base::ToLowerASCII(kCJKFontName), base::ToLowerASCII(cjk_font.GetActualFontNameForTesting())); - EXPECT_NE(verdana_font.GetHeight(), cjk_font.GetHeight()); - EXPECT_NE(verdana_font.GetBaseline(), cjk_font.GetBaseline()); - // "a" should be rendered with Verdana, not with the CJK font. - const char* verdana_font_text = "a"; + EXPECT_NE(test_font.GetHeight(), cjk_font.GetHeight()); + EXPECT_NE(test_font.GetBaseline(), cjk_font.GetBaseline()); + // "a" should be rendered with the test font, not with the CJK font. + const char* test_font_text = "a"; // "円" (U+5168 Han character YEN) should render with the CJK font, not - // Verdana. + // the test font. const char* cjk_font_text = "\u5168"; - Font smaller_font = verdana_font; + Font smaller_font = test_font; Font larger_font = cjk_font; - const char* smaller_font_text = verdana_font_text; + const char* smaller_font_text = test_font_text; const char* larger_font_text = cjk_font_text; - if (cjk_font.GetHeight() < verdana_font.GetHeight() && - cjk_font.GetBaseline() < verdana_font.GetBaseline()) { + if (cjk_font.GetHeight() < test_font.GetHeight() && + cjk_font.GetBaseline() < test_font.GetBaseline()) { std::swap(smaller_font, larger_font); std::swap(smaller_font_text, larger_font_text); } @@ -4251,11 +4222,17 @@ TEST_P(RenderTextTest, StringFitsOwnWidth) { // Ensure that RenderText examines all of the fonts in its FontList before // falling back to other fonts. TEST_P(RenderTextHarfBuzzTest, HarfBuzz_FontListFallback) { +#if defined(OS_LINUX) + const char kTestFont[] = "Arimo"; +#else + const char kTestFont[] = "Arial"; +#endif // Double-check that the requested fonts are present. - FontList font_list(base::StringPrintf("Arial, %s, 12px", kSymbolFontName)); + std::string format = std::string(kTestFont) + ", %s, 12px"; + FontList font_list(base::StringPrintf(format.c_str(), kSymbolFontName)); const std::vector<Font>& fonts = font_list.GetFonts(); ASSERT_EQ(2u, fonts.size()); - ASSERT_EQ("arial", + ASSERT_EQ(base::ToLowerASCII(kTestFont), base::ToLowerASCII(fonts[0].GetActualFontNameForTesting())); ASSERT_EQ(base::ToLowerASCII(kSymbolFontName), base::ToLowerASCII(fonts[1].GetActualFontNameForTesting())); @@ -4369,13 +4346,13 @@ TEST_P(RenderTextTest, TextDoesntClip) { } { SCOPED_TRACE("TextDoesntClip Left Side"); -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) || \ +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ defined(ARCH_CPU_MIPS_FAMILY) - // TODO(dschuyler): On Windows, Chrome OS, and Mac smoothing draws to the - // left of text. This appears to be a preexisting issue that wasn't - // revealed by the prior unit tests. RenderText currently only uses - // origins and advances and ignores bounding boxes so cannot account for - // under- and over-hang. + // TODO(dschuyler): On Windows, Chrome OS, Linux, and Mac smoothing draws + // to the left of text. This appears to be a preexisting issue that + // wasn't revealed by the prior unit tests. RenderText currently only + // uses origins and advances and ignores bounding boxes so cannot account + // for under- and over-hang. rect_buffer.EnsureSolidRect(SK_ColorWHITE, 0, kTestSize, kTestSize - 1, string_size.height()); #else @@ -4385,13 +4362,13 @@ TEST_P(RenderTextTest, TextDoesntClip) { } { SCOPED_TRACE("TextDoesntClip Right Side"); -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) || \ +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ defined(ARCH_CPU_MIPS_FAMILY) - // TODO(dschuyler): On Windows, Chrome OS, and Mac smoothing draws to the - // right of text. This appears to be a preexisting issue that wasn't - // revealed by the prior unit tests. RenderText currently only uses - // origins and advances and ignores bounding boxes so cannot account for - // under- and over-hang. + // TODO(dschuyler): On Windows, Chrome OS, Linux, and Mac smoothing draws + // to the right of text. This appears to be a preexisting issue that + // wasn't revealed by the prior unit tests. RenderText currently only + // uses origins and advances and ignores bounding boxes so cannot account + // for under- and over-hang. rect_buffer.EnsureSolidRect(SK_ColorWHITE, kTestSize + string_size.width() + 1, kTestSize, kTestSize - 1, @@ -4574,9 +4551,9 @@ TEST_P(RenderTextTest, SubpixelRenderingSuppressed) { EXPECT_FALSE(GetRendererPaint().isLCDRenderText()); } -// Verify GetDecoratedWordAtPoint returns the correct baseline point and +// Verify GetWordLookupDataAtPoint returns the correct baseline point and // decorated word for an LTR string. -TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_LTR) { +TEST_P(RenderTextHarfBuzzTest, GetWordLookupDataAtPoint_LTR) { const base::string16 ltr = UTF8ToUTF16(" ab c "); const int kWordOneStartIndex = 2; const int kWordTwoStartIndex = 6; @@ -4621,14 +4598,14 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_LTR) { { SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds")); - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( Point(-5, cursor_y), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); } { SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds")); - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( Point(105, cursor_y), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); @@ -4641,8 +4618,8 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_LTR) { render_text->GetCursorBounds(SelectionModel(i, CURSOR_FORWARD), false) .origin(); - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint(query, &decorated_word, - &baseline_point)); + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint(query, &decorated_word, + &baseline_point)); if (i < kWordTwoStartIndex) { VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); @@ -4654,9 +4631,9 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_LTR) { } } -// Verify GetDecoratedWordAtPoint returns the correct baseline point and +// Verify GetWordLookupDataAtPoint returns the correct baseline point and // decorated word for an RTL string. -TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_RTL) { +TEST_P(RenderTextHarfBuzzTest, GetWordLookupDataAtPoint_RTL) { const base::string16 rtl = UTF8ToUTF16(" \u0634\u0632 \u0634"); const int kWordOneStartIndex = 1; const int kWordTwoStartIndex = 5; @@ -4699,14 +4676,14 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_RTL) { { SCOPED_TRACE(base::StringPrintf("Query to the left of text bounds")); - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( Point(-5, cursor_y), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); } { SCOPED_TRACE(base::StringPrintf("Query to the right of text bounds")); - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( Point(105, cursor_y), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); @@ -4721,8 +4698,8 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_RTL) { render_text->GetCursorBounds(SelectionModel(i, CURSOR_FORWARD), false) .top_right(); - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint(query, &decorated_word, - &baseline_point)); + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint(query, &decorated_word, + &baseline_point)); if (i < kWordTwoStartIndex) { VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); @@ -4733,8 +4710,8 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_RTL) { } } -// Test that GetDecoratedWordAtPoint behaves correctly for multiline text. -TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_Multiline) { +// Test that GetWordLookupDataAtPoint behaves correctly for multiline text. +TEST_P(RenderTextHarfBuzzTest, GetWordLookupDataAtPoint_Multiline) { const base::string16 text = UTF8ToUTF16("a b\n..\ncd."); const size_t kWordOneIndex = 0; // Index of character 'a'. const size_t kWordTwoIndex = 2; // Index of character 'b'. @@ -4784,36 +4761,36 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_Multiline) { Point baseline_point; { // Query to the left of the first line. - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( Point(-5, GetCursorYForTesting(0)), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); } { // Query on the second line. - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( Point(5, GetCursorYForTesting(1)), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); } { // Query at the center point of the character 'c'. - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( left_glyph_word_3.CenterPoint(), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_3, decorated_word); EXPECT_TRUE(left_glyph_word_3.Contains(baseline_point)); } { // Query to the right of the third line. - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint( + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint( Point(505, GetCursorYForTesting(2)), &decorated_word, &baseline_point)); VerifyDecoratedWordsAreEqual(expected_word_3, decorated_word); EXPECT_TRUE(left_glyph_word_3.Contains(baseline_point)); } } -// Verify the boolean return value of GetDecoratedWordAtPoint. -TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_Return) { +// Verify the boolean return value of GetWordLookupDataAtPoint. +TEST_P(RenderTextHarfBuzzTest, GetWordLookupDataAtPoint_Return) { RenderText* render_text = GetRenderText(); render_text->SetText(UTF8ToUTF16("...")); @@ -4824,21 +4801,90 @@ TEST_P(RenderTextHarfBuzzTest, GetDecoratedWordAtPoint_Return) { Point query = render_text->GetCursorBounds(SelectionModel(0, CURSOR_FORWARD), false) .origin(); - EXPECT_FALSE(render_text->GetDecoratedWordAtPoint(query, &decorated_word, - &baseline_point)); + EXPECT_FALSE(render_text->GetWordLookupDataAtPoint(query, &decorated_word, + &baseline_point)); render_text->SetText(UTF8ToUTF16("abc")); query = render_text->GetCursorBounds(SelectionModel(0, CURSOR_FORWARD), false) .origin(); - EXPECT_TRUE(render_text->GetDecoratedWordAtPoint(query, &decorated_word, - &baseline_point)); + EXPECT_TRUE(render_text->GetWordLookupDataAtPoint(query, &decorated_word, + &baseline_point)); // False should be returned for obscured text. render_text->SetObscured(true); query = render_text->GetCursorBounds(SelectionModel(0, CURSOR_FORWARD), false) .origin(); - EXPECT_FALSE(render_text->GetDecoratedWordAtPoint(query, &decorated_word, - &baseline_point)); + EXPECT_FALSE(render_text->GetWordLookupDataAtPoint(query, &decorated_word, + &baseline_point)); +} + +// Test that GetLookupDataAtPoint behaves correctly when the range spans lines. +TEST_P(RenderTextHarfBuzzTest, GetLookupDataAtRange_Multiline) { + const base::string16 text = UTF8ToUTF16("a\nb"); + constexpr Range kWordOneRange = Range(0, 1); // Range of character 'a'. + constexpr Range kWordTwoRange = Range(2, 3); // Range of character 'b'. + constexpr Range kTextRange = Range(0, 3); // Range of the entire text. + + // Set up render text. Apply style ranges so that each character index gets + // a corresponding font. + RenderText* render_text = GetRenderText(); + render_text->SetMultiline(true); + render_text->SetDisplayRect(Rect(500, 500)); + render_text->SetText(text); + render_text->ApplyWeight(Font::Weight::SEMIBOLD, kWordOneRange); + render_text->ApplyStyle(UNDERLINE, true, kWordTwoRange); + + // Set up test expectations. + const std::vector<RenderText::FontSpan> font_spans = + render_text->GetFontSpansForTesting(); + + DecoratedText expected_word_1; + expected_word_1.text = UTF8ToUTF16("a"); + expected_word_1.attributes.push_back(CreateRangedAttribute( + font_spans, 0, kWordOneRange.start(), Font::Weight::SEMIBOLD, 0)); + const Rect left_glyph_word_1 = GetSubstringBoundsUnion(kWordOneRange); + + DecoratedText expected_word_2; + expected_word_2.text = UTF8ToUTF16("b"); + expected_word_2.attributes.push_back( + CreateRangedAttribute(font_spans, 0, kWordTwoRange.start(), + Font::Weight::NORMAL, UNDERLINE_MASK)); + const Rect left_glyph_word_2 = GetSubstringBoundsUnion(kWordTwoRange); + + DecoratedText expected_entire_text; + expected_entire_text.text = UTF8ToUTF16("a\nb"); + expected_entire_text.attributes.push_back( + CreateRangedAttribute(font_spans, kWordOneRange.start(), + kWordOneRange.start(), Font::Weight::SEMIBOLD, 0)); + expected_entire_text.attributes.push_back( + CreateRangedAttribute(font_spans, 1, 1, Font::Weight::NORMAL, 0)); + expected_entire_text.attributes.push_back(CreateRangedAttribute( + font_spans, kWordTwoRange.start(), kWordTwoRange.start(), + Font::Weight::NORMAL, UNDERLINE_MASK)); + + DecoratedText decorated_word; + Point baseline_point; + { + // Query for the range of the first word. + EXPECT_TRUE(render_text->GetLookupDataForRange( + kWordOneRange, &decorated_word, &baseline_point)); + VerifyDecoratedWordsAreEqual(expected_word_1, decorated_word); + EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); + } + { + // Query for the range of the second word. + EXPECT_TRUE(render_text->GetLookupDataForRange( + kWordTwoRange, &decorated_word, &baseline_point)); + VerifyDecoratedWordsAreEqual(expected_word_2, decorated_word); + EXPECT_TRUE(left_glyph_word_2.Contains(baseline_point)); + } + { + // Query the entire text range. + EXPECT_TRUE(render_text->GetLookupDataForRange(kTextRange, &decorated_word, + &baseline_point)); + VerifyDecoratedWordsAreEqual(expected_entire_text, decorated_word); + EXPECT_TRUE(left_glyph_word_1.Contains(baseline_point)); + } } // Tests text selection made at end points of individual lines of multiline @@ -5166,6 +5212,24 @@ TEST_P(RenderTextHarfBuzzTest, GlyphSpacing) { EXPECT_EQ(width_without_glyph_spacing + total_spacing, run->width); } +// Ensure font size overrides propagate through to text runs. +TEST_P(RenderTextHarfBuzzTest, FontSizeOverride) { + RenderTextHarfBuzz* render_text = GetRenderTextHarfBuzz(); + const int default_font_size = render_text->font_list().GetFontSize(); + const int test_font_size_override = default_font_size + 5; + render_text->SetText(UTF8ToUTF16("0123456789")); + render_text->ApplyFontSizeOverride(test_font_size_override, gfx::Range(3, 7)); + test_api()->EnsureLayout(); + EXPECT_EQ(ToString16Vec({"012", "3456", "789"}), GetRunListStrings()); + + const internal::TextRunList* run_list = GetHarfBuzzRunList(); + ASSERT_EQ(3U, run_list->size()); + + EXPECT_EQ(default_font_size, run_list->runs()[0].get()->font_size); + EXPECT_EQ(test_font_size_override, run_list->runs()[1].get()->font_size); + EXPECT_EQ(default_font_size, run_list->runs()[2].get()->font_size); +} + // Prefix for test instantiations intentionally left blank since each test // fixture class has a single parameterization. #if defined(OS_MACOSX) diff --git a/chromium/ui/gfx/skia_paint_util.cc b/chromium/ui/gfx/skia_paint_util.cc index df9dcded399..50279d1dbc7 100644 --- a/chromium/ui/gfx/skia_paint_util.cc +++ b/chromium/ui/gfx/skia_paint_util.cc @@ -4,10 +4,9 @@ #include "ui/gfx/skia_paint_util.h" -#include "base/memory/ptr_util.h" #include "cc/paint/paint_image_builder.h" #include "third_party/skia/include/core/SkColorFilter.h" -#include "third_party/skia/include/effects/SkBlurMaskFilter.h" +#include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkLayerDrawLooper.h" #include "ui/gfx/image/image_skia_rep.h" @@ -87,11 +86,10 @@ sk_sp<SkDrawLooper> CreateShadowDrawLooper( SkIntToScalar(shadow.y())); SkPaint* paint = looper_builder.addLayer(layer_info); - // SkBlurMaskFilter's blur radius defines the range to extend the blur from + // Skia's blur radius defines the range to extend the blur from // original mask, which is half of blur amount as defined in ShadowValue. - paint->setMaskFilter(SkBlurMaskFilter::Make( - kNormal_SkBlurStyle, RadiusToSigma(shadow.blur() / 2), - SkBlurMaskFilter::kHighQuality_BlurFlag)); + paint->setMaskFilter(SkMaskFilter::MakeBlur( + kNormal_SkBlurStyle, RadiusToSigma(shadow.blur() / 2))); paint->setColorFilter( SkColorFilter::MakeModeFilter(shadow.color(), SkBlendMode::kSrcIn)); } diff --git a/chromium/ui/gfx/skia_util.cc b/chromium/ui/gfx/skia_util.cc index 2fbae69129c..4379e76c584 100644 --- a/chromium/ui/gfx/skia_util.cc +++ b/chromium/ui/gfx/skia_util.cc @@ -12,7 +12,6 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColorPriv.h" #include "third_party/skia/include/core/SkUnPreMultiply.h" -#include "third_party/skia/include/effects/SkBlurMaskFilter.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkLayerDrawLooper.h" #include "ui/gfx/geometry/quad_f.h" @@ -69,6 +68,10 @@ SkSize SizeFToSkSize(const SizeF& size) { SkFloatToScalar(size.height())); } +SkISize SizeToSkISize(const Size& size) { + return SkISize::Make(size.width(), size.height()); +} + SizeF SkSizeToSizeF(const SkSize& size) { return SizeF(SkScalarToFloat(size.width()), SkScalarToFloat(size.height())); } diff --git a/chromium/ui/gfx/skia_util.h b/chromium/ui/gfx/skia_util.h index 0c1f251f7fc..d0efa6b14d2 100644 --- a/chromium/ui/gfx/skia_util.h +++ b/chromium/ui/gfx/skia_util.h @@ -35,6 +35,7 @@ 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); diff --git a/chromium/ui/gfx/vector_icon_types.h b/chromium/ui/gfx/vector_icon_types.h index fae6871dffb..e03aa12657a 100644 --- a/chromium/ui/gfx/vector_icon_types.h +++ b/chromium/ui/gfx/vector_icon_types.h @@ -63,9 +63,6 @@ enum CommandType { // Parameters are delay (ms), duration (ms), and tween type // (gfx::Tween::Type). TRANSITION_END, - // Marks the end of the list of commands. TODO(estade): remove this sentinel - // value and rely on VectorIcon::path_size. - END }; // A POD that describes either a path command or an argument for it. @@ -79,18 +76,29 @@ struct PathElement { }; }; -struct VectorIcon { - VectorIcon() = default; +// Describes the drawing commands for a single vector icon at a particular pixel +// size or range of sizes. +struct VectorIconRep { + VectorIconRep() = default; - bool is_empty() const { return !path; } + const PathElement* path = nullptr; - const gfx::PathElement* path = nullptr; // The length of |path|. size_t path_size = 0u; - const gfx::PathElement* path_1x = nullptr; - // The length of |path_1x|. - size_t path_1x_size = 0u; + private: + DISALLOW_COPY_AND_ASSIGN(VectorIconRep); +}; + +// A vector icon that stores one or more representations to be used for various +// scale factors and pixel dimensions. +struct VectorIcon { + VectorIcon() = default; + + bool is_empty() const { return !rep; } + + const VectorIconRep* rep = nullptr; + const VectorIconRep* rep_1x = nullptr; // A human-readable name, useful for debugging, derived from the name of the // icon file. This can also be used as an identifier, but vector icon targets diff --git a/chromium/ui/gfx/x/x11.h b/chromium/ui/gfx/x/x11.h index eb53d6dd906..4bce2bc3a34 100644 --- a/chromium/ui/gfx/x/x11.h +++ b/chromium/ui/gfx/x/x11.h @@ -89,6 +89,7 @@ extern "C" { #undef Bool // Defined by X11/Xlib.h to int #undef RootWindow // Defined by X11/Xlib.h #undef DestroyAll // Defined by X11/X.h to 0 +#undef AddToList // Defined by X11/extensions/XI.h to 0 #undef COUNT // Defined by X11/extensions/XI.h to 0 #undef CREATE // Defined by X11/extensions/XI.h to 1 #undef DeviceAdded // Defined by X11/extensions/XI.h to 0 diff --git a/chromium/ui/gl/BUILD.gn b/chromium/ui/gl/BUILD.gn index 238df5099a5..a79f2efbff8 100644 --- a/chromium/ui/gl/BUILD.gn +++ b/chromium/ui/gl/BUILD.gn @@ -11,9 +11,10 @@ import("//ui/ozone/ozone.gni") import("//testing/test.gni") declare_args() { - enable_swiftshader = (is_win || (is_linux && use_x11) || - (is_chromeos && ozone_platform_x11)) && - (target_cpu == "x86" || target_cpu == "x64") + enable_swiftshader = + (is_win || (is_linux && use_x11) || (is_mac && use_egl) || + (is_chromeos && ozone_platform_x11)) && + (target_cpu == "x86" || target_cpu == "x64") } use_glx = use_x11 || ozone_platform_x11 @@ -25,6 +26,7 @@ if (is_android) { buildflag_header("gl_features") { header = "gl_features.h" + use_egl_on_mac = use_egl && is_mac flags = [ "ENABLE_SWIFTSHADER=$enable_swiftshader", "USE_EGL_ON_MAC=$use_egl_on_mac", @@ -196,7 +198,7 @@ component("gl") { "gl_surface_egl.h", ] - if (is_linux) { + if (is_linux || (is_win && use_ozone)) { sources += [ "gl_image_native_pixmap.cc", "gl_image_native_pixmap.h", @@ -214,7 +216,7 @@ component("gl") { } } - if (is_posix) { + if (is_posix || is_fuchsia) { # Windows has USE_EGL but doesn't support base::FileDescriptor sources += [ "gl_fence_android_native_fence_sync.cc", @@ -222,7 +224,15 @@ component("gl") { ] } } - if (is_android || is_linux || is_fuchsia) { + if (is_mac || use_egl) { + sources += [ + "yuv_to_rgb_converter.cc", + "yuv_to_rgb_converter.h", + ] + } + + # TODO(camurcu): Windows/Ozone uses OSMesa for now. Will be updated. + if (is_android || is_linux || is_fuchsia || (is_win && use_ozone)) { sources += [ "gl_implementation_osmesa.cc", "gl_implementation_osmesa.h", @@ -311,8 +321,6 @@ component("gl") { "gl_image_io_surface.mm", "scoped_cgl.cc", "scoped_cgl.h", - "yuv_to_rgb_converter.cc", - "yuv_to_rgb_converter.h", ] libs = [ @@ -331,6 +339,7 @@ component("gl") { data_deps += [ "//third_party/angle:libEGL", "//third_party/angle:libGLESv2", + "//third_party/swiftshader", ] } } @@ -429,7 +438,7 @@ source_set("run_all_unittests") { if (use_ozone) { deps += [ - "//mojo/edk/system", + "//mojo/edk", "//services/service_manager/public/cpp/test:test_support", "//ui/ozone", ] diff --git a/chromium/ui/gl/PRESUBMIT.py b/chromium/ui/gl/PRESUBMIT.py index dad553dd754..290b06c5d06 100644 --- a/chromium/ui/gl/PRESUBMIT.py +++ b/chromium/ui/gl/PRESUBMIT.py @@ -23,9 +23,9 @@ def PostUploadHook(cl, change, output_api): return output_api.EnsureCQIncludeTrybotsAreAdded( cl, [ - 'master.tryserver.chromium.linux:linux_optional_gpu_tests_rel', - 'master.tryserver.chromium.mac:mac_optional_gpu_tests_rel', - 'master.tryserver.chromium.win:win_optional_gpu_tests_rel', - 'master.tryserver.chromium.android:android_optional_gpu_tests_rel', + 'luci.chromium.try:linux_optional_gpu_tests_rel', + 'luci.chromium.try:mac_optional_gpu_tests_rel', + 'luci.chromium.try:win_optional_gpu_tests_rel', + 'luci.chromium.try:android_optional_gpu_tests_rel', ], 'Automatically added optional GPU tests to run on CQ.') diff --git a/chromium/ui/gl/egl_bindings_autogen_mock.cc b/chromium/ui/gl/egl_bindings_autogen_mock.cc index 514c551c468..961eed4d07a 100644 --- a/chromium/ui/gl/egl_bindings_autogen_mock.cc +++ b/chromium/ui/gl/egl_bindings_autogen_mock.cc @@ -120,13 +120,13 @@ MockEGLInterface::Mock_eglCreateStreamKHR(EGLDisplay dpy, } EGLBoolean GL_BINDING_CALL -MockEGLInterface::Mock_eglCreateStreamProducerD3DTextureNV12ANGLE( +MockEGLInterface::Mock_eglCreateStreamProducerD3DTextureANGLE( EGLDisplay dpy, EGLStreamKHR stream, EGLAttrib* attrib_list) { - MakeFunctionUnique("eglCreateStreamProducerD3DTextureNV12ANGLE"); - return interface_->CreateStreamProducerD3DTextureNV12ANGLE(dpy, stream, - attrib_list); + MakeFunctionUnique("eglCreateStreamProducerD3DTextureANGLE"); + return interface_->CreateStreamProducerD3DTextureANGLE(dpy, stream, + attrib_list); } EGLSyncKHR GL_BINDING_CALL @@ -185,6 +185,27 @@ MockEGLInterface::Mock_eglDupNativeFenceFDANDROID(EGLDisplay dpy, } EGLBoolean GL_BINDING_CALL +MockEGLInterface::Mock_eglExportDMABUFImageMESA(EGLDisplay dpy, + EGLImageKHR image, + int* fds, + EGLint* strides, + EGLint* offsets) { + MakeFunctionUnique("eglExportDMABUFImageMESA"); + return interface_->ExportDMABUFImageMESA(dpy, image, fds, strides, offsets); +} + +EGLBoolean GL_BINDING_CALL +MockEGLInterface::Mock_eglExportDMABUFImageQueryMESA(EGLDisplay dpy, + EGLImageKHR image, + int* fourcc, + int* num_planes, + EGLuint64KHR* modifiers) { + MakeFunctionUnique("eglExportDMABUFImageQueryMESA"); + return interface_->ExportDMABUFImageQueryMESA(dpy, image, fourcc, num_planes, + modifiers); +} + +EGLBoolean GL_BINDING_CALL MockEGLInterface::Mock_eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, @@ -501,15 +522,14 @@ MockEGLInterface::Mock_eglStreamConsumerReleaseKHR(EGLDisplay dpy, return interface_->StreamConsumerReleaseKHR(dpy, stream); } -EGLBoolean GL_BINDING_CALL -MockEGLInterface::Mock_eglStreamPostD3DTextureNV12ANGLE( +EGLBoolean GL_BINDING_CALL MockEGLInterface::Mock_eglStreamPostD3DTextureANGLE( EGLDisplay dpy, EGLStreamKHR stream, void* texture, const EGLAttrib* attrib_list) { - MakeFunctionUnique("eglStreamPostD3DTextureNV12ANGLE"); - return interface_->StreamPostD3DTextureNV12ANGLE(dpy, stream, texture, - attrib_list); + MakeFunctionUnique("eglStreamPostD3DTextureANGLE"); + return interface_->StreamPostD3DTextureANGLE(dpy, stream, texture, + attrib_list); } EGLBoolean GL_BINDING_CALL @@ -599,9 +619,9 @@ MockEGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<GLFunctionPointerType>(Mock_eglCreatePixmapSurface); if (strcmp(name, "eglCreateStreamKHR") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_eglCreateStreamKHR); - if (strcmp(name, "eglCreateStreamProducerD3DTextureNV12ANGLE") == 0) + if (strcmp(name, "eglCreateStreamProducerD3DTextureANGLE") == 0) return reinterpret_cast<GLFunctionPointerType>( - Mock_eglCreateStreamProducerD3DTextureNV12ANGLE); + Mock_eglCreateStreamProducerD3DTextureANGLE); if (strcmp(name, "eglCreateSyncKHR") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_eglCreateSyncKHR); if (strcmp(name, "eglCreateWindowSurface") == 0) @@ -619,6 +639,12 @@ MockEGLInterface::GetGLProcAddress(const char* name) { if (strcmp(name, "eglDupNativeFenceFDANDROID") == 0) return reinterpret_cast<GLFunctionPointerType>( Mock_eglDupNativeFenceFDANDROID); + if (strcmp(name, "eglExportDMABUFImageMESA") == 0) + return reinterpret_cast<GLFunctionPointerType>( + Mock_eglExportDMABUFImageMESA); + if (strcmp(name, "eglExportDMABUFImageQueryMESA") == 0) + return reinterpret_cast<GLFunctionPointerType>( + Mock_eglExportDMABUFImageQueryMESA); if (strcmp(name, "eglGetCompositorTimingANDROID") == 0) return reinterpret_cast<GLFunctionPointerType>( Mock_eglGetCompositorTimingANDROID); @@ -715,9 +741,9 @@ MockEGLInterface::GetGLProcAddress(const char* name) { if (strcmp(name, "eglStreamConsumerReleaseKHR") == 0) return reinterpret_cast<GLFunctionPointerType>( Mock_eglStreamConsumerReleaseKHR); - if (strcmp(name, "eglStreamPostD3DTextureNV12ANGLE") == 0) + if (strcmp(name, "eglStreamPostD3DTextureANGLE") == 0) return reinterpret_cast<GLFunctionPointerType>( - Mock_eglStreamPostD3DTextureNV12ANGLE); + Mock_eglStreamPostD3DTextureANGLE); if (strcmp(name, "eglSurfaceAttrib") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_eglSurfaceAttrib); if (strcmp(name, "eglSwapBuffers") == 0) diff --git a/chromium/ui/gl/egl_bindings_autogen_mock.h b/chromium/ui/gl/egl_bindings_autogen_mock.h index 409e8b526e0..5f4e99afc67 100644 --- a/chromium/ui/gl/egl_bindings_autogen_mock.h +++ b/chromium/ui/gl/egl_bindings_autogen_mock.h @@ -55,9 +55,9 @@ Mock_eglCreatePixmapSurface(EGLDisplay dpy, static EGLStreamKHR GL_BINDING_CALL Mock_eglCreateStreamKHR(EGLDisplay dpy, const EGLint* attrib_list); static EGLBoolean GL_BINDING_CALL -Mock_eglCreateStreamProducerD3DTextureNV12ANGLE(EGLDisplay dpy, - EGLStreamKHR stream, - EGLAttrib* attrib_list); +Mock_eglCreateStreamProducerD3DTextureANGLE(EGLDisplay dpy, + EGLStreamKHR stream, + EGLAttrib* attrib_list); static EGLSyncKHR GL_BINDING_CALL Mock_eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list); static EGLSurface GL_BINDING_CALL @@ -78,6 +78,18 @@ static EGLBoolean GL_BINDING_CALL Mock_eglDestroySyncKHR(EGLDisplay dpy, static EGLint GL_BINDING_CALL Mock_eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync); static EGLBoolean GL_BINDING_CALL +Mock_eglExportDMABUFImageMESA(EGLDisplay dpy, + EGLImageKHR image, + int* fds, + EGLint* strides, + EGLint* offsets); +static EGLBoolean GL_BINDING_CALL +Mock_eglExportDMABUFImageQueryMESA(EGLDisplay dpy, + EGLImageKHR image, + int* fourcc, + int* num_planes, + EGLuint64KHR* modifiers); +static EGLBoolean GL_BINDING_CALL Mock_eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, @@ -212,10 +224,10 @@ Mock_eglStreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream); static EGLBoolean GL_BINDING_CALL Mock_eglStreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream); static EGLBoolean GL_BINDING_CALL -Mock_eglStreamPostD3DTextureNV12ANGLE(EGLDisplay dpy, - EGLStreamKHR stream, - void* texture, - const EGLAttrib* attrib_list); +Mock_eglStreamPostD3DTextureANGLE(EGLDisplay dpy, + EGLStreamKHR stream, + void* texture, + const EGLAttrib* attrib_list); static EGLBoolean GL_BINDING_CALL Mock_eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, diff --git a/chromium/ui/gl/features.gni b/chromium/ui/gl/features.gni index 75a55a3a095..a09f70fbe3d 100644 --- a/chromium/ui/gl/features.gni +++ b/chromium/ui/gl/features.gni @@ -7,11 +7,7 @@ declare_args() { # False by default, enabling currently supported only on Android use_static_angle = false - # Whether experimental support for ANGLE on Mac should be enabled. - # False by default since it is experimental - use_egl_on_mac = false + # Should EGL support be compiled. Can be overriden to test during bring up + # of EGL support on other platforms + use_egl = is_win || is_android || is_linux || is_fuchsia } - -# Should EGL support be compiled -use_egl = - is_win || is_android || is_linux || is_fuchsia || (is_mac && use_egl_on_mac) diff --git a/chromium/ui/gl/generate_bindings.py b/chromium/ui/gl/generate_bindings.py index f5c2c9900bd..c20f860cca5 100755 --- a/chromium/ui/gl/generate_bindings.py +++ b/chromium/ui/gl/generate_bindings.py @@ -662,7 +662,7 @@ GL_FUNCTIONS = [ 'arguments': 'GLenum target, GLenum pname, GLsizei bufSize, GLsizei* length, ' 'void** params', }, -{ 'return_type': 'void', +{ 'return_type': 'GLuint', 'versions': [{ 'name': 'glGetDebugMessageLog' }, { 'name': 'glGetDebugMessageLogKHR', 'extensions': ['GL_KHR_debug'] }], @@ -1834,7 +1834,10 @@ OSMESA_FUNCTIONS = [ 'arguments': 'GLint pname, GLint* value', }, { 'return_type': 'OSMESAproc', 'names': ['OSMesaGetProcAddress'], - 'arguments': 'const char* funcName', }, + 'arguments': 'const char* funcName', + 'logging_code': """ + GL_SERVICE_LOG("GL_RESULT: " << reinterpret_cast<void*>(result)); +""", }, { 'return_type': 'GLboolean', 'names': ['OSMesaMakeCurrent'], 'arguments': 'OSMesaContext ctx, void* buffer, GLenum type, GLsizei width, ' @@ -2019,7 +2022,10 @@ EGL_FUNCTIONS = [ 'const EGLint* attrib_list', }, { 'return_type': '__eglMustCastToProperFunctionPointerType', 'names': ['eglGetProcAddress'], - 'arguments': 'const char* procname', }, + 'arguments': 'const char* procname', + 'logging_code': """ + GL_SERVICE_LOG("GL_RESULT: " << reinterpret_cast<void*>(result)); +""", }, { 'return_type': 'EGLBoolean', 'versions': [{ 'name': 'eglGetSyncAttribKHR', 'extensions': [ @@ -2895,35 +2901,46 @@ void DriverEGL::InitializeExtensionBindings() { file.write('\n') file.write('%s Debug%sApi::%sFn(%s) {\n' % (return_type, set_name.upper(), func['known_as'], arguments)) + # Strip pointer types. argument_names = re.sub( r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', arguments) argument_names = re.sub( r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', argument_names) + # Replace certain `Type varname` combinations with TYPE_varname. log_argument_names = re.sub( r'const char\* ([a-zA-Z0-9_]+)', r'CONSTCHAR_\1', arguments) log_argument_names = re.sub( r'(const )?[a-zA-Z0-9_]+\* ([a-zA-Z0-9_]+)', r'CONSTVOID_\2', log_argument_names) log_argument_names = re.sub( - r'(?<!E)GLenum ([a-zA-Z0-9_]+)', r'GLenum_\1', 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_]+)', + r'GLDEBUGPROC_\1', log_argument_names) + log_argument_names = re.sub( + r'(?<!E)GLenum ([a-zA-Z0-9_]+)', r'GLenum_\1', log_argument_names) + # Strip remaining types. + log_argument_names = re.sub( r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', log_argument_names) + # One more round of stripping to remove both type parts in `unsigned long`. log_argument_names = re.sub( r'(const )?[a-zA-Z0-9_]+\** ([a-zA-Z0-9_]+)', r'\2', log_argument_names) + # Convert TYPE_varname log arguments to the corresponding log expression. log_argument_names = re.sub( r'CONSTVOID_([a-zA-Z0-9_]+)', r'static_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'GLenum_([a-zA-Z0-9_]+)', r'GLEnums::GetStringEnum(\1)', + r'GLboolean_([a-zA-Z0-9_]+)', r'GLEnums::GetStringBool(\1)', log_argument_names) log_argument_names = re.sub( - r'GLboolean_([a-zA-Z0-9_]+)', r'GLEnums::GetStringBool(\1)', + r'GLDEBUGPROC_([a-zA-Z0-9_]+)', + r'reinterpret_cast<void*>(\1)', log_argument_names) + log_argument_names = re.sub( + r'GLenum_([a-zA-Z0-9_]+)', r'GLEnums::GetStringEnum(\1)', log_argument_names) log_argument_names = log_argument_names.replace(',', ' << ", " <<') if argument_names == 'void' or argument_names == '': diff --git a/chromium/ui/gl/gl_bindings_api_autogen_gl.h b/chromium/ui/gl/gl_bindings_api_autogen_gl.h index 851bb00a5ae..2aeddbf61a8 100644 --- a/chromium/ui/gl/gl_bindings_api_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_api_autogen_gl.h @@ -436,14 +436,14 @@ void glGetBufferPointervRobustANGLEFn(GLenum target, GLsizei bufSize, GLsizei* length, void** params) override; -void glGetDebugMessageLogFn(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog) override; +GLuint glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) override; GLenum glGetErrorFn(void) override; void glGetFenceivNVFn(GLuint fence, GLenum pname, GLint* params) override; void glGetFloatvFn(GLenum pname, GLfloat* params) override; diff --git a/chromium/ui/gl/gl_bindings_autogen_egl.cc b/chromium/ui/gl/gl_bindings_autogen_egl.cc index 4a1c1ed9af3..53f3fa140f7 100644 --- a/chromium/ui/gl/gl_bindings_autogen_egl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_egl.cc @@ -1766,7 +1766,9 @@ __eglMustCastToProperFunctionPointerType DebugEGLApi::eglGetProcAddressFn( << "(" << procname << ")"); __eglMustCastToProperFunctionPointerType result = egl_api_->eglGetProcAddressFn(procname); - GL_SERVICE_LOG("GL_RESULT: " << result); + + GL_SERVICE_LOG("GL_RESULT: " << reinterpret_cast<void*>(result)); + return result; } diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.cc b/chromium/ui/gl/gl_bindings_autogen_gl.cc index 5897a33b935..193c37c9fc3 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_gl.cc @@ -3146,16 +3146,16 @@ void GLApiBase::glGetBufferPointervRobustANGLEFn(GLenum target, params); } -void GLApiBase::glGetDebugMessageLogFn(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog) { - driver_->fn.glGetDebugMessageLogFn(count, bufSize, sources, types, ids, - severities, lengths, messageLog); +GLuint GLApiBase::glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) { + return driver_->fn.glGetDebugMessageLogFn(count, bufSize, sources, types, ids, + severities, lengths, messageLog); } GLenum GLApiBase::glGetErrorFn(void) { @@ -5917,17 +5917,17 @@ void TraceGLApi::glGetBufferPointervRobustANGLEFn(GLenum target, params); } -void TraceGLApi::glGetDebugMessageLogFn(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog) { +GLuint TraceGLApi::glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glGetDebugMessageLog") - gl_api_->glGetDebugMessageLogFn(count, bufSize, sources, types, ids, - severities, lengths, messageLog); + return gl_api_->glGetDebugMessageLogFn(count, bufSize, sources, types, ids, + severities, lengths, messageLog); } GLenum TraceGLApi::glGetErrorFn(void) { @@ -8646,7 +8646,7 @@ void DebugGLApi::glCullFaceFn(GLenum mode) { void DebugGLApi::glDebugMessageCallbackFn(GLDEBUGPROC callback, const void* userParam) { GL_SERVICE_LOG("glDebugMessageCallback" - << "(" << callback << ", " + << "(" << reinterpret_cast<void*>(callback) << ", " << static_cast<const void*>(userParam) << ")"); gl_api_->glDebugMessageCallbackFn(callback, userParam); } @@ -9310,14 +9310,14 @@ void DebugGLApi::glGetBufferPointervRobustANGLEFn(GLenum target, params); } -void DebugGLApi::glGetDebugMessageLogFn(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog) { +GLuint DebugGLApi::glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) { GL_SERVICE_LOG("glGetDebugMessageLog" << "(" << count << ", " << bufSize << ", " << static_cast<const void*>(sources) << ", " @@ -9326,8 +9326,10 @@ void DebugGLApi::glGetDebugMessageLogFn(GLuint count, << static_cast<const void*>(severities) << ", " << static_cast<const void*>(lengths) << ", " << static_cast<const void*>(messageLog) << ")"); - gl_api_->glGetDebugMessageLogFn(count, bufSize, sources, types, ids, - severities, lengths, messageLog); + GLuint result = gl_api_->glGetDebugMessageLogFn( + count, bufSize, sources, types, ids, severities, lengths, messageLog); + GL_SERVICE_LOG("GL_RESULT: " << result); + return result; } GLenum DebugGLApi::glGetErrorFn(void) { @@ -12879,15 +12881,16 @@ void NoContextGLApi::glGetBufferPointervRobustANGLEFn(GLenum target, NoContextHelper("glGetBufferPointervRobustANGLE"); } -void NoContextGLApi::glGetDebugMessageLogFn(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog) { +GLuint NoContextGLApi::glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) { NoContextHelper("glGetDebugMessageLog"); + return 0U; } GLenum NoContextGLApi::glGetErrorFn(void) { diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.h b/chromium/ui/gl/gl_bindings_autogen_gl.h index 0d5fe329291..353ab3622d7 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_autogen_gl.h @@ -506,14 +506,14 @@ typedef void(GL_BINDING_CALL* glGetBufferPointervRobustANGLEProc)( GLsizei bufSize, GLsizei* length, void** params); -typedef void(GL_BINDING_CALL* glGetDebugMessageLogProc)(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog); +typedef GLuint(GL_BINDING_CALL* glGetDebugMessageLogProc)(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog); typedef GLenum(GL_BINDING_CALL* glGetErrorProc)(void); typedef void(GL_BINDING_CALL* glGetFenceivNVProc)(GLuint fence, GLenum pname, @@ -2434,14 +2434,14 @@ class GL_EXPORT GLApi { GLsizei bufSize, GLsizei* length, void** params) = 0; - virtual void glGetDebugMessageLogFn(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog) = 0; + virtual GLuint glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) = 0; virtual GLenum glGetErrorFn(void) = 0; virtual void glGetFenceivNVFn(GLuint fence, GLenum pname, GLint* params) = 0; virtual void glGetFloatvFn(GLenum pname, GLfloat* params) = 0; diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.cc b/chromium/ui/gl/gl_bindings_autogen_mock.cc index 72f639e92b3..cb8e0edd575 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.cc +++ b/chromium/ui/gl/gl_bindings_autogen_mock.cc @@ -1500,7 +1500,7 @@ MockGLInterface::Mock_glGetBufferPointervRobustANGLE(GLenum target, params); } -void GL_BINDING_CALL +GLuint GL_BINDING_CALL MockGLInterface::Mock_glGetDebugMessageLog(GLuint count, GLsizei bufSize, GLenum* sources, @@ -1510,11 +1510,11 @@ MockGLInterface::Mock_glGetDebugMessageLog(GLuint count, GLsizei* lengths, char* messageLog) { MakeFunctionUnique("glGetDebugMessageLog"); - interface_->GetDebugMessageLog(count, bufSize, sources, types, ids, - severities, lengths, messageLog); + return interface_->GetDebugMessageLog(count, bufSize, sources, types, ids, + severities, lengths, messageLog); } -void GL_BINDING_CALL +GLuint GL_BINDING_CALL MockGLInterface::Mock_glGetDebugMessageLogKHR(GLuint count, GLsizei bufSize, GLenum* sources, @@ -1524,8 +1524,8 @@ MockGLInterface::Mock_glGetDebugMessageLogKHR(GLuint count, GLsizei* lengths, char* messageLog) { MakeFunctionUnique("glGetDebugMessageLogKHR"); - interface_->GetDebugMessageLog(count, bufSize, sources, types, ids, - severities, lengths, messageLog); + return interface_->GetDebugMessageLog(count, bufSize, sources, types, ids, + severities, lengths, messageLog); } GLenum GL_BINDING_CALL MockGLInterface::Mock_glGetError(void) { diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.h b/chromium/ui/gl/gl_bindings_autogen_mock.h index 77e4bf54dd2..edfa3003e8f 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.h +++ b/chromium/ui/gl/gl_bindings_autogen_mock.h @@ -617,22 +617,22 @@ static void GL_BINDING_CALL Mock_glGetBufferPointervRobustANGLE(GLenum target, GLsizei bufSize, GLsizei* length, void** params); -static void GL_BINDING_CALL Mock_glGetDebugMessageLog(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog); -static void GL_BINDING_CALL Mock_glGetDebugMessageLogKHR(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog); +static GLuint GL_BINDING_CALL Mock_glGetDebugMessageLog(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog); +static GLuint GL_BINDING_CALL Mock_glGetDebugMessageLogKHR(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog); static GLenum GL_BINDING_CALL Mock_glGetError(void); static void GL_BINDING_CALL Mock_glGetFenceivNV(GLuint fence, GLenum pname, diff --git a/chromium/ui/gl/gl_bindings_autogen_osmesa.cc b/chromium/ui/gl/gl_bindings_autogen_osmesa.cc index 75bd1e9bc87..a72fbf41b62 100644 --- a/chromium/ui/gl/gl_bindings_autogen_osmesa.cc +++ b/chromium/ui/gl/gl_bindings_autogen_osmesa.cc @@ -289,7 +289,9 @@ OSMESAproc DebugOSMESAApi::OSMesaGetProcAddressFn(const char* funcName) { GL_SERVICE_LOG("OSMesaGetProcAddress" << "(" << funcName << ")"); OSMESAproc result = osmesa_api_->OSMesaGetProcAddressFn(funcName); - GL_SERVICE_LOG("GL_RESULT: " << result); + + GL_SERVICE_LOG("GL_RESULT: " << reinterpret_cast<void*>(result)); + return result; } diff --git a/chromium/ui/gl/gl_context_cgl.cc b/chromium/ui/gl/gl_context_cgl.cc index 6b85039c6dd..d0a1d8fe2d0 100644 --- a/chromium/ui/gl/gl_context_cgl.cc +++ b/chromium/ui/gl/gl_context_cgl.cc @@ -156,7 +156,8 @@ void GLContextCGL::Destroy() { SetCurrentGL(GetCurrentGL()); } - ScopedCGLSetCurrentContext(static_cast<CGLContextObj>(context_)); + ScopedCGLSetCurrentContext scoped_set_current( + static_cast<CGLContextObj>(context_)); yuv_to_rgb_converters_.clear(); // Rebind the current context's API if needed. @@ -228,8 +229,8 @@ YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter( std::unique_ptr<YUVToRGBConverter>& yuv_to_rgb_converter = yuv_to_rgb_converters_[color_space]; if (!yuv_to_rgb_converter) - yuv_to_rgb_converter.reset( - new YUVToRGBConverter(*GetVersionInfo(), color_space)); + yuv_to_rgb_converter = + std::make_unique<YUVToRGBConverter>(*GetVersionInfo(), color_space); return yuv_to_rgb_converter.get(); } diff --git a/chromium/ui/gl/gl_context_egl.cc b/chromium/ui/gl/gl_context_egl.cc index 8e7043a0325..ae5ebe72ce0 100644 --- a/chromium/ui/gl/gl_context_egl.cc +++ b/chromium/ui/gl/gl_context_egl.cc @@ -21,8 +21,9 @@ #include "third_party/khronos/EGL/eglext.h" #include "ui/gl/egl_util.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_surface_egl.h" - +#include "ui/gl/yuv_to_rgb_converter.h" #ifndef EGL_CHROMIUM_create_context_bind_generates_resource #define EGL_CHROMIUM_create_context_bind_generates_resource 1 @@ -62,8 +63,8 @@ namespace gl { GLContextEGL::GLContextEGL(GLShareGroup* share_group) : GLContextReal(share_group), - context_(nullptr), - display_(nullptr), + context_(EGL_NO_CONTEXT), + display_(EGL_NO_DISPLAY), config_(nullptr), unbind_fbo_on_makecurrent_(false) {} @@ -204,6 +205,7 @@ bool GLContextEGL::Initialize(GLSurface* compatible_surface, } void GLContextEGL::Destroy() { + ReleaseYUVToRGBConverters(); if (context_) { if (!eglDestroyContext(display_, context_)) { LOG(ERROR) << "eglDestroyContext failed with error " @@ -214,6 +216,66 @@ void GLContextEGL::Destroy() { } } +YUVToRGBConverter* GLContextEGL::GetYUVToRGBConverter( + const gfx::ColorSpace& color_space) { + // Make sure YUVToRGBConverter objects never get created when surfaceless EGL + // contexts aren't supported since support for surfaceless EGL contexts is + // required in order to properly release YUVToRGBConverter objects (see + // GLContextEGL::ReleaseYUVToRGBConverters()) + if (!GLSurfaceEGL::IsEGLSurfacelessContextSupported()) { + return nullptr; + } + + std::unique_ptr<YUVToRGBConverter>& yuv_to_rgb_converter = + yuv_to_rgb_converters_[color_space]; + if (!yuv_to_rgb_converter) { + yuv_to_rgb_converter = + std::make_unique<YUVToRGBConverter>(*GetVersionInfo(), color_space); + } + return yuv_to_rgb_converter.get(); +} + +void GLContextEGL::ReleaseYUVToRGBConverters() { + if (!yuv_to_rgb_converters_.empty()) { + // If this context is not current, bind this context's API so that the YUV + // converter can safely destruct + GLContext* current_context = GetRealCurrent(); + if (current_context != this) { + SetCurrentGL(GetCurrentGL()); + } + + EGLContext current_egl_context = eglGetCurrentContext(); + EGLSurface current_draw_surface = EGL_NO_SURFACE; + EGLSurface current_read_surface = EGL_NO_SURFACE; + if (context_ != current_egl_context) { + current_draw_surface = eglGetCurrentSurface(EGL_DRAW); + current_read_surface = eglGetCurrentSurface(EGL_READ); + // This call relies on the fact that yuv_to_rgb_converters_ are only ever + // allocated in GLImageIOSurfaceEGL::CopyTexImage, which is only on + // MacOS, where surfaceless EGL contexts are always supported. + if (!eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, context_)) { + DVLOG(1) << "eglMakeCurrent failed with error " + << GetLastEGLErrorString(); + } + } + + yuv_to_rgb_converters_.clear(); + + // Rebind the current context's API if needed. + if (current_context && current_context != this) { + SetCurrentGL(current_context->GetCurrentGL()); + } + + if (context_ != current_egl_context) { + if (!eglMakeCurrent(display_, current_draw_surface, current_read_surface, + current_egl_context)) { + DVLOG(1) << "eglMakeCurrent failed with error " + << GetLastEGLErrorString(); + } + } + } +} + bool GLContextEGL::MakeCurrent(GLSurface* surface) { DCHECK(context_); if (IsCurrent(surface)) diff --git a/chromium/ui/gl/gl_context_egl.h b/chromium/ui/gl/gl_context_egl.h index 75b2f3208c1..36a01fff7b9 100644 --- a/chromium/ui/gl/gl_context_egl.h +++ b/chromium/ui/gl/gl_context_egl.h @@ -5,6 +5,7 @@ #ifndef UI_GL_GL_CONTEXT_EGL_H_ #define UI_GL_GL_CONTEXT_EGL_H_ +#include <map> #include <string> #include "base/compiler_specific.h" @@ -35,17 +36,22 @@ class GL_EXPORT GLContextEGL : public GLContextReal { void OnSetSwapInterval(int interval) override; bool WasAllocatedUsingRobustnessExtension() override; void SetUnbindFboOnMakeCurrent() override; + YUVToRGBConverter* GetYUVToRGBConverter( + const gfx::ColorSpace& color_space) override; protected: ~GLContextEGL() override; private: void Destroy(); + void ReleaseYUVToRGBConverters(); EGLContext context_; EGLDisplay display_; EGLConfig config_; bool unbind_fbo_on_makecurrent_; + std::map<gfx::ColorSpace, std::unique_ptr<YUVToRGBConverter>> + yuv_to_rgb_converters_; DISALLOW_COPY_AND_ASSIGN(GLContextEGL); }; diff --git a/chromium/ui/gl/gl_context_glx_unittest.cc b/chromium/ui/gl/gl_context_glx_unittest.cc index 07df17483f1..c393a792c5f 100644 --- a/chromium/ui/gl/gl_context_glx_unittest.cc +++ b/chromium/ui/gl/gl_context_glx_unittest.cc @@ -15,7 +15,16 @@ namespace gl { -TEST(GLContextGLXTest, DoNotDestroyOnFailedMakeCurrent) { +#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ + defined(THREAD_SANITIZER) +// https://crbug.com/830653 +#define MAYBE_DoNotDestroyOnFailedMakeCurrent \ + DISABLED_DoNotDestroyOnFailedMakeCurrent +#else +#define MAYBE_DoNotDestroyOnFailedMakeCurrent DoNotDestroyOnFailedMakeCurrent +#endif + +TEST(GLContextGLXTest, MAYBE_DoNotDestroyOnFailedMakeCurrent) { auto* xdisplay = gfx::GetXDisplay(); ASSERT_TRUE(xdisplay); diff --git a/chromium/ui/gl/gl_fence.h b/chromium/ui/gl/gl_fence.h index a3d05e60f26..81d80ca9139 100644 --- a/chromium/ui/gl/gl_fence.h +++ b/chromium/ui/gl/gl_fence.h @@ -5,8 +5,9 @@ #ifndef UI_GL_GL_FENCE_H_ #define UI_GL_GL_FENCE_H_ +#include <memory> + #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "ui/gfx/gpu_fence.h" #include "ui/gl/gl_export.h" diff --git a/chromium/ui/gl/gl_fence_android_native_fence_sync.cc b/chromium/ui/gl/gl_fence_android_native_fence_sync.cc index ae1b0aa33c2..75ea725ca58 100644 --- a/chromium/ui/gl/gl_fence_android_native_fence_sync.cc +++ b/chromium/ui/gl/gl_fence_android_native_fence_sync.cc @@ -4,6 +4,7 @@ #include "ui/gl/gl_fence_android_native_fence_sync.h" +#include "base/memory/ptr_util.h" #include "ui/gfx/gpu_fence_handle.h" #include "ui/gl/gl_surface_egl.h" diff --git a/chromium/ui/gl/gl_fence_egl.cc b/chromium/ui/gl/gl_fence_egl.cc index 536346f5395..5973b6e9482 100644 --- a/chromium/ui/gl/gl_fence_egl.cc +++ b/chromium/ui/gl/gl_fence_egl.cc @@ -4,6 +4,7 @@ #include "ui/gl/gl_fence_egl.h" +#include "base/memory/ptr_util.h" #include "ui/gl/egl_util.h" #include "ui/gl/gl_bindings.h" diff --git a/chromium/ui/gl/gl_gl_api_implementation.cc b/chromium/ui/gl/gl_gl_api_implementation.cc index 250bb1c2909..2313b1f238e 100644 --- a/chromium/ui/gl/gl_gl_api_implementation.cc +++ b/chromium/ui/gl/gl_gl_api_implementation.cc @@ -6,7 +6,6 @@ #include <vector> -#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" diff --git a/chromium/ui/gl/gl_image.h b/chromium/ui/gl/gl_image.h index 1b92e741ff0..c23670f2ab1 100644 --- a/chromium/ui/gl/gl_image.h +++ b/chromium/ui/gl/gl_image.h @@ -71,7 +71,8 @@ class GL_EXPORT GLImage : public base::RefCounted<GLImage> { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) = 0; + const gfx::RectF& crop_rect, + bool enable_blend) = 0; // Set the color space when image is used as an overlay. virtual void SetColorSpace(const gfx::ColorSpace& color_space) = 0; diff --git a/chromium/ui/gl/gl_image_ahardwarebuffer.cc b/chromium/ui/gl/gl_image_ahardwarebuffer.cc index 3121a95c5a0..f9dd6d814f3 100644 --- a/chromium/ui/gl/gl_image_ahardwarebuffer.cc +++ b/chromium/ui/gl/gl_image_ahardwarebuffer.cc @@ -41,7 +41,8 @@ bool GLImageAHardwareBuffer::ScheduleOverlayPlane( int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return false; } diff --git a/chromium/ui/gl/gl_image_ahardwarebuffer.h b/chromium/ui/gl/gl_image_ahardwarebuffer.h index b3ebd04f35a..6674f1a9a65 100644 --- a/chromium/ui/gl/gl_image_ahardwarebuffer.h +++ b/chromium/ui/gl/gl_image_ahardwarebuffer.h @@ -6,6 +6,7 @@ #define UI_GL_GL_IMAGE_AHARDWAREBUFFER_H_ #include "base/macros.h" +#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_export.h" #include "ui/gl/gl_image_egl.h" @@ -28,7 +29,8 @@ class GL_EXPORT GLImageAHardwareBuffer : public GLImageEGL { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override {} void Flush() override; void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, diff --git a/chromium/ui/gl/gl_image_dxgi.cc b/chromium/ui/gl/gl_image_dxgi.cc index 9891680bb15..612dde30783 100644 --- a/chromium/ui/gl/gl_image_dxgi.cc +++ b/chromium/ui/gl/gl_image_dxgi.cc @@ -6,6 +6,7 @@ #include <d3d11_1.h> +#include "base/debug/alias.h" #include "third_party/khronos/EGL/egl.h" #include "third_party/khronos/EGL/eglext.h" #include "ui/gl/gl_angle_util_win.h" @@ -174,7 +175,8 @@ bool GLImageDXGIBase::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return false; } @@ -235,6 +237,12 @@ bool CopyingGLImageDXGI::Initialize() { HRESULT hr = d3d11_device_->CreateTexture2D( &desc, nullptr, decoder_copy_texture_.GetAddressOf()); + // TODO(sunnyps): Remove after fixing https://crbug.com/794735 + base::debug::Alias(&hr); + HRESULT reason_hr = S_OK; + base::debug::Alias(&reason_hr); + if (hr == DXGI_ERROR_DEVICE_REMOVED) + reason_hr = d3d11_device_->GetDeviceRemovedReason(); CHECK(SUCCEEDED(hr)); EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay(); diff --git a/chromium/ui/gl/gl_image_dxgi.h b/chromium/ui/gl/gl_image_dxgi.h index 05157bdbaaa..74bdf59a32a 100644 --- a/chromium/ui/gl/gl_image_dxgi.h +++ b/chromium/ui/gl/gl_image_dxgi.h @@ -42,7 +42,8 @@ class GL_EXPORT GLImageDXGIBase : public GLImage { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override; void Flush() override; void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, diff --git a/chromium/ui/gl/gl_image_egl.h b/chromium/ui/gl/gl_image_egl.h index b32e9489ef7..e014dc7742b 100644 --- a/chromium/ui/gl/gl_image_egl.h +++ b/chromium/ui/gl/gl_image_egl.h @@ -5,9 +5,10 @@ #ifndef UI_GL_GL_IMAGE_EGL_H_ #define UI_GL_GL_IMAGE_EGL_H_ +#include <EGL/eglplatform.h> + #include "base/macros.h" #include "base/threading/thread_checker.h" -#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_export.h" #include "ui/gl/gl_image.h" @@ -35,12 +36,12 @@ class GL_EXPORT GLImageEGL : public GLImage { // it is required to pass EGL_NO_CONTEXT. This allows to create an EGLImage // from an external resource. Then this EGLImage can be converted to a GL // texture. - bool Initialize(EGLContext context, - EGLenum target, - EGLClientBuffer buffer, + bool Initialize(void* context /* EGLContext */, + unsigned target /* EGLenum */, + void* buffer /* EGLClientBuffer */, const EGLint* attrs); - EGLImageKHR egl_image_; + void* egl_image_ /* EGLImageKHR */; const gfx::Size size_; base::ThreadChecker thread_checker_; diff --git a/chromium/ui/gl/gl_image_glx.cc b/chromium/ui/gl/gl_image_glx.cc index 4c4ba1a294a..176e86af488 100644 --- a/chromium/ui/gl/gl_image_glx.cc +++ b/chromium/ui/gl/gl_image_glx.cc @@ -13,16 +13,6 @@ namespace gl { namespace { -bool ValidFormat(unsigned internalformat) { - switch (internalformat) { - case GL_RGB: - case GL_RGBA: - return true; - default: - return false; - } -} - int TextureFormat(unsigned internalformat) { switch (internalformat) { case GL_RGB: @@ -189,7 +179,8 @@ bool GLImageGLX::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return false; } @@ -199,4 +190,14 @@ void GLImageGLX::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, // TODO(ericrk): Implement GLImage OnMemoryDump. crbug.com/514914 } +bool GLImageGLX::ValidFormat(unsigned internalformat) { + switch (internalformat) { + case GL_RGB: + case GL_RGBA: + return true; + default: + return false; + } +} + } // namespace gl diff --git a/chromium/ui/gl/gl_image_glx.h b/chromium/ui/gl/gl_image_glx.h index abff58fbb5e..9bd917f6a2b 100644 --- a/chromium/ui/gl/gl_image_glx.h +++ b/chromium/ui/gl/gl_image_glx.h @@ -34,7 +34,8 @@ class GL_EXPORT GLImageGLX : public GLImage { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override {} void Flush() override {} void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, @@ -45,6 +46,8 @@ class GL_EXPORT GLImageGLX : public GLImage { ~GLImageGLX() override; private: + static bool ValidFormat(unsigned internalformat); + XID glx_pixmap_; const gfx::Size size_; unsigned internalformat_; diff --git a/chromium/ui/gl/gl_image_io_surface.h b/chromium/ui/gl/gl_image_io_surface.h index fd9741d9f08..1a5959418dc 100644 --- a/chromium/ui/gl/gl_image_io_surface.h +++ b/chromium/ui/gl/gl_image_io_surface.h @@ -58,7 +58,8 @@ class GL_EXPORT GLImageIOSurface : public GLImage { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override; void Flush() override {} void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, @@ -89,6 +90,7 @@ class GL_EXPORT GLImageIOSurface : public GLImage { ~GLImageIOSurface() override; virtual bool BindTexImageImpl(unsigned internalformat); + static bool ValidFormat(gfx::BufferFormat format); Type GetType() const override; class RGBConverter; diff --git a/chromium/ui/gl/gl_image_io_surface.mm b/chromium/ui/gl/gl_image_io_surface.mm index 12bb7790c11..8ae7b77c202 100644 --- a/chromium/ui/gl/gl_image_io_surface.mm +++ b/chromium/ui/gl/gl_image_io_surface.mm @@ -55,36 +55,6 @@ bool ValidInternalFormat(unsigned internalformat) { } } -bool ValidFormat(gfx::BufferFormat format) { - switch (format) { - case gfx::BufferFormat::R_8: - case gfx::BufferFormat::BGRA_8888: - case gfx::BufferFormat::BGRX_8888: - case gfx::BufferFormat::RGBA_8888: - case gfx::BufferFormat::RGBA_F16: - case gfx::BufferFormat::BGRX_1010102: - case gfx::BufferFormat::UYVY_422: - case gfx::BufferFormat::YUV_420_BIPLANAR: - 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: - case gfx::BufferFormat::RGBX_1010102: - case gfx::BufferFormat::YVU_420: - return false; - } - - NOTREACHED(); - return false; -} - GLenum TextureFormat(gfx::BufferFormat format) { switch (format) { case gfx::BufferFormat::R_8: @@ -212,7 +182,8 @@ GLenum ConvertRequestedInternalFormat(GLenum internalformat) { GLImageIOSurface* GLImageIOSurface::Create(const gfx::Size& size, unsigned internalformat) { #if BUILDFLAG(USE_EGL_ON_MAC) - if (GLContext::GetCurrent()->GetVersionInfo()->is_angle) { + if (GLContext::GetCurrent()->GetVersionInfo()->is_angle || + GLContext::GetCurrent()->GetVersionInfo()->is_swiftshader) { return new GLImageIOSurfaceEGL(size, internalformat); } #endif // BUILDFLAG(USE_EGL_ON_MAC) @@ -413,7 +384,8 @@ bool GLImageIOSurface::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { NOTREACHED(); return false; } @@ -493,4 +465,35 @@ GLImageIOSurface* GLImageIOSurface::FromGLImage(GLImage* image) { return static_cast<GLImageIOSurface*>(image); } +// static +bool GLImageIOSurface::ValidFormat(gfx::BufferFormat format) { + switch (format) { + case gfx::BufferFormat::R_8: + case gfx::BufferFormat::BGRA_8888: + case gfx::BufferFormat::BGRX_8888: + case gfx::BufferFormat::RGBA_8888: + case gfx::BufferFormat::RGBA_F16: + case gfx::BufferFormat::BGRX_1010102: + case gfx::BufferFormat::UYVY_422: + case gfx::BufferFormat::YUV_420_BIPLANAR: + 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: + case gfx::BufferFormat::RGBX_1010102: + case gfx::BufferFormat::YVU_420: + return false; + } + + NOTREACHED(); + return false; +} + } // namespace gl diff --git a/chromium/ui/gl/gl_image_io_surface_egl.h b/chromium/ui/gl/gl_image_io_surface_egl.h index 17d96825ec4..aeeee89c590 100644 --- a/chromium/ui/gl/gl_image_io_surface_egl.h +++ b/chromium/ui/gl/gl_image_io_surface_egl.h @@ -22,6 +22,7 @@ class GL_EXPORT GLImageIOSurfaceEGL : public GLImageIOSurface { protected: ~GLImageIOSurfaceEGL() override; bool BindTexImageImpl(unsigned internalformat) override; + bool CopyTexImage(unsigned target) override; private: EGLDisplay display_; diff --git a/chromium/ui/gl/gl_image_io_surface_egl.mm b/chromium/ui/gl/gl_image_io_surface_egl.mm index 787870d540a..e425d5a315a 100644 --- a/chromium/ui/gl/gl_image_io_surface_egl.mm +++ b/chromium/ui/gl/gl_image_io_surface_egl.mm @@ -4,7 +4,12 @@ #include "ui/gl/gl_image_io_surface_egl.h" +#include "base/callback_helpers.h" +#include "base/mac/bind_objc_block.h" +#include "ui/gl/gl_context.h" #include "ui/gl/gl_surface_egl.h" +#include "ui/gl/scoped_binders.h" +#include "ui/gl/yuv_to_rgb_converter.h" // Enums for the EGL_ANGLE_iosurface_client_buffer extension #define EGL_IOSURFACE_ANGLE 0x3454 @@ -53,6 +58,7 @@ InternalFormatType BufferFormatToInternalFormatType(gfx::BufferFormat format) { case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: + case gfx::BufferFormat::RGBX_1010102: case gfx::BufferFormat::YVU_420: NOTREACHED(); return {GL_NONE, GL_NONE}; @@ -127,7 +133,7 @@ bool GLImageIOSurfaceEGL::BindTexImageImpl(unsigned internalformat) { EGL_TEXTURE_TYPE_ANGLE, formatType.type, EGL_NONE, EGL_NONE, }; - // clang-format off + // clang-format on pbuffer_ = eglCreatePbufferFromClientBuffer(display_, EGL_IOSURFACE_ANGLE, io_surface_.get(), dummy_config_, attribs); @@ -151,4 +157,138 @@ bool GLImageIOSurfaceEGL::BindTexImageImpl(unsigned internalformat) { return true; } +bool GLImageIOSurfaceEGL::CopyTexImage(unsigned target) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (format_ != gfx::BufferFormat::YUV_420_BIPLANAR) + return false; + + GLContext* gl_context = GLContext::GetCurrent(); + DCHECK(gl_context); + + YUVToRGBConverter* yuv_to_rgb_converter = + gl_context->GetYUVToRGBConverter(color_space_for_yuv_to_rgb_); + DCHECK(yuv_to_rgb_converter); + + // Note that state restoration is done explicitly instead of scoped binders to + // avoid https://crbug.com/601729. + GLint rgb_texture = 0; + GLenum target_getter = 0; + switch (target) { + case GL_TEXTURE_2D: + target_getter = GL_TEXTURE_BINDING_2D; + break; + case GL_TEXTURE_CUBE_MAP: + target_getter = GL_TEXTURE_BINDING_CUBE_MAP; + break; + case GL_TEXTURE_EXTERNAL_OES: + target_getter = GL_TEXTURE_BINDING_EXTERNAL_OES; + break; + case GL_TEXTURE_RECTANGLE_ARB: + target_getter = GL_TEXTURE_BINDING_RECTANGLE_ARB; + break; + default: + NOTIMPLEMENTED() << " Target not supported."; + return false; + } + + EGLSurface y_surface = EGL_NO_SURFACE; + EGLSurface uv_surface = EGL_NO_SURFACE; + + glGetIntegerv(target_getter, &rgb_texture); + base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ + if (y_surface != EGL_NO_SURFACE) { + EGLBoolean result = + eglReleaseTexImage(display_, y_surface, EGL_BACK_BUFFER); + DCHECK(result == EGL_TRUE); + result = eglDestroySurface(display_, y_surface); + DCHECK(result == EGL_TRUE); + } + if (uv_surface != EGL_NO_SURFACE) { + EGLBoolean result = + eglReleaseTexImage(display_, uv_surface, EGL_BACK_BUFFER); + DCHECK(result == EGL_TRUE); + result = eglDestroySurface(display_, uv_surface); + DCHECK(result == EGL_TRUE); + } + glBindTexture(target, rgb_texture); + })); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, yuv_to_rgb_converter->y_texture()); + if (glGetError() != GL_NO_ERROR) { + LOG(ERROR) << "Can't bind Y texture"; + return false; + } + + // clang-format off + const EGLint yAttribs[] = { + EGL_WIDTH, size_.width(), + EGL_HEIGHT, size_.height(), + EGL_IOSURFACE_PLANE_ANGLE, 0, + EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, + EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RED, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, + EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, + EGL_NONE, EGL_NONE, + }; + // clang-format on + + y_surface = eglCreatePbufferFromClientBuffer(display_, EGL_IOSURFACE_ANGLE, + io_surface_.get(), dummy_config_, + yAttribs); + if (y_surface == EGL_NO_SURFACE) { + LOG(ERROR) << "eglCreatePbufferFromClientBuffer failed, EGL error is " + << eglGetError(); + return false; + } + + EGLBoolean result = eglBindTexImage(display_, y_surface, EGL_BACK_BUFFER); + if (result != EGL_TRUE) { + LOG(ERROR) << "eglBindTexImage failed, EGL error is " << eglGetError(); + return false; + } + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, yuv_to_rgb_converter->uv_texture()); + if (glGetError() != GL_NO_ERROR) { + LOG(ERROR) << "Can't bind UV texture"; + return false; + } + + // clang-format off + const EGLint uvAttribs[] = { + EGL_WIDTH, size_.width() / 2, + EGL_HEIGHT, size_.height() / 2, + EGL_IOSURFACE_PLANE_ANGLE, 1, + EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE, + EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RG, + EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA, + EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE, + EGL_NONE, EGL_NONE, + }; + // clang-format on + + uv_surface = eglCreatePbufferFromClientBuffer(display_, EGL_IOSURFACE_ANGLE, + io_surface_.get(), + dummy_config_, uvAttribs); + if (uv_surface == EGL_NO_SURFACE) { + LOG(ERROR) << "eglCreatePbufferFromClientBuffer failed, EGL error is " + << eglGetError(); + return false; + } + + result = eglBindTexImage(display_, uv_surface, EGL_BACK_BUFFER); + if (result != EGL_TRUE) { + LOG(ERROR) << "eglBindTexImage failed, EGL error is " << eglGetError(); + return false; + } + + yuv_to_rgb_converter->CopyYUV420ToRGB(target, size_, rgb_texture); + if (glGetError() != GL_NO_ERROR) { + LOG(ERROR) << "Failed converting from YUV to RGB"; + return false; + } + + return true; +} + } // namespace gl diff --git a/chromium/ui/gl/gl_image_memory.cc b/chromium/ui/gl/gl_image_memory.cc index 1f5b76838bd..167ed71891f 100644 --- a/chromium/ui/gl/gl_image_memory.cc +++ b/chromium/ui/gl/gl_image_memory.cc @@ -39,36 +39,6 @@ bool ValidInternalFormat(unsigned internalformat) { } } -bool 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: - 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 true; - case gfx::BufferFormat::YVU_420: - case gfx::BufferFormat::YUV_420_BIPLANAR: - case gfx::BufferFormat::UYVY_422: - return false; - } - - NOTREACHED(); - return false; -} - bool IsCompressedFormat(gfx::BufferFormat format) { switch (format) { case gfx::BufferFormat::ATC: @@ -532,7 +502,8 @@ bool GLImageMemory::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return false; } @@ -546,4 +517,35 @@ unsigned GLImageMemory::GetInternalFormatForTesting(gfx::BufferFormat format) { return TextureFormat(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: + 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 true; + case gfx::BufferFormat::YVU_420: + case gfx::BufferFormat::YUV_420_BIPLANAR: + case gfx::BufferFormat::UYVY_422: + return false; + } + + NOTREACHED(); + return false; +} + } // namespace gl diff --git a/chromium/ui/gl/gl_image_memory.h b/chromium/ui/gl/gl_image_memory.h index 75a3aef0995..349bbdf3776 100644 --- a/chromium/ui/gl/gl_image_memory.h +++ b/chromium/ui/gl/gl_image_memory.h @@ -40,7 +40,8 @@ class GL_EXPORT GLImageMemory : public GLImage { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override {} void Flush() override {} Type GetType() const override; @@ -55,6 +56,8 @@ class GL_EXPORT GLImageMemory : public GLImage { ~GLImageMemory() override; private: + static bool ValidFormat(gfx::BufferFormat format); + const gfx::Size size_; const unsigned internalformat_; const unsigned char* memory_; diff --git a/chromium/ui/gl/gl_image_native_pixmap.cc b/chromium/ui/gl/gl_image_native_pixmap.cc index a917940b89b..26e066f2bbb 100644 --- a/chromium/ui/gl/gl_image_native_pixmap.cc +++ b/chromium/ui/gl/gl_image_native_pixmap.cc @@ -9,6 +9,7 @@ #include "ui/gfx/buffer_format_util.h" #include "ui/gl/egl_util.h" #include "ui/gl/gl_context.h" +#include "ui/gl/gl_enums.h" #include "ui/gl/gl_surface_egl.h" #define FOURCC(a, b, c, d) \ @@ -59,36 +60,6 @@ bool ValidInternalFormat(unsigned internalformat, gfx::BufferFormat format) { } } -bool ValidFormat(gfx::BufferFormat format) { - switch (format) { - case gfx::BufferFormat::R_8: - case gfx::BufferFormat::R_16: - case gfx::BufferFormat::RG_88: - case gfx::BufferFormat::BGR_565: - case gfx::BufferFormat::RGBA_8888: - case gfx::BufferFormat::RGBX_8888: - case gfx::BufferFormat::BGRA_8888: - case gfx::BufferFormat::BGRX_8888: - case gfx::BufferFormat::BGRX_1010102: - case gfx::BufferFormat::RGBX_1010102: - 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::RGBA_F16: - case gfx::BufferFormat::UYVY_422: - return false; - } - - NOTREACHED(); - return false; -} - EGLint FourCC(gfx::BufferFormat format) { switch (format) { case gfx::BufferFormat::R_8: @@ -190,7 +161,8 @@ bool GLImageNativePixmap::Initialize(gfx::NativePixmap* pixmap, } if (!ValidInternalFormat(internalformat_, format)) { - LOG(ERROR) << "Invalid internalformat: " << internalformat_ + LOG(ERROR) << "Invalid internalformat: " + << GLEnums::GetStringEnum(internalformat_) << " for format: " << gfx::BufferFormatToString(format); return false; } @@ -299,7 +271,7 @@ gfx::NativePixmapHandle GLImageNativePixmap::ExportHandle() { if (num_planes > 0 && static_cast<size_t>(num_planes) != gfx::NumberOfPlanesForBufferFormat(format)) { LOG(ERROR) << "Invalid number of planes: " << num_planes - << " for format: " << static_cast<int>(format); + << " for format: " << gfx::BufferFormatToString(format); return gfx::NativePixmapHandle(); } @@ -308,8 +280,9 @@ gfx::NativePixmapHandle GLImageNativePixmap::ExportHandle() { // This can happen if RGBX is implemented using RGBA. Otherwise there is // a real mistake from the user and we have to fail. if (internalformat_ == GL_RGB && format != gfx::BufferFormat::RGBA_8888) { - LOG(ERROR) << "Invalid internalformat: 0x" << std::hex << internalformat_ - << " for format: " << static_cast<int>(format); + LOG(ERROR) << "Invalid internalformat: " + << GLEnums::GetStringEnum(internalformat_) + << " for format: " << gfx::BufferFormatToString(format); return gfx::NativePixmapHandle(); } } @@ -377,10 +350,11 @@ bool GLImageNativePixmap::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { DCHECK(pixmap_); return pixmap_->ScheduleOverlayPlane(widget, z_order, transform, bounds_rect, - crop_rect); + crop_rect, enable_blend); } void GLImageNativePixmap::Flush() { @@ -448,4 +422,35 @@ unsigned GLImageNativePixmap::GetInternalFormatForTesting( return GL_NONE; } +// static +bool GLImageNativePixmap::ValidFormat(gfx::BufferFormat format) { + switch (format) { + case gfx::BufferFormat::R_8: + case gfx::BufferFormat::R_16: + case gfx::BufferFormat::RG_88: + case gfx::BufferFormat::BGR_565: + case gfx::BufferFormat::RGBA_8888: + case gfx::BufferFormat::RGBX_8888: + case gfx::BufferFormat::BGRA_8888: + case gfx::BufferFormat::BGRX_8888: + case gfx::BufferFormat::BGRX_1010102: + case gfx::BufferFormat::RGBX_1010102: + 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::RGBA_F16: + case gfx::BufferFormat::UYVY_422: + return false; + } + + NOTREACHED(); + return false; +} + } // namespace gl diff --git a/chromium/ui/gl/gl_image_native_pixmap.h b/chromium/ui/gl/gl_image_native_pixmap.h index 66c985c8f75..7bf1e66aaf2 100644 --- a/chromium/ui/gl/gl_image_native_pixmap.h +++ b/chromium/ui/gl/gl_image_native_pixmap.h @@ -36,7 +36,8 @@ class GL_EXPORT GLImageNativePixmap : public gl::GLImageEGL { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override {} void Flush() override; void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, @@ -49,6 +50,8 @@ class GL_EXPORT GLImageNativePixmap : public gl::GLImageEGL { ~GLImageNativePixmap() override; private: + static bool ValidFormat(gfx::BufferFormat format); + unsigned internalformat_; scoped_refptr<gfx::NativePixmap> pixmap_; bool has_image_flush_external_; diff --git a/chromium/ui/gl/gl_image_native_pixmap_unittest.cc b/chromium/ui/gl/gl_image_native_pixmap_unittest.cc index 99a852db831..a8639cbfa4b 100644 --- a/chromium/ui/gl/gl_image_native_pixmap_unittest.cc +++ b/chromium/ui/gl/gl_image_native_pixmap_unittest.cc @@ -4,6 +4,7 @@ #include "ui/gl/gl_image_native_pixmap.h" +#include "ui/gl/gl_bindings.h" #include "ui/gl/test/gl_image_test_template.h" namespace gl { diff --git a/chromium/ui/gl/gl_image_shared_memory_unittest.cc b/chromium/ui/gl/gl_image_shared_memory_unittest.cc index e48ed75c8f0..26eb38be4cb 100644 --- a/chromium/ui/gl/gl_image_shared_memory_unittest.cc +++ b/chromium/ui/gl/gl_image_shared_memory_unittest.cc @@ -68,9 +68,13 @@ INSTANTIATE_TYPED_TEST_CASE_P(GLImageSharedMemory, GLImageOddSizeTest, GLImageTestTypes); +// https://crbug.com/830653 +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ + !defined(THREAD_SANITIZER) INSTANTIATE_TYPED_TEST_CASE_P(GLImageSharedMemory, GLImageCopyTest, GLImageTestTypes); +#endif class GLImageSharedMemoryPoolTestDelegate : public GLImageTestDelegateBase { public: @@ -109,9 +113,13 @@ class GLImageSharedMemoryPoolTestDelegate : public GLImageTestDelegateBase { int GetAdmissibleError() const { return 0; } }; +// https://crbug.com/830653 +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ + !defined(THREAD_SANITIZER) INSTANTIATE_TYPED_TEST_CASE_P(GLImageSharedMemoryPool, GLImageCopyTest, GLImageSharedMemoryPoolTestDelegate); +#endif } // namespace } // namespace gl diff --git a/chromium/ui/gl/gl_image_stub.cc b/chromium/ui/gl/gl_image_stub.cc index 3d99c4e7eec..8317cf6dc60 100644 --- a/chromium/ui/gl/gl_image_stub.cc +++ b/chromium/ui/gl/gl_image_stub.cc @@ -34,7 +34,8 @@ bool GLImageStub::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return false; } diff --git a/chromium/ui/gl/gl_image_stub.h b/chromium/ui/gl/gl_image_stub.h index 1c2cd6eb90c..524113f4dae 100644 --- a/chromium/ui/gl/gl_image_stub.h +++ b/chromium/ui/gl/gl_image_stub.h @@ -30,7 +30,8 @@ class GL_EXPORT GLImageStub : public GLImage { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override {} void Flush() override {} void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, diff --git a/chromium/ui/gl/gl_image_surface_texture.cc b/chromium/ui/gl/gl_image_surface_texture.cc index 40e30a8948b..de07bd7931c 100644 --- a/chromium/ui/gl/gl_image_surface_texture.cc +++ b/chromium/ui/gl/gl_image_surface_texture.cc @@ -86,7 +86,8 @@ bool GLImageSurfaceTexture::ScheduleOverlayPlane( int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return false; } diff --git a/chromium/ui/gl/gl_image_surface_texture.h b/chromium/ui/gl/gl_image_surface_texture.h index bd0ffdf2ede..22e3327b27b 100644 --- a/chromium/ui/gl/gl_image_surface_texture.h +++ b/chromium/ui/gl/gl_image_surface_texture.h @@ -37,7 +37,8 @@ class GL_EXPORT GLImageSurfaceTexture : public GLImage { int z_order, gfx::OverlayTransform transform, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; void SetColorSpace(const gfx::ColorSpace& color_space) override {} void Flush() override {} void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, diff --git a/chromium/ui/gl/gl_implementation.cc b/chromium/ui/gl/gl_implementation.cc index f4493d63fd4..c9d3d892efe 100644 --- a/chromium/ui/gl/gl_implementation.cc +++ b/chromium/ui/gl/gl_implementation.cc @@ -14,7 +14,6 @@ #include "base/macros.h" #include "base/memory/protected_memory.h" #include "base/memory/protected_memory_cfi.h" -#include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" diff --git a/chromium/ui/gl/gl_implementation_osmesa.cc b/chromium/ui/gl/gl_implementation_osmesa.cc index 14fa64897a2..f5090948d24 100644 --- a/chromium/ui/gl/gl_implementation_osmesa.cc +++ b/chromium/ui/gl/gl_implementation_osmesa.cc @@ -33,7 +33,14 @@ bool InitializeStaticGLBindingsOSMesaGL() { } #endif // !defined(OS_FUCHSIA) - base::FilePath library_path = module_path.Append("libosmesa.so"); +#if defined(OS_WIN) + base::FilePath library_path = + module_path.Append(FILE_PATH_LITERAL("osmesa.dll")); +#else + base::FilePath library_path = + module_path.Append(FILE_PATH_LITERAL("libosmesa.so")); +#endif + base::NativeLibrary library = LoadLibraryAndPrintError(library_path); if (!library) return false; diff --git a/chromium/ui/gl/gl_mock_autogen_egl.h b/chromium/ui/gl/gl_mock_autogen_egl.h index c4780e0ed90..8a6f77e0fce 100644 --- a/chromium/ui/gl/gl_mock_autogen_egl.h +++ b/chromium/ui/gl/gl_mock_autogen_egl.h @@ -52,7 +52,7 @@ MOCK_METHOD4(CreatePixmapSurface, const EGLint* attrib_list)); MOCK_METHOD2(CreateStreamKHR, EGLStreamKHR(EGLDisplay dpy, const EGLint* attrib_list)); -MOCK_METHOD3(CreateStreamProducerD3DTextureNV12ANGLE, +MOCK_METHOD3(CreateStreamProducerD3DTextureANGLE, EGLBoolean(EGLDisplay dpy, EGLStreamKHR stream, EGLAttrib* attrib_list)); @@ -71,6 +71,18 @@ MOCK_METHOD2(DestroyStreamKHR, EGLBoolean(EGLDisplay dpy, EGLStreamKHR stream)); MOCK_METHOD2(DestroySurface, EGLBoolean(EGLDisplay dpy, EGLSurface surface)); MOCK_METHOD2(DestroySyncKHR, EGLBoolean(EGLDisplay dpy, EGLSyncKHR sync)); MOCK_METHOD2(DupNativeFenceFDANDROID, EGLint(EGLDisplay dpy, EGLSyncKHR sync)); +MOCK_METHOD5(ExportDMABUFImageMESA, + EGLBoolean(EGLDisplay dpy, + EGLImageKHR image, + int* fds, + EGLint* strides, + EGLint* offsets)); +MOCK_METHOD5(ExportDMABUFImageQueryMESA, + EGLBoolean(EGLDisplay dpy, + EGLImageKHR image, + int* fourcc, + int* num_planes, + EGLuint64KHR* modifiers)); MOCK_METHOD5(GetCompositorTimingANDROID, EGLBoolean(EGLDisplay dpy, EGLSurface surface, @@ -206,7 +218,7 @@ MOCK_METHOD2(StreamConsumerGLTextureExternalKHR, EGLBoolean(EGLDisplay dpy, EGLStreamKHR stream)); MOCK_METHOD2(StreamConsumerReleaseKHR, EGLBoolean(EGLDisplay dpy, EGLStreamKHR stream)); -MOCK_METHOD4(StreamPostD3DTextureNV12ANGLE, +MOCK_METHOD4(StreamPostD3DTextureANGLE, EGLBoolean(EGLDisplay dpy, EGLStreamKHR stream, void* texture, diff --git a/chromium/ui/gl/gl_mock_autogen_gl.h b/chromium/ui/gl/gl_mock_autogen_gl.h index c363dd99095..002a1fef8c0 100644 --- a/chromium/ui/gl/gl_mock_autogen_gl.h +++ b/chromium/ui/gl/gl_mock_autogen_gl.h @@ -419,14 +419,14 @@ MOCK_METHOD5(GetBufferPointervRobustANGLE, GLsizei* length, void** params)); MOCK_METHOD8(GetDebugMessageLog, - void(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog)); + GLuint(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog)); MOCK_METHOD0(GetError, GLenum()); MOCK_METHOD3(GetFenceivNV, void(GLuint fence, GLenum pname, GLint* params)); MOCK_METHOD2(GetFloatv, void(GLenum pname, GLfloat* params)); diff --git a/chromium/ui/gl/gl_stub_autogen_gl.cc b/chromium/ui/gl/gl_stub_autogen_gl.cc index e30dbfd7eaf..80d35bd17fe 100644 --- a/chromium/ui/gl/gl_stub_autogen_gl.cc +++ b/chromium/ui/gl/gl_stub_autogen_gl.cc @@ -42,6 +42,17 @@ GLint GLStubApiBase::glGetAttribLocationFn(GLuint program, const char* name) { return 0; } +GLuint GLStubApiBase::glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) { + return 0; +} + GLenum GLStubApiBase::glGetErrorFn() { return 0; } diff --git a/chromium/ui/gl/gl_stub_autogen_gl.h b/chromium/ui/gl/gl_stub_autogen_gl.h index 394cffd7f84..32160fa8d5c 100644 --- a/chromium/ui/gl/gl_stub_autogen_gl.h +++ b/chromium/ui/gl/gl_stub_autogen_gl.h @@ -438,14 +438,14 @@ void glGetBufferPointervRobustANGLEFn(GLenum target, GLsizei bufSize, GLsizei* length, void** params) override {} -void glGetDebugMessageLogFn(GLuint count, - GLsizei bufSize, - GLenum* sources, - GLenum* types, - GLuint* ids, - GLenum* severities, - GLsizei* lengths, - char* messageLog) override {} +GLuint glGetDebugMessageLogFn(GLuint count, + GLsizei bufSize, + GLenum* sources, + GLenum* types, + GLuint* ids, + GLenum* severities, + GLsizei* lengths, + char* messageLog) override; GLenum glGetErrorFn() override; void glGetFenceivNVFn(GLuint fence, GLenum pname, GLint* params) override {} void glGetFloatvFn(GLenum pname, GLfloat* params) override {} diff --git a/chromium/ui/gl/gl_surface.cc b/chromium/ui/gl/gl_surface.cc index 584f033f54a..9ec8b0a53ec 100644 --- a/chromium/ui/gl/gl_surface.cc +++ b/chromium/ui/gl/gl_surface.cc @@ -157,7 +157,8 @@ bool GLSurface::ScheduleOverlayPlane(int z_order, gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { NOTIMPLEMENTED(); return false; } @@ -404,9 +405,10 @@ bool GLSurfaceAdapter::ScheduleOverlayPlane(int z_order, gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { - return surface_->ScheduleOverlayPlane( - z_order, transform, image, bounds_rect, crop_rect); + const gfx::RectF& crop_rect, + bool enable_blend) { + return surface_->ScheduleOverlayPlane(z_order, transform, image, bounds_rect, + crop_rect, enable_blend); } bool GLSurfaceAdapter::ScheduleDCLayer( diff --git a/chromium/ui/gl/gl_surface.h b/chromium/ui/gl/gl_surface.h index 8fad23d2154..ee5ee65f7b6 100644 --- a/chromium/ui/gl/gl_surface.h +++ b/chromium/ui/gl/gl_surface.h @@ -216,11 +216,14 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { // |bounds_rect| specify where it is supposed to be on the screen in pixels. // |crop_rect| specifies the region within the buffer to be placed inside // |bounds_rect|. + // |enable_blend| specifies if alpha blending, with premultiplied alpha + // should be applied at scanout. virtual bool ScheduleOverlayPlane(int z_order, gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect); + const gfx::RectF& crop_rect, + bool enable_blend); // Schedule a CALayer to be shown at swap time. // All arguments correspond to their CALayer properties. @@ -347,7 +350,8 @@ class GL_EXPORT GLSurfaceAdapter : public GLSurface { gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; bool ScheduleDCLayer(const ui::DCRendererLayerParams& params) override; bool SetEnableDCLayers(bool enable) override; bool IsSurfaceless() const override; diff --git a/chromium/ui/gl/gl_surface_egl.cc b/chromium/ui/gl/gl_surface_egl.cc index f1413f215a3..9c77b7aff23 100644 --- a/chromium/ui/gl/gl_surface_egl.cc +++ b/chromium/ui/gl/gl_surface_egl.cc @@ -15,7 +15,6 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" @@ -131,7 +130,7 @@ bool GLSurfaceEGL::initialized_ = false; namespace { -EGLDisplay g_display = EGL_NO_DISPLAY; +EGLDisplay g_egl_display = EGL_NO_DISPLAY; EGLNativeDisplayType g_native_display = EGL_DEFAULT_DISPLAY; const char* g_egl_extensions = nullptr; @@ -184,9 +183,10 @@ class EGLSyncControlVSyncProvider : public SyncControlVSyncProvider { int64_t* media_stream_counter, int64_t* swap_buffer_counter) override { uint64_t u_system_time, u_media_stream_counter, u_swap_buffer_counter; - bool result = eglGetSyncValuesCHROMIUM( - g_display, surface_, &u_system_time, - &u_media_stream_counter, &u_swap_buffer_counter) == EGL_TRUE; + bool result = + eglGetSyncValuesCHROMIUM(g_egl_display, surface_, &u_system_time, + &u_media_stream_counter, + &u_swap_buffer_counter) == EGL_TRUE; if (result) { *system_time = static_cast<int64_t>(u_system_time); *media_stream_counter = static_cast<int64_t>(u_media_stream_counter); @@ -411,7 +411,7 @@ EGLConfig ChooseConfig(GLSurfaceFormat format, bool surfaceless) { EGLConfig config = nullptr; EGLConfig* config_data = &config; // Validate if there are any configs for given attribs. - if (!ValidateEglConfig(g_display, choose_attributes, &num_configs)) { + if (!ValidateEglConfig(g_egl_display, choose_attributes, &num_configs)) { // Try the next renderable_type continue; } @@ -422,8 +422,8 @@ EGLConfig ChooseConfig(GLSurfaceFormat format, bool surfaceless) { config_data = matching_configs.get(); } - if (!eglChooseConfig(g_display, choose_attributes, config_data, config_size, - &num_configs)) { + if (!eglChooseConfig(g_egl_display, choose_attributes, config_data, + config_size, &num_configs)) { LOG(ERROR) << "eglChooseConfig failed with error " << GetLastEGLErrorString(); return config; @@ -437,14 +437,14 @@ EGLConfig ChooseConfig(GLSurfaceFormat format, bool surfaceless) { for (int i = 0; i < num_configs; i++) { EGLint red, green, blue, alpha; // Read the relevant attributes of the EGLConfig. - if (eglGetConfigAttrib(g_display, matching_configs[i], EGL_RED_SIZE, + if (eglGetConfigAttrib(g_egl_display, matching_configs[i], EGL_RED_SIZE, &red) && - eglGetConfigAttrib(g_display, matching_configs[i], EGL_BLUE_SIZE, - &blue) && - eglGetConfigAttrib(g_display, matching_configs[i], EGL_GREEN_SIZE, - &green) && - eglGetConfigAttrib(g_display, matching_configs[i], EGL_ALPHA_SIZE, - &alpha) && + eglGetConfigAttrib(g_egl_display, matching_configs[i], + EGL_BLUE_SIZE, &blue) && + eglGetConfigAttrib(g_egl_display, matching_configs[i], + EGL_GREEN_SIZE, &green) && + eglGetConfigAttrib(g_egl_display, matching_configs[i], + EGL_ALPHA_SIZE, &alpha) && alpha == 0 && red == 5 && green == 6 && blue == 5) { config = matching_configs[i]; match_found = true; @@ -454,11 +454,12 @@ EGLConfig ChooseConfig(GLSurfaceFormat format, bool surfaceless) { if (!match_found) { // To fall back to default 32 bit format, choose with // the right attributes again. - if (!ValidateEglConfig(g_display, config_attribs_8888, &num_configs)) { + if (!ValidateEglConfig(g_egl_display, config_attribs_8888, + &num_configs)) { // Try the next renderable_type continue; } - if (!eglChooseConfig(g_display, config_attribs_8888, &config, 1, + if (!eglChooseConfig(g_egl_display, config_attribs_8888, &config, 1, &num_configs)) { LOG(ERROR) << "eglChooseConfig failed with error " << GetLastEGLErrorString(); @@ -549,7 +550,7 @@ GLSurfaceFormat GLSurfaceEGL::GetFormat() { } EGLDisplay GLSurfaceEGL::GetDisplay() { - return g_display; + return g_egl_display; } EGLConfig GLSurfaceEGL::GetConfig() { @@ -568,7 +569,7 @@ bool GLSurfaceEGL::InitializeOneOff(EGLNativeDisplayType native_display) { g_driver_egl.InitializeClientExtensionBindings(); InitializeDisplay(native_display); - if (g_display == EGL_NO_DISPLAY) + if (g_egl_display == EGL_NO_DISPLAY) return false; // Must be called after InitializeDisplay(). @@ -580,14 +581,14 @@ bool GLSurfaceEGL::InitializeOneOff(EGLNativeDisplayType native_display) { // static bool GLSurfaceEGL::InitializeOneOffForTesting() { g_driver_egl.InitializeClientExtensionBindings(); - g_display = eglGetCurrentDisplay(); + g_egl_display = eglGetCurrentDisplay(); g_driver_egl.InitializeExtensionBindings(); return InitializeOneOffCommon(); } // static bool GLSurfaceEGL::InitializeOneOffCommon() { - g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS); + g_egl_extensions = eglQueryString(g_egl_display, EGL_EXTENSIONS); g_egl_create_context_robustness_supported = HasEGLExtension("EGL_EXT_create_context_robustness"); @@ -690,20 +691,20 @@ bool GLSurfaceEGL::InitializeExtensionSettingsOneOff() { if (!initialized_) return false; g_driver_egl.UpdateConditionalExtensionBindings(); - g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS); + g_egl_extensions = eglQueryString(g_egl_display, EGL_EXTENSIONS); return true; } // static void GLSurfaceEGL::ShutdownOneOff() { - angle::ResetPlatform(g_display); + angle::ResetPlatform(g_egl_display); - if (g_display != EGL_NO_DISPLAY) { + if (g_egl_display != EGL_NO_DISPLAY) { DCHECK(g_driver_egl.fn.eglTerminateFn); - eglTerminate(g_display); + eglTerminate(g_egl_display); } - g_display = EGL_NO_DISPLAY; + g_egl_display = EGL_NO_DISPLAY; g_egl_extensions = nullptr; g_egl_create_context_robustness_supported = false; @@ -723,7 +724,7 @@ void GLSurfaceEGL::ShutdownOneOff() { // static EGLDisplay GLSurfaceEGL::GetHardwareDisplay() { - return g_display; + return g_egl_display; } // static @@ -792,8 +793,8 @@ GLSurfaceEGL::~GLSurfaceEGL() {} // static EGLDisplay GLSurfaceEGL::InitializeDisplay( EGLNativeDisplayType native_display) { - if (g_display != EGL_NO_DISPLAY) { - return g_display; + if (g_egl_display != EGL_NO_DISPLAY) { + return g_egl_display; } g_native_display = native_display; @@ -846,12 +847,12 @@ EGLDisplay GLSurfaceEGL::InitializeDisplay( } else { UMA_HISTOGRAM_ENUMERATION("GPU.EGLDisplayType", display_type, DISPLAY_TYPE_MAX); - g_display = display; + g_egl_display = display; break; } } - return g_display; + return g_egl_display; } NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL( @@ -1361,13 +1362,14 @@ bool NativeViewGLSurfaceEGL::ScheduleOverlayPlane( gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { #if !defined(OS_ANDROID) NOTIMPLEMENTED(); return false; #else - pending_overlays_.push_back( - GLSurfaceOverlay(z_order, transform, image, bounds_rect, crop_rect)); + pending_overlays_.push_back(GLSurfaceOverlay(z_order, transform, image, + bounds_rect, crop_rect, true)); return true; #endif } @@ -1523,8 +1525,7 @@ void* PbufferGLSurfaceEGL::GetShareHandle() { return NULL; void* handle; - if (!eglQuerySurfacePointerANGLE(g_display, - GetHandle(), + if (!eglQuerySurfacePointerANGLE(g_egl_display, GetHandle(), EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &handle)) { return NULL; diff --git a/chromium/ui/gl/gl_surface_egl.h b/chromium/ui/gl/gl_surface_egl.h index 1e8cb17097c..8f7110e22a7 100644 --- a/chromium/ui/gl/gl_surface_egl.h +++ b/chromium/ui/gl/gl_surface_egl.h @@ -135,7 +135,8 @@ class GL_EXPORT NativeViewGLSurfaceEGL : public GLSurfaceEGL { gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; bool FlipsVertically() const override; bool BuffersFlipped() const override; diff --git a/chromium/ui/gl/gl_surface_glx.cc b/chromium/ui/gl/gl_surface_glx.cc index fb426317082..cfca601a772 100644 --- a/chromium/ui/gl/gl_surface_glx.cc +++ b/chromium/ui/gl/gl_surface_glx.cc @@ -35,7 +35,6 @@ namespace gl { namespace { -Display* g_display = nullptr; bool g_glx_context_create = false; bool g_glx_create_context_robustness_supported = false; bool g_glx_create_context_profile_supported = false; @@ -150,7 +149,7 @@ class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider { bool GetSyncValues(int64_t* system_time, int64_t* media_stream_counter, int64_t* swap_buffer_counter) override { - return glXGetSyncValuesOML(g_display, glx_window_, system_time, + return glXGetSyncValuesOML(gfx::GetXDisplay(), glx_window_, system_time, media_stream_counter, swap_buffer_counter); } @@ -158,7 +157,8 @@ class OMLSyncControlVSyncProvider : public SyncControlVSyncProvider { if (!g_glx_get_msc_rate_oml_supported) return false; - if (!glXGetMscRateOML(g_display, glx_window_, numerator, denominator)) { + if (!glXGetMscRateOML(gfx::GetXDisplay(), glx_window_, numerator, + denominator)) { // Once glXGetMscRateOML has been found to fail, don't try again, // since each failing call may spew an error message. g_glx_get_msc_rate_oml_supported = false; @@ -218,7 +218,7 @@ class SGIVideoSyncProviderThreadShim { vsync_lock_() { // This ensures that creation of |parent_window_| has occured when this shim // is executing in the same thread as the call to create |parent_window_|. - XSync(g_display, x11::False); + XSync(gfx::GetXDisplay(), x11::False); } virtual ~SGIVideoSyncProviderThreadShim() { @@ -346,8 +346,8 @@ class SGIVideoSyncVSyncProvider const gfx::VSyncProvider::UpdateVSyncCallback& callback) override { // Only one outstanding request per surface. if (!pending_callback_) { - pending_callback_ = - std::make_unique<gfx::VSyncProvider::UpdateVSyncCallback>(callback); + DCHECK(callback); + pending_callback_ = callback; vsync_thread_->task_runner()->PostTask( FROM_HERE, base::BindOnce(&SGIVideoSyncProviderThreadShim::GetVSyncParameters, @@ -370,8 +370,7 @@ class SGIVideoSyncVSyncProvider void PendingCallbackRunner(const base::TimeTicks timebase, const base::TimeDelta interval) { DCHECK(pending_callback_); - pending_callback_->Run(timebase, interval); - pending_callback_.reset(); + std::move(pending_callback_).Run(timebase, interval); } scoped_refptr<SGIVideoSyncThread> vsync_thread_; @@ -379,7 +378,7 @@ class SGIVideoSyncVSyncProvider // Thread shim through which the sync provider is accessed on |vsync_thread_|. std::unique_ptr<SGIVideoSyncProviderThreadShim> shim_; - std::unique_ptr<gfx::VSyncProvider::UpdateVSyncCallback> pending_callback_; + gfx::VSyncProvider::UpdateVSyncCallback pending_callback_; // Raw pointers to sync primitives owned by the shim_. // These will only be referenced before we post a task to destroy @@ -415,14 +414,13 @@ bool GLSurfaceGLX::InitializeOneOff() { // it's own thread. gfx::InitializeThreadedX11(); - g_display = gfx::GetXDisplay(); - if (!g_display) { + if (!gfx::GetXDisplay()) { LOG(ERROR) << "XOpenDisplay failed."; return false; } int major, minor; - if (!glXQueryVersion(g_display, &major, &minor)) { + if (!glXQueryVersion(gfx::GetXDisplay(), &major, &minor)) { LOG(ERROR) << "glxQueryVersion failed"; return false; } @@ -436,8 +434,9 @@ bool GLSurfaceGLX::InitializeOneOff() { gl::GLVisualPickerGLX::GetInstance()->system_visual(); g_visual = visual_info.visual; g_depth = visual_info.depth; - g_colormap = XCreateColormap(g_display, DefaultRootWindow(g_display), - visual_info.visual, AllocNone); + g_colormap = + XCreateColormap(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()), + visual_info.visual, AllocNone); // We create a dummy unmapped window for both the main Display and the video // sync Display so that the Nvidia driver can initialize itself before the @@ -445,8 +444,8 @@ bool GLSurfaceGLX::InitializeOneOff() { // Unfortunately some fds e.g. /dev/nvidia0 are cached per thread and because // we can't start threads before the sandbox is set up, these are accessed // through the broker process. See GpuProcessPolicy::InitGpuBrokerProcess. - if (!CreateDummyWindow(g_display)) { - LOG(ERROR) << "CreateDummyWindow(g_display) failed"; + if (!CreateDummyWindow(gfx::GetXDisplay())) { + LOG(ERROR) << "CreateDummyWindow(gfx::GetXDisplay()) failed"; return false; } @@ -494,7 +493,6 @@ bool GLSurfaceGLX::InitializeExtensionSettingsOneOff() { // static void GLSurfaceGLX::ShutdownOneOff() { initialized_ = false; - g_display = nullptr; g_glx_context_create = false; g_glx_create_context_robustness_supported = false; g_glx_create_context_profile_supported = false; @@ -514,7 +512,7 @@ void GLSurfaceGLX::ShutdownOneOff() { // static const char* GLSurfaceGLX::GetGLXExtensions() { - return glXQueryExtensionsString(g_display, 0); + return glXQueryExtensionsString(gfx::GetXDisplay(), 0); } // static @@ -563,7 +561,7 @@ bool GLSurfaceGLX::IsOMLSyncControlSupported() { } void* GLSurfaceGLX::GetDisplay() { - return g_display; + return gfx::GetXDisplay(); } GLSurfaceGLX::~GLSurfaceGLX() {} @@ -577,7 +575,7 @@ NativeViewGLSurfaceGLX::NativeViewGLSurfaceGLX(gfx::AcceleratedWidget window) bool NativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) { XWindowAttributes attributes; - if (!XGetWindowAttributes(g_display, parent_window_, &attributes)) { + if (!XGetWindowAttributes(gfx::GetXDisplay(), parent_window_, &attributes)) { LOG(ERROR) << "XGetWindowAttributes failed for window " << parent_window_ << "."; return false; @@ -592,24 +590,25 @@ bool NativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) { memset(&swa, 0, sizeof(swa)); swa.background_pixmap = 0; swa.bit_gravity = NorthWestGravity; - window_ = XCreateWindow(g_display, parent_window_, 0, 0, size_.width(), - size_.height(), 0, CopyFromParent, InputOutput, - CopyFromParent, CWBackPixmap | CWBitGravity, &swa); + window_ = + XCreateWindow(gfx::GetXDisplay(), parent_window_, 0, 0, size_.width(), + size_.height(), 0, CopyFromParent, InputOutput, + CopyFromParent, CWBackPixmap | CWBitGravity, &swa); if (!window_) { LOG(ERROR) << "XCreateWindow failed"; return false; } - XMapWindow(g_display, window_); + XMapWindow(gfx::GetXDisplay(), window_); RegisterEvents(); - XFlush(g_display); + XFlush(gfx::GetXDisplay()); GetConfig(); if (!config_) { LOG(ERROR) << "Failed to get GLXConfig"; return false; } - glx_window_ = glXCreateWindow(g_display, config_, window_, NULL); + glx_window_ = glXCreateWindow(gfx::GetXDisplay(), config_, window_, NULL); if (!glx_window_) { LOG(ERROR) << "glXCreateWindow failed"; return false; @@ -647,14 +646,14 @@ void NativeViewGLSurfaceGLX::Destroy() { presentation_helper_ = nullptr; vsync_provider_ = nullptr; if (glx_window_) { - glXDestroyWindow(g_display, glx_window_); + glXDestroyWindow(gfx::GetXDisplay(), glx_window_); glx_window_ = 0; } if (window_) { UnregisterEvents(); - XDestroyWindow(g_display, window_); + XDestroyWindow(gfx::GetXDisplay(), window_); window_ = 0; - XFlush(g_display); + XFlush(gfx::GetXDisplay()); } } @@ -664,7 +663,7 @@ bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size, bool has_alpha) { size_ = size; glXWaitGL(); - XResizeWindow(g_display, window_, size.width(), size.height()); + XResizeWindow(gfx::GetXDisplay(), window_, size.width(), size.height()); glXWaitX(); return true; } @@ -679,7 +678,7 @@ gfx::SwapResult NativeViewGLSurfaceGLX::SwapBuffers( GetSize().width(), "height", GetSize().height()); GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers( presentation_helper_.get(), callback); - glXSwapBuffers(g_display, GetDrawableHandle()); + glXSwapBuffers(gfx::GetXDisplay(), GetDrawableHandle()); return scoped_swap_buffers.result(); } @@ -701,7 +700,7 @@ bool NativeViewGLSurfaceGLX::SupportsPostSubBuffer() { void* NativeViewGLSurfaceGLX::GetConfig() { if (!config_) - config_ = GetConfigForWindow(g_display, window_); + config_ = GetConfigForWindow(gfx::GetXDisplay(), window_); return config_; } @@ -723,7 +722,8 @@ gfx::SwapResult NativeViewGLSurfaceGLX::PostSubBuffer( GLSurfacePresentationHelper::ScopedSwapBuffers scoped_swap_buffers( presentation_helper_.get(), callback); - glXCopySubBufferMESA(g_display, GetDrawableHandle(), x, y, width, height); + glXCopySubBufferMESA(gfx::GetXDisplay(), GetDrawableHandle(), x, y, width, + height); return scoped_swap_buffers.result(); } @@ -743,9 +743,9 @@ NativeViewGLSurfaceGLX::~NativeViewGLSurfaceGLX() { void NativeViewGLSurfaceGLX::ForwardExposeEvent(XEvent* event) { XEvent forwarded_event = *event; forwarded_event.xexpose.window = parent_window_; - XSendEvent(g_display, parent_window_, x11::False, ExposureMask, + XSendEvent(gfx::GetXDisplay(), parent_window_, x11::False, ExposureMask, &forwarded_event); - XFlush(g_display); + XFlush(gfx::GetXDisplay()); } bool NativeViewGLSurfaceGLX::CanHandleEvent(XEvent* event) { @@ -768,14 +768,14 @@ UnmappedNativeViewGLSurfaceGLX::UnmappedNativeViewGLSurfaceGLX( bool UnmappedNativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) { DCHECK(!window_); - gfx::AcceleratedWidget parent_window = DefaultRootWindow(g_display); + gfx::AcceleratedWidget parent_window = DefaultRootWindow(gfx::GetXDisplay()); XSetWindowAttributes attrs; attrs.border_pixel = 0; attrs.colormap = g_colormap; - window_ = XCreateWindow(g_display, parent_window, 0, 0, size_.width(), - size_.height(), 0, g_depth, InputOutput, g_visual, - CWBorderPixel | CWColormap, &attrs); + window_ = XCreateWindow( + gfx::GetXDisplay(), parent_window, 0, 0, size_.width(), size_.height(), 0, + g_depth, InputOutput, g_visual, CWBorderPixel | CWColormap, &attrs); if (!window_) { LOG(ERROR) << "XCreateWindow failed"; return false; @@ -785,7 +785,7 @@ bool UnmappedNativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) { LOG(ERROR) << "Failed to get GLXConfig"; return false; } - glx_window_ = glXCreateWindow(g_display, config_, window_, NULL); + glx_window_ = glXCreateWindow(gfx::GetXDisplay(), config_, window_, NULL); if (!glx_window_) { LOG(ERROR) << "glXCreateWindow failed"; return false; @@ -796,11 +796,11 @@ bool UnmappedNativeViewGLSurfaceGLX::Initialize(GLSurfaceFormat format) { void UnmappedNativeViewGLSurfaceGLX::Destroy() { config_ = nullptr; if (glx_window_) { - glXDestroyWindow(g_display, glx_window_); + glXDestroyWindow(gfx::GetXDisplay(), glx_window_); glx_window_ = 0; } if (window_) { - XDestroyWindow(g_display, window_); + XDestroyWindow(gfx::GetXDisplay(), window_); window_ = 0; } } @@ -825,7 +825,7 @@ void* UnmappedNativeViewGLSurfaceGLX::GetHandle() { void* UnmappedNativeViewGLSurfaceGLX::GetConfig() { if (!config_) - config_ = GetConfigForWindow(g_display, window_); + config_ = GetConfigForWindow(gfx::GetXDisplay(), window_); return config_; } diff --git a/chromium/ui/gl/gl_surface_overlay.cc b/chromium/ui/gl/gl_surface_overlay.cc index a8a606b8212..0078c78f5ea 100644 --- a/chromium/ui/gl/gl_surface_overlay.cc +++ b/chromium/ui/gl/gl_surface_overlay.cc @@ -16,12 +16,14 @@ GLSurfaceOverlay::GLSurfaceOverlay(int z_order, gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) + const gfx::RectF& crop_rect, + bool enable_blend) : z_order_(z_order), transform_(transform), image_(image), bounds_rect_(bounds_rect), - crop_rect_(crop_rect) {} + crop_rect_(crop_rect), + enable_blend_(enable_blend) {} GLSurfaceOverlay::GLSurfaceOverlay(const GLSurfaceOverlay& other) = default; @@ -30,7 +32,7 @@ GLSurfaceOverlay::~GLSurfaceOverlay() {} bool GLSurfaceOverlay::ScheduleOverlayPlane( gfx::AcceleratedWidget widget) const { return image_->ScheduleOverlayPlane(widget, z_order_, transform_, - bounds_rect_, crop_rect_); + bounds_rect_, crop_rect_, enable_blend_); } void GLSurfaceOverlay::Flush() const { diff --git a/chromium/ui/gl/gl_surface_overlay.h b/chromium/ui/gl/gl_surface_overlay.h index ac378197224..7adba324a73 100644 --- a/chromium/ui/gl/gl_surface_overlay.h +++ b/chromium/ui/gl/gl_surface_overlay.h @@ -23,7 +23,8 @@ class GL_EXPORT GLSurfaceOverlay { gfx::OverlayTransform transform, GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect); + const gfx::RectF& crop_rect, + bool enable_blend); GLSurfaceOverlay(const GLSurfaceOverlay& other); ~GLSurfaceOverlay(); @@ -39,6 +40,7 @@ class GL_EXPORT GLSurfaceOverlay { scoped_refptr<GLImage> image_; gfx::Rect bounds_rect_; gfx::RectF crop_rect_; + bool enable_blend_; }; } // namespace gl diff --git a/chromium/ui/gl/gl_surface_wgl.cc b/chromium/ui/gl/gl_surface_wgl.cc index d3a91b9b00b..f880c831bdd 100644 --- a/chromium/ui/gl/gl_surface_wgl.cc +++ b/chromium/ui/gl/gl_surface_wgl.cc @@ -145,7 +145,7 @@ class DisplayWGL { HDC device_context_; int pixel_format_; }; -DisplayWGL* g_display; +DisplayWGL* g_wgl_display; } // namespace // static @@ -166,12 +166,12 @@ bool GLSurfaceWGL::InitializeOneOff() { if (initialized_) return true; - DCHECK(g_display == NULL); + DCHECK(g_wgl_display == NULL); std::unique_ptr<DisplayWGL> wgl_display(new DisplayWGL); if (!wgl_display->Init()) return false; - g_display = wgl_display.release(); + g_wgl_display = wgl_display.release(); initialized_ = true; return true; } @@ -185,13 +185,13 @@ bool GLSurfaceWGL::InitializeExtensionSettingsOneOff() { } void GLSurfaceWGL::InitializeOneOffForTesting() { - if (g_display == NULL) { - g_display = new DisplayWGL; + if (g_wgl_display == NULL) { + g_wgl_display = new DisplayWGL; } } HDC GLSurfaceWGL::GetDisplayDC() { - return g_display->device_context(); + return g_wgl_display->device_context(); } NativeViewGLSurfaceWGL::NativeViewGLSurfaceWGL(gfx::AcceleratedWidget window) @@ -215,19 +215,11 @@ bool NativeViewGLSurfaceWGL::Initialize(GLSurfaceFormat format) { // Create a child window. WGL has problems using a window handle owned by // another process. - child_window_ = - CreateWindowEx(WS_EX_NOPARENTNOTIFY, - reinterpret_cast<wchar_t*>(g_display->window_class()), - L"", - WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, - 0, - 0, - rect.right - rect.left, - rect.bottom - rect.top, - window_, - NULL, - NULL, - NULL); + child_window_ = CreateWindowEx( + WS_EX_NOPARENTNOTIFY, + reinterpret_cast<wchar_t*>(g_wgl_display->window_class()), L"", + WS_CHILDWINDOW | WS_DISABLED | WS_VISIBLE, 0, 0, rect.right - rect.left, + rect.bottom - rect.top, window_, NULL, NULL, NULL); if (!child_window_) { LOG(ERROR) << "CreateWindow failed.\n"; Destroy(); @@ -242,8 +234,7 @@ bool NativeViewGLSurfaceWGL::Initialize(GLSurfaceFormat format) { return false; } - if (!SetPixelFormat(device_context_, - g_display->pixel_format(), + if (!SetPixelFormat(device_context_, g_wgl_display->pixel_format(), &kPixelFormatDescriptor)) { LOG(ERROR) << "Unable to set the pixel format for GL context."; Destroy(); @@ -362,10 +353,9 @@ bool PbufferGLSurfaceWGL::Initialize(GLSurfaceFormat format) { } const int kNoAttributes[] = { 0 }; - pbuffer_ = wglCreatePbufferARB(g_display->device_context(), - g_display->pixel_format(), - size_.width(), size_.height(), - kNoAttributes); + pbuffer_ = wglCreatePbufferARB(g_wgl_display->device_context(), + g_wgl_display->pixel_format(), size_.width(), + size_.height(), kNoAttributes); if (!pbuffer_) { LOG(ERROR) << "Unable to create pbuffer."; diff --git a/chromium/ui/gl/init/BUILD.gn b/chromium/ui/gl/init/BUILD.gn index d55a06bf481..d708e6c468b 100644 --- a/chromium/ui/gl/init/BUILD.gn +++ b/chromium/ui/gl/init/BUILD.gn @@ -38,7 +38,7 @@ jumbo_component("init") { "gl_factory_android.cc", "gl_initializer_android.cc", ] - } else if (is_win) { + } else if (is_win && !use_ozone) { sources += [ "gl_factory_win.cc", "gl_initializer_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 283dd1f028f..a1e787e416b 100644 --- a/chromium/ui/gl/init/create_gr_gl_interface.cc +++ b/chromium/ui/gl/init/create_gr_gl_interface.cc @@ -460,13 +460,8 @@ sk_sp<const GrGLInterface> CreateGrGLInterface( functions->fDebugMessageControl = gl->glDebugMessageControlFn; functions->fDebugMessageInsert = gl->glDebugMessageInsertFn; - // TODO(piman): Our GL headers are out-of-date and define GLDEBUGPROC - // incorrectly wrt const-ness. - functions->fDebugMessageCallback = - reinterpret_cast<GrGLDebugMessageCallbackProc>( - gl->glDebugMessageCallbackFn); - functions->fGetDebugMessageLog = - reinterpret_cast<GrGLGetDebugMessageLogProc>(gl->glGetDebugMessageLogFn); + functions->fDebugMessageCallback = gl->glDebugMessageCallbackFn; + functions->fGetDebugMessageLog = gl->glGetDebugMessageLogFn; functions->fPushDebugGroup = gl->glPushDebugGroupFn; functions->fPopDebugGroup = gl->glPopDebugGroupFn; functions->fObjectLabel = gl->glObjectLabelFn; diff --git a/chromium/ui/gl/init/gl_factory_mac.cc b/chromium/ui/gl/init/gl_factory_mac.cc index 52544651c31..da1b21fe3e0 100644 --- a/chromium/ui/gl/init/gl_factory_mac.cc +++ b/chromium/ui/gl/init/gl_factory_mac.cc @@ -67,6 +67,7 @@ std::vector<GLImplementation> GetAllowedGLImplementations() { impls.push_back(kGLImplementationDesktopGLCoreProfile); #if BUILDFLAG(USE_EGL_ON_MAC) impls.push_back(kGLImplementationEGLGLES2); + impls.push_back(kGLImplementationSwiftShaderGL); #endif // BUILDFLAG(USE_EGL_ON_MAC) impls.push_back(kGLImplementationDesktopGL); impls.push_back(kGLImplementationAppleGL); @@ -90,6 +91,7 @@ scoped_refptr<GLContext> CreateGLContext(GLShareGroup* share_group, compatible_surface, attribs); #if BUILDFLAG(USE_EGL_ON_MAC) case kGLImplementationEGLGLES2: + case kGLImplementationSwiftShaderGL: return InitializeGLContext(new GLContextEGL(share_group), compatible_surface, attribs); #endif // BUILDFLAG(USE_EGL_ON_MAC) @@ -116,7 +118,8 @@ scoped_refptr<GLSurface> CreateViewGLSurface(gfx::AcceleratedWidget window) { case kGLImplementationDesktopGL: case kGLImplementationDesktopGLCoreProfile: case kGLImplementationAppleGL: - case kGLImplementationEGLGLES2: { + case kGLImplementationEGLGLES2: + case kGLImplementationSwiftShaderGL: { NOTIMPLEMENTED() << "No onscreen support on Mac."; return nullptr; } @@ -147,6 +150,7 @@ scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( new NoOpGLSurface(size), format); #if BUILDFLAG(USE_EGL_ON_MAC) case kGLImplementationEGLGLES2: + case kGLImplementationSwiftShaderGL: if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() && size.width() == 0 && size.height() == 0) { return InitializeGLSurfaceWithFormat(new SurfacelessEGL(size), format); diff --git a/chromium/ui/gl/init/gl_initializer_mac.cc b/chromium/ui/gl/init/gl_initializer_mac.cc index 3ca3b88be3f..d58e64ff193 100644 --- a/chromium/ui/gl/init/gl_initializer_mac.cc +++ b/chromium/ui/gl/init/gl_initializer_mac.cc @@ -11,6 +11,7 @@ #include "base/base_paths.h" #include "base/files/file_path.h" #include "base/logging.h" +#include "base/mac/bundle_locations.h" #include "base/mac/foundation_util.h" #include "base/native_library.h" #include "base/path_service.h" @@ -137,23 +138,42 @@ bool InitializeStaticCGLInternal(GLImplementation implementation) { const char kGLESv2ANGLELibraryName[] = "libGLESv2.dylib"; const char kEGLANGLELibraryName[] = "libEGL.dylib"; +const char kGLESv2SwiftShaderLibraryName[] = "libswiftshader_libGLESv2.dylib"; +const char kEGLSwiftShaderLibraryName[] = "libswiftshader_libEGL.dylib"; + bool InitializeStaticEGLInternal(GLImplementation implementation) { - if (implementation == kGLImplementationSwiftShaderGL) { - return false; + // Some unit test targets depend on Angle/SwiftShader but aren't built + // as app bundles. In that case, the .dylib is next to the executable. + base::FilePath base_dir; + if (base::mac::AmIBundled()) { + base_dir = base::mac::FrameworkBundlePath().Append("Libraries/"); + } else { + if (!PathService::Get(base::FILE_EXE, &base_dir)) { + LOG(ERROR) << "PathService::Get failed."; + return false; + } + base_dir = base_dir.DirName(); } - base::FilePath module_path; - if (!PathService::Get(base::DIR_MODULE, &module_path)) { + base::FilePath glesv2_path; + base::FilePath egl_path; + if (implementation == kGLImplementationSwiftShaderGL) { +#if BUILDFLAG(ENABLE_SWIFTSHADER) + glesv2_path = base_dir.Append(kGLESv2SwiftShaderLibraryName); + egl_path = base_dir.Append(kEGLSwiftShaderLibraryName); +#else return false; +#endif + } else { + glesv2_path = base_dir.Append(kGLESv2ANGLELibraryName); + egl_path = base_dir.Append(kEGLANGLELibraryName); } - base::FilePath glesv2_path = module_path.Append(kGLESv2ANGLELibraryName); base::NativeLibrary gles_library = LoadLibraryAndPrintError(glesv2_path); if (!gles_library) { return false; } - base::FilePath egl_path = module_path.Append(kEGLANGLELibraryName); base::NativeLibrary egl_library = LoadLibraryAndPrintError(egl_path); if (!egl_library) { base::UnloadNativeLibrary(gles_library); @@ -172,9 +192,11 @@ bool InitializeStaticEGLInternal(GLImplementation implementation) { } SetGLGetProcAddressProc(get_proc_address); - AddGLNativeLibrary(egl_library); + // FIXME: SwiftShader must load symbols from libGLESv2 before libEGL on MacOS + // currently AddGLNativeLibrary(gles_library); - SetGLImplementation(kGLImplementationEGLGLES2); + AddGLNativeLibrary(egl_library); + SetGLImplementation(implementation); InitializeStaticGLBindingsGL(); InitializeStaticGLBindingsEGL(); @@ -197,6 +219,7 @@ bool InitializeGLOneOffPlatform() { return true; #if BUILDFLAG(USE_EGL_ON_MAC) case kGLImplementationEGLGLES2: + case kGLImplementationSwiftShaderGL: if (!GLSurfaceEGL::InitializeOneOff(0)) { LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; return false; @@ -229,6 +252,7 @@ bool InitializeStaticGLBindings(GLImplementation implementation) { return InitializeStaticCGLInternal(implementation); #if BUILDFLAG(USE_EGL_ON_MAC) case kGLImplementationEGLGLES2: + case kGLImplementationSwiftShaderGL: return InitializeStaticEGLInternal(implementation); #endif // BUILDFLAG(USE_EGL_ON_MAC) case kGLImplementationMockGL: diff --git a/chromium/ui/gl/yuv_to_rgb_converter.cc b/chromium/ui/gl/yuv_to_rgb_converter.cc index 73410cb1c04..bf6f8dd044e 100644 --- a/chromium/ui/gl/yuv_to_rgb_converter.cc +++ b/chromium/ui/gl/yuv_to_rgb_converter.cc @@ -14,6 +14,12 @@ namespace gl { namespace { +const char kVertexHeaderES3[] = + "#version 300 es\n" + "precision mediump float;\n" + "#define ATTRIBUTE in\n" + "#define VARYING out\n"; + const char kVertexHeaderCompatiblityProfile[] = "#version 110\n" "#define ATTRIBUTE attribute\n" @@ -24,6 +30,14 @@ const char kVertexHeaderCoreProfile[] = "#define ATTRIBUTE in\n" "#define VARYING out\n"; +const char kFragmentHeaderES3[] = + "#version 300 es\n" + "precision mediump float;\n" + "#define VARYING in\n" + "#define TEX texture\n" + "#define FRAGCOLOR frag_color\n" + "out vec4 FRAGCOLOR;\n"; + const char kFragmentHeaderCompatiblityProfile[] = "#version 110\n" "#extension GL_ARB_texture_rectangle : require\n" @@ -45,7 +59,7 @@ STRINGIZE( uniform vec2 a_texScale; VARYING vec2 v_texCoord; void main() { - gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0); + gl_Position = vec4(a_position, 0.0, 1.0); v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5 * a_texScale; } ); @@ -75,22 +89,27 @@ YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info, DCHECK(color_transform->CanGetShaderSource()); std::string do_color_conversion = color_transform->GetShaderSource(); + bool use_es3 = gl_version_info.is_es3; bool use_core_profile = gl_version_info.is_desktop_core_profile; glGenFramebuffersEXT(1, &framebuffer_); vertex_buffer_ = GLHelper::SetupQuadVertexBuffer(); vertex_shader_ = GLHelper::LoadShader( GL_VERTEX_SHADER, - base::StringPrintf("%s\n%s", - use_core_profile ? kVertexHeaderCoreProfile - : kVertexHeaderCompatiblityProfile, - kVertexShader) + base::StringPrintf( + "%s\n%s", + use_es3 ? kVertexHeaderES3 + : (use_core_profile ? kVertexHeaderCoreProfile + : kVertexHeaderCompatiblityProfile), + kVertexShader) .c_str()); fragment_shader_ = GLHelper::LoadShader( GL_FRAGMENT_SHADER, - base::StringPrintf("%s\n%s\n%s", - use_core_profile ? kFragmentHeaderCoreProfile - : kFragmentHeaderCompatiblityProfile, - do_color_conversion.c_str(), kFragmentShader) + base::StringPrintf( + "%s\n%s\n%s", + use_es3 ? kFragmentHeaderES3 + : (use_core_profile ? kFragmentHeaderCoreProfile + : kFragmentHeaderCompatiblityProfile), + do_color_conversion.c_str(), kFragmentShader) .c_str()); program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_); diff --git a/chromium/ui/keyboard/BUILD.gn b/chromium/ui/keyboard/BUILD.gn index ccf79d43925..5e5041ae56c 100644 --- a/chromium/ui/keyboard/BUILD.gn +++ b/chromium/ui/keyboard/BUILD.gn @@ -16,6 +16,8 @@ jumbo_component("keyboard") { "container_floating_behavior.h", "container_full_width_behavior.cc", "container_full_width_behavior.h", + "display_util.cc", + "display_util.h", "drag_descriptor.cc", "drag_descriptor.h", "keyboard_controller.cc", @@ -27,6 +29,8 @@ jumbo_component("keyboard") { "keyboard_layout_delegate.h", "keyboard_layout_manager.cc", "keyboard_layout_manager.h", + "keyboard_resource_util.cc", + "keyboard_resource_util.h", "keyboard_switches.cc", "keyboard_switches.h", "keyboard_ui.cc", @@ -37,11 +41,14 @@ jumbo_component("keyboard") { "notification_manager.h", "queued_container_type.cc", "queued_container_type.h", + "queued_display_change.cc", + "queued_display_change.h", ] defines = [ "KEYBOARD_IMPLEMENTATION" ] deps = [ + ":resources", "//base", "//ui/aura", "//ui/base", @@ -58,27 +65,6 @@ jumbo_component("keyboard") { if (use_ozone) { deps += [ "//ui/ozone" ] } -} - -jumbo_component("keyboard_with_content") { - sources = [ - "content/keyboard.cc", - "content/keyboard.h", - "content/keyboard_constants.cc", - "content/keyboard_constants.h", - "content/keyboard_content_util.cc", - "content/keyboard_content_util.h", - ] - - defines = [ "KEYBOARD_IMPLEMENTATION" ] - - deps = [ - ":keyboard", - ":resources", - "//base", - "//ui/base", - "//url", - ] data_deps = [ ":resources", @@ -165,11 +151,10 @@ test("keyboard_unittests") { deps = [ ":keyboard", - ":keyboard_with_content", ":test_support", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//testing/gtest", "//ui/aura:test_support", "//ui/base", diff --git a/chromium/ui/keyboard/container_behavior.h b/chromium/ui/keyboard/container_behavior.h index d641d88bff6..346ead117ee 100644 --- a/chromium/ui/keyboard/container_behavior.h +++ b/chromium/ui/keyboard/container_behavior.h @@ -39,10 +39,11 @@ class KEYBOARD_EXPORT ContainerBehavior { // Used by the layout manager to intercept any bounds setting request to // adjust the request to different bounds, if necessary. This method gets - // called at any time during the keyboard's life cycle. + // called at any time during the keyboard's life cycle. The bounds are in + // global screen coordinates. virtual const gfx::Rect AdjustSetBoundsRequest( const gfx::Rect& display_bounds, - const gfx::Rect& requested_bounds) = 0; + const gfx::Rect& requested_bounds_in_screen_coords) = 0; // Used to set the bounds to the default location. This is generally called // during initialization, but may also be have identical behavior to @@ -63,8 +64,8 @@ class KEYBOARD_EXPORT ContainerBehavior { virtual void SavePosition(const gfx::Rect& keyboard_bounds, const gfx::Size& screen_size) = 0; - virtual void HandlePointerEvent(const ui::LocatedEvent& event, - const gfx::Rect& display_bounds) = 0; + virtual bool HandlePointerEvent(const ui::LocatedEvent& event, + const display::Display& current_display) = 0; virtual ContainerType GetType() const = 0; diff --git a/chromium/ui/keyboard/container_floating_behavior.cc b/chromium/ui/keyboard/container_floating_behavior.cc index 855cdc202cf..7a0c6a03f07 100644 --- a/chromium/ui/keyboard/container_floating_behavior.cc +++ b/chromium/ui/keyboard/container_floating_behavior.cc @@ -4,6 +4,7 @@ #include "ui/keyboard/container_floating_behavior.h" +#include "ui/events/event.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" @@ -115,20 +116,20 @@ gfx::Rect ContainerFloatingBehavior::ContainKeyboardToScreenBounds( int bottom = keyboard_bounds.bottom(); // Prevent keyboard from appearing off screen or overlapping with the edge. - if (left < 0) { - left = 0; - right = keyboard_bounds.width(); + if (left < display_bounds.x()) { + left = display_bounds.x(); + right = left + keyboard_bounds.width(); } - if (right >= display_bounds.width()) { - right = display_bounds.width(); + if (right >= display_bounds.right()) { + right = display_bounds.right(); left = right - keyboard_bounds.width(); } - if (top < 0) { - top = 0; - bottom = keyboard_bounds.height(); + if (top < display_bounds.y()) { + top = display_bounds.y(); + bottom = top + keyboard_bounds.height(); } - if (bottom >= display_bounds.height()) { - bottom = display_bounds.height(); + if (bottom >= display_bounds.bottom()) { + bottom = display_bounds.bottom(); top = bottom - keyboard_bounds.height(); } @@ -162,7 +163,11 @@ gfx::Point ContainerFloatingBehavior::GetPositionForShowingKeyboard( // Make sure that this location is valid according to the current size of the // screen. - gfx::Rect keyboard_bounds = gfx::Rect(top_left_offset, keyboard_size); + gfx::Rect keyboard_bounds = + gfx::Rect(top_left_offset.x() + display_bounds.x(), + top_left_offset.y() + display_bounds.y(), keyboard_size.width(), + keyboard_size.height()); + gfx::Rect valid_keyboard_bounds = ContainKeyboardToScreenBounds(keyboard_bounds, display_bounds); @@ -175,9 +180,9 @@ bool ContainerFloatingBehavior::IsDragHandle( return draggable_area_.Contains(offset.x(), offset.y()); } -void ContainerFloatingBehavior::HandlePointerEvent( +bool ContainerFloatingBehavior::HandlePointerEvent( const ui::LocatedEvent& event, - const gfx::Rect& display_bounds) { + const display::Display& current_display) { // Cannot call UI-backed operations without a KeyboardController DCHECK(controller_); auto kb_offset = gfx::Vector2d(event.x(), event.y()); @@ -188,51 +193,90 @@ void ContainerFloatingBehavior::HandlePointerEvent( // Don't handle events if this runs in a partially initialized state. if (keyboard_bounds.height() <= 0) - return; + return false; + + ui::PointerId pointer_id = -1; + if (event.IsTouchEvent()) { + const ui::TouchEvent* te = event.AsTouchEvent(); + pointer_id = te->pointer_details().id; + } - bool handle_drag = false; const ui::EventType type = event.type(); - if (IsDragHandle(kb_offset, keyboard_bounds.size())) { - if (type == ui::ET_TOUCH_PRESSED || - (type == ui::ET_MOUSE_PRESSED && - ((const ui::MouseEvent*)&event)->IsOnlyLeftMouseButton())) { - // Drag is starting. - // Mouse events are limited to just the left mouse button. - - drag_started_by_touch_ = (type == ui::ET_TOUCH_PRESSED); - if (!drag_descriptor_) { + switch (type) { + case ui::ET_TOUCH_PRESSED: + case ui::ET_MOUSE_PRESSED: + if (!IsDragHandle(kb_offset, keyboard_bounds.size())) { + drag_descriptor_ = nullptr; + } else if (type == ui::ET_MOUSE_PRESSED && + !((const ui::MouseEvent*)&event)->IsOnlyLeftMouseButton()) { + // Mouse events are limited to just the left mouse button. + drag_descriptor_ = nullptr; + } else if (!drag_descriptor_) { // If there is no active drag descriptor, start a new one. + bool drag_started_by_touch = (type == ui::ET_TOUCH_PRESSED); drag_descriptor_.reset( - new DragDescriptor(keyboard_bounds.origin(), kb_offset)); + new DragDescriptor(keyboard_bounds.origin(), kb_offset, + drag_started_by_touch, pointer_id)); } - handle_drag = true; - } - } - if (drag_descriptor_ && - (type == ui::ET_MOUSE_DRAGGED || - (drag_started_by_touch_ && type == ui::ET_TOUCH_MOVED))) { - // Drag continues. - // If there is an active drag, use it to determine the new location - // of the keyboard. - const gfx::Point original_click_location = - drag_descriptor_->original_keyboard_location() + - drag_descriptor_->original_click_offset(); - const gfx::Point current_drag_location = - keyboard_bounds.origin() + kb_offset; - const gfx::Vector2d cumulative_drag_offset = - current_drag_location - original_click_location; - const gfx::Point new_keyboard_location = - drag_descriptor_->original_keyboard_location() + cumulative_drag_offset; - const gfx::Rect new_bounds = - gfx::Rect(new_keyboard_location, keyboard_bounds.size()); - controller_->MoveKeyboard(new_bounds); - SavePosition(container->bounds(), display_bounds.size()); - handle_drag = true; - } - if (!handle_drag && drag_descriptor_) { - // drag has ended - drag_descriptor_ = nullptr; + break; + + case ui::ET_MOUSE_DRAGGED: + case ui::ET_TOUCH_MOVED: + if (!drag_descriptor_) { + // do nothing + } else if (drag_descriptor_->is_touch_drag() != + (type == ui::ET_TOUCH_MOVED)) { + // If the event isn't of the same type that started the drag, end the + // drag to prevent confusion. + drag_descriptor_ = nullptr; + } else if (drag_descriptor_->pointer_id() != pointer_id) { + // do nothing. + } else { + // Drag continues. + // If there is an active drag, use it to determine the new location + // of the keyboard. + const gfx::Point original_click_location = + drag_descriptor_->original_keyboard_location() + + drag_descriptor_->original_click_offset(); + const gfx::Point current_drag_location = + keyboard_bounds.origin() + kb_offset; + const gfx::Vector2d cumulative_drag_offset = + current_drag_location - original_click_location; + const gfx::Point new_keyboard_location = + drag_descriptor_->original_keyboard_location() + + cumulative_drag_offset; + gfx::Rect new_bounds = + gfx::Rect(new_keyboard_location, keyboard_bounds.size()); + + DisplayUtil display_util; + const display::Display& new_display = + display_util.FindAdjacentDisplayIfPointIsNearMargin( + current_display, current_drag_location); + + if (current_display.id() == new_display.id()) { + controller_->MoveKeyboard(new_bounds); + return true; + } else { + new_bounds = + ContainKeyboardToScreenBounds(new_bounds, new_display.bounds()); + // Since the keyboard has jumped across screens, cancel the current + // drag descriptor as though the user has lifted their finger. + drag_descriptor_ = nullptr; + + // Enqueue a transition to the adjacent display. + // TODO(blakeo): pass new_bounds to display transition. + controller_->MoveToDisplayWithTransition(new_display); + return true; + } + SavePosition(container->bounds(), new_display.size()); + } + break; + + default: + drag_descriptor_ = nullptr; + break; } + return false; } void ContainerFloatingBehavior::SetCanonicalBounds( diff --git a/chromium/ui/keyboard/container_floating_behavior.h b/chromium/ui/keyboard/container_floating_behavior.h index 6ea2b2abfde..4d3b81bc103 100644 --- a/chromium/ui/keyboard/container_floating_behavior.h +++ b/chromium/ui/keyboard/container_floating_behavior.h @@ -43,14 +43,14 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior { void InitializeShowAnimationStartingState(aura::Window* container) override; const gfx::Rect AdjustSetBoundsRequest( const gfx::Rect& display_bounds, - const gfx::Rect& requested_bounds) override; + const gfx::Rect& requested_bounds_in_screen_coords) override; bool IsOverscrollAllowed() const override; bool IsDragHandle(const gfx::Vector2d& offset, const gfx::Size& keyboard_size) const override; void SavePosition(const gfx::Rect& keyboard_bounds, const gfx::Size& screen_size) override; - void HandlePointerEvent(const ui::LocatedEvent& event, - const gfx::Rect& display_bounds) override; + bool HandlePointerEvent(const ui::LocatedEvent& event, + const display::Display& current_display) override; void SetCanonicalBounds(aura::Window* container, const gfx::Rect& display_bounds) override; ContainerType GetType() const override; @@ -84,10 +84,6 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior { // Otherwise nullptr. std::unique_ptr<DragDescriptor> drag_descriptor_ = nullptr; - // Distinguish whether the current drag is from a touch event or mouse event, - // so drag/move events can be filtered accordingly - bool drag_started_by_touch_ = false; - gfx::Rect draggable_area_ = gfx::Rect(); }; diff --git a/chromium/ui/keyboard/container_floating_behavior_unittest.cc b/chromium/ui/keyboard/container_floating_behavior_unittest.cc index 6db3b404566..cc13bd678f9 100644 --- a/chromium/ui/keyboard/container_floating_behavior_unittest.cc +++ b/chromium/ui/keyboard/container_floating_behavior_unittest.cc @@ -48,6 +48,13 @@ TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequest) { workspace.height() - keyboard_height, keyboard_width, keyboard_height), result); + + // Try to move the keyboard to the center of the primary display while it's + // in a secondary display. + gfx::Rect secondary_display(1000, -200, 1200, 800); + result = floating_behavior.AdjustSetBoundsRequest(secondary_display, center); + // It gets clipped to the far left of this display + ASSERT_EQ(gfx::Rect(1000, 100, keyboard_width, keyboard_height), result); } TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequestVariousSides) { diff --git a/chromium/ui/keyboard/container_full_width_behavior.cc b/chromium/ui/keyboard/container_full_width_behavior.cc index 2dca9c087ec..53071634d9b 100644 --- a/chromium/ui/keyboard/container_full_width_behavior.cc +++ b/chromium/ui/keyboard/container_full_width_behavior.cc @@ -58,19 +58,19 @@ void ContainerFullWidthBehavior::InitializeShowAnimationStartingState( const gfx::Rect ContainerFullWidthBehavior::AdjustSetBoundsRequest( const gfx::Rect& display_bounds, - const gfx::Rect& requested_bounds) { + const gfx::Rect& requested_bounds_in_screen_coords) { gfx::Rect new_bounds; // Honors only the height of the request bounds - const int keyboard_height = requested_bounds.height(); + const int keyboard_height = requested_bounds_in_screen_coords.height(); - new_bounds.set_y(display_bounds.height() - keyboard_height); + new_bounds.set_y(display_bounds.bottom() - keyboard_height); new_bounds.set_height(keyboard_height); // If shelf is positioned on the left side of screen, x is not 0. In // FULL_WIDTH mode, the virtual keyboard should always align with the left - // edge of the screen. So manually set x to 0 here. - new_bounds.set_x(0); + // edge of the screen. So manually set x to the left side of the screen. + new_bounds.set_x(display_bounds.x()); new_bounds.set_width(display_bounds.width()); return new_bounds; @@ -93,10 +93,11 @@ bool ContainerFullWidthBehavior::IsDragHandle( return false; } -void ContainerFullWidthBehavior::HandlePointerEvent( +bool ContainerFullWidthBehavior::HandlePointerEvent( const ui::LocatedEvent& event, - const gfx::Rect& display_bounds) { + const display::Display& current_display) { // No-op. Nothing special to do for pointer events. + return false; } void ContainerFullWidthBehavior::SetCanonicalBounds( @@ -120,7 +121,10 @@ bool ContainerFullWidthBehavior::BoundsAffectWorkspaceLayout() const { } bool ContainerFullWidthBehavior::SetDraggableArea(const gfx::Rect& rect) { - return false; + // Allow extension to call this function but does nothing here. + // To avoid unnecessary exception when VK calls this function to + // clear draggable area in full width mode. + return true; } } // namespace keyboard diff --git a/chromium/ui/keyboard/container_full_width_behavior.h b/chromium/ui/keyboard/container_full_width_behavior.h index 077d3b92eb6..661d471d43a 100644 --- a/chromium/ui/keyboard/container_full_width_behavior.h +++ b/chromium/ui/keyboard/container_full_width_behavior.h @@ -35,14 +35,14 @@ class KEYBOARD_EXPORT ContainerFullWidthBehavior : public ContainerBehavior { void InitializeShowAnimationStartingState(aura::Window* container) override; const gfx::Rect AdjustSetBoundsRequest( const gfx::Rect& display_bounds, - const gfx::Rect& requested_bounds) override; + const gfx::Rect& requested_bounds_in_screen_coords) override; bool IsOverscrollAllowed() const override; bool IsDragHandle(const gfx::Vector2d& offset, const gfx::Size& keyboard_size) const override; void SavePosition(const gfx::Rect& keyboard_bounds, const gfx::Size& screen_size) override; - void HandlePointerEvent(const ui::LocatedEvent& event, - const gfx::Rect& display_bounds) override; + bool HandlePointerEvent(const ui::LocatedEvent& event, + const display::Display& current_display) override; void SetCanonicalBounds(aura::Window* container, const gfx::Rect& display_bounds) override; ContainerType GetType() const override; diff --git a/chromium/ui/keyboard/container_full_width_behavior_unittest.cc b/chromium/ui/keyboard/container_full_width_behavior_unittest.cc index 76e93843efd..eaceffb775e 100644 --- a/chromium/ui/keyboard/container_full_width_behavior_unittest.cc +++ b/chromium/ui/keyboard/container_full_width_behavior_unittest.cc @@ -18,20 +18,22 @@ class ContainerFullWidthBehaviorTest : public testing::Test { TEST(ContainerFullWidthBehaviorTest, AdjustSetBoundsRequest) { ContainerFullWidthBehavior full_width_behavior(nullptr); - gfx::Rect workspace(0, 0, 300, 200); + // workspace is not always necessarily positioned at the origin (e.g. + // secondary display). + gfx::Rect workspace(20, -30, 300, 200); gfx::Rect requested_bounds(0, 0, 10, 10); gfx::Rect result; // Ignore width. Stretch the bounds across the bottom of the screen. result = full_width_behavior.AdjustSetBoundsRequest(workspace, requested_bounds); - ASSERT_EQ(gfx::Rect(0, 190, 300, 10), result); + ASSERT_EQ(gfx::Rect(20, 160, 300, 10), result); // Even if the coordinates are non-zero, ignore them. Only use height. requested_bounds = gfx::Rect(30, 80, 20, 100); result = full_width_behavior.AdjustSetBoundsRequest(workspace, requested_bounds); - ASSERT_EQ(gfx::Rect(0, 100, 300, 100), result); + ASSERT_EQ(gfx::Rect(20, 70, 300, 100), result); } } // namespace keyboard diff --git a/chromium/ui/keyboard/content/keyboard.cc b/chromium/ui/keyboard/content/keyboard.cc deleted file mode 100644 index 4f7da4d2daa..00000000000 --- a/chromium/ui/keyboard/content/keyboard.cc +++ /dev/null @@ -1,27 +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/keyboard/content/keyboard.h" - -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "ui/base/resource/resource_bundle.h" - -namespace keyboard { - -void InitializeKeyboard() { - static bool initialized = false; - if (initialized) - return; - initialized = true; - - base::FilePath pak_dir; - PathService::Get(base::DIR_MODULE, &pak_dir); - base::FilePath pak_file = pak_dir.Append( - FILE_PATH_LITERAL("keyboard_resources.pak")); - ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( - pak_file, ui::SCALE_FACTOR_100P); -} - -} // namespace keyboard diff --git a/chromium/ui/keyboard/content/keyboard.h b/chromium/ui/keyboard/content/keyboard.h deleted file mode 100644 index 0878b7b90e7..00000000000 --- a/chromium/ui/keyboard/content/keyboard.h +++ /dev/null @@ -1,19 +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_KEYBOARD_KEYBOARD_H_ -#define UI_KEYBOARD_KEYBOARD_H_ - -#include "ui/keyboard/keyboard_export.h" - -namespace keyboard { - -// Initializes the keyboard module. This includes adding the necessary pak files -// for loading resources used in for the virtual keyboard. This becomes a no-op -// after the first call. -KEYBOARD_EXPORT void InitializeKeyboard(); - -} // namespace keyboard - -#endif // UI_KEYBOARD_KEYBOARD_H_ diff --git a/chromium/ui/keyboard/content/keyboard_constants.cc b/chromium/ui/keyboard/content/keyboard_constants.cc deleted file mode 100644 index 449f96f4127..00000000000 --- a/chromium/ui/keyboard/content/keyboard_constants.cc +++ /dev/null @@ -1,12 +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/keyboard/content/keyboard_constants.h" - -namespace keyboard { - -const char kKeyboardURL[] = "chrome://keyboard"; -const char kKeyboardHost[] = "keyboard"; - -} // namespace keyboard diff --git a/chromium/ui/keyboard/content/keyboard_constants.h b/chromium/ui/keyboard/content/keyboard_constants.h deleted file mode 100644 index 6c4e6612b11..00000000000 --- a/chromium/ui/keyboard/content/keyboard_constants.h +++ /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. - -#ifndef UI_KEYBOARD_CONTENT_KEYBOARD_CONSTANTS_H_ -#define UI_KEYBOARD_CONTENT_KEYBOARD_CONSTANTS_H_ - -#include "ui/keyboard/keyboard_export.h" - -namespace keyboard { - -// The URL of the keyboard extension. -KEYBOARD_EXPORT extern const char kKeyboardURL[]; - -// The host of the keyboard extension URL. -KEYBOARD_EXPORT extern const char kKeyboardHost[]; - -} // namespace keyboard - -#endif // UI_KEYBOARD_CONTENT_KEYBOARD_CONSTANTS_H_ diff --git a/chromium/ui/keyboard/content/keyboard_content_util.h b/chromium/ui/keyboard/content/keyboard_content_util.h deleted file mode 100644 index e96cdfe3e93..00000000000 --- a/chromium/ui/keyboard/content/keyboard_content_util.h +++ /dev/null @@ -1,33 +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_KEYBOARD_CONTENT_KEYBOARD_COTNENT_UTIL_H_ -#define UI_KEYBOARD_CONTENT_KEYBOARD_COTNENT_UTIL_H_ - -#include <stddef.h> - -#include "base/strings/string16.h" -#include "ui/keyboard/keyboard_export.h" - -class GURL; - -struct GritResourceMap; - -namespace keyboard { - -// Sets the override content url. -// This is used by for input view for extension IMEs. -KEYBOARD_EXPORT void SetOverrideContentUrl(const GURL& url); - -// Gets the override content url. -KEYBOARD_EXPORT const GURL& GetOverrideContentUrl(); - -// Get the list of keyboard resources. |size| is populated with the number of -// resources in the returned array. -KEYBOARD_EXPORT const GritResourceMap* GetKeyboardExtensionResources( - size_t* size); - -} // namespace keyboard - -#endif // UI_KEYBOARD_CONTENT_KEYBOARD_COTNENT_UTIL_H_ diff --git a/chromium/ui/keyboard/display_util.cc b/chromium/ui/keyboard/display_util.cc new file mode 100644 index 00000000000..d1431387fde --- /dev/null +++ b/chromium/ui/keyboard/display_util.cc @@ -0,0 +1,72 @@ +// 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/keyboard/display_util.h" + +#include "ui/aura/window.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" +#include "ui/display/types/display_constants.h" + +namespace { + +constexpr int kWindowMargin = 10; + +} // namespace + +namespace keyboard { + +DisplayUtil::DisplayUtil() {} + +int64_t DisplayUtil::GetNearestDisplayIdToWindow(aura::Window* window) const { + return GetNearestDisplayToWindow(window).id(); +} + +display::Display DisplayUtil::GetNearestDisplayToWindow( + aura::Window* window) const { + return display::Screen::GetScreen()->GetDisplayNearestWindow(window); +} + +display::Display DisplayUtil::FindAdjacentDisplayIfPointIsNearMargin( + const display::Display& current_display, + const gfx::Point& point_in_local) const { + const gfx::Rect current_bounds = current_display.bounds(); + + const gfx::Point point = + point_in_local + current_display.bounds().origin().OffsetFromOrigin(); + + int representative_x = point.x(); + int representative_y = point.y(); + + int current_left = current_bounds.x(); + int current_right = current_left + current_bounds.width(); + int current_top = current_bounds.y(); + int current_bottom = current_top + current_bounds.height(); + + // If the point is close to + if (point.x() - current_left <= kWindowMargin) { + representative_x = current_left - kWindowMargin; + } else if (current_right - point.x() <= kWindowMargin) { + representative_x = current_right + kWindowMargin; + } else if (point.y() - current_top <= kWindowMargin) { + representative_y = current_top - kWindowMargin; + } else if (current_bottom - point.y() <= kWindowMargin) { + representative_y = current_bottom + kWindowMargin; + } else { + return current_display; + } + + for (const display::Display& display : + display::Screen::GetScreen()->GetAllDisplays()) { + const gfx::Rect& new_bounds = display.work_area(); + if (display.touch_support() == display::Display::TouchSupport::AVAILABLE && + display.id() != current_display.id() && + new_bounds.Contains(representative_x, representative_y)) { + return display; + } + } + return current_display; +} + +} // namespace keyboard diff --git a/chromium/ui/keyboard/display_util.h b/chromium/ui/keyboard/display_util.h new file mode 100644 index 00000000000..8d4faf6dbeb --- /dev/null +++ b/chromium/ui/keyboard/display_util.h @@ -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. + +#ifndef UI_KEYBOARD_DISPLAY_UTIL_H_ +#define UI_KEYBOARD_DISPLAY_UTIL_H_ + +#include "ui/aura/window.h" +#include "ui/display/display.h" + +namespace keyboard { + +// Helper class for querying information about the screen. +class DisplayUtil { + public: + DisplayUtil(); + + int64_t GetNearestDisplayIdToWindow(aura::Window* window) const; + display::Display GetNearestDisplayToWindow(aura::Window* window) const; + display::Display FindAdjacentDisplayIfPointIsNearMargin( + const display::Display& current_display, + const gfx::Point& point) const; +}; + +} // namespace keyboard + +#endif // UI_KEYBOARD_DISPLAY_UTIL_H_ diff --git a/chromium/ui/keyboard/drag_descriptor.cc b/chromium/ui/keyboard/drag_descriptor.cc index e15d5fd5d1c..edadab99ab4 100644 --- a/chromium/ui/keyboard/drag_descriptor.cc +++ b/chromium/ui/keyboard/drag_descriptor.cc @@ -4,14 +4,19 @@ #include "ui/keyboard/drag_descriptor.h" +#include "ui/events/event.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/vector2d.h" namespace keyboard { DragDescriptor::DragDescriptor(const gfx::Point& keyboard_location, - const gfx::Vector2d& click_offset) + const gfx::Vector2d& click_offset, + bool is_touch_drag, + ui::PointerId pointer_id) : original_keyboard_location_(keyboard_location), - original_click_offset_(click_offset) {} + original_click_offset_(click_offset), + is_touch_drag_(is_touch_drag), + pointer_id_(pointer_id) {} } // namespace keyboard diff --git a/chromium/ui/keyboard/drag_descriptor.h b/chromium/ui/keyboard/drag_descriptor.h index a40ab226e5f..ff792096d76 100644 --- a/chromium/ui/keyboard/drag_descriptor.h +++ b/chromium/ui/keyboard/drag_descriptor.h @@ -5,6 +5,7 @@ #ifndef UI_KEYBOARD_DRAG_DESCRIPTOR_H_ #define UI_KEYBOARD_DRAG_DESCRIPTOR_H_ +#include "ui/events/event.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/vector2d.h" @@ -18,16 +19,28 @@ namespace keyboard { class DragDescriptor { public: DragDescriptor(const gfx::Point& keyboard_location, - const gfx::Vector2d& click_offset); + const gfx::Vector2d& click_offset, + bool is_touch_drag, + ui::PointerId pointer_id); gfx::Point original_keyboard_location() const { return original_keyboard_location_; } gfx::Vector2d original_click_offset() const { return original_click_offset_; } + bool is_touch_drag() { return is_touch_drag_; } + ui::PointerId pointer_id() { return pointer_id_; } private: const gfx::Point original_keyboard_location_; const gfx::Vector2d original_click_offset_; + + // Distinguish whether the current drag is from a touch event or mouse event, + // so drag/move events can be filtered accordingly + const bool is_touch_drag_; + + // The pointer ID provided by the touch event to disambiguate multiple + // touch points. If this is a mouse event, then this value is -1. + const ui::PointerId pointer_id_; }; } // namespace keyboard diff --git a/chromium/ui/keyboard/keyboard_controller.cc b/chromium/ui/keyboard/keyboard_controller.cc index b9ba7f519ef..9c595718d6c 100644 --- a/chromium/ui/keyboard/keyboard_controller.cc +++ b/chromium/ui/keyboard/keyboard_controller.cc @@ -23,8 +23,6 @@ #include "ui/base/ime/text_input_client.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/display/display.h" -#include "ui/display/screen.h" #include "ui/display/types/display_constants.h" #include "ui/events/base_event_utils.h" #include "ui/gfx/geometry/rect.h" @@ -33,12 +31,14 @@ #include "ui/keyboard/container_floating_behavior.h" #include "ui/keyboard/container_full_width_behavior.h" #include "ui/keyboard/container_type.h" +#include "ui/keyboard/display_util.h" #include "ui/keyboard/keyboard_controller_observer.h" #include "ui/keyboard/keyboard_layout_manager.h" #include "ui/keyboard/keyboard_ui.h" #include "ui/keyboard/keyboard_util.h" #include "ui/keyboard/notification_manager.h" #include "ui/keyboard/queued_container_type.h" +#include "ui/keyboard/queued_display_change.h" #include "ui/wm/core/window_animations.h" #if defined(OS_CHROMEOS) @@ -309,11 +309,8 @@ void KeyboardController::SetContainerBounds(const gfx::Rect& new_bounds, if (keyboard_locked()) { // Do not move the keyboard to another display after switch to an IME in // a different extension. - const int64_t display_id = - display::Screen::GetScreen() - ->GetDisplayNearestWindow(GetContainerWindow()) - .id(); - ShowKeyboardInDisplay(display_id); + ShowKeyboardInDisplay( + display_util_.GetNearestDisplayIdToWindow(GetContainerWindow())); } else { ShowKeyboard(false /* lock */); } @@ -340,6 +337,11 @@ void KeyboardController::RemoveObserver(KeyboardControllerObserver* observer) { observer_list_.RemoveObserver(observer); } +void KeyboardController::MoveToDisplayWithTransition(display::Display display) { + queued_display_change_ = std::make_unique<QueuedDisplayChange>(display); + HideKeyboard(HIDE_REASON_AUTOMATIC); +} + void KeyboardController::HideKeyboard(HideReason reason) { TRACE_EVENT0("vk", "HideKeyboard"); @@ -394,12 +396,30 @@ void KeyboardController::HideKeyboard(HideReason reason) { } void KeyboardController::HideAnimationFinished() { - if (state_ == KeyboardControllerState::HIDDEN && queued_container_type_) { - SetContainerBehaviorInternal(queued_container_type_->container_type()); - ShowKeyboard(false /* lock */); + if (state_ == KeyboardControllerState::HIDDEN) { + if (queued_container_type_) { + SetContainerBehaviorInternal(queued_container_type_->container_type()); + // The position of the container window will be adjusted shortly in + // |PopulateKeyboardContent| before showing animation, so we can set the + // passed bounds directly. + if (queued_container_type_->target_bounds()) + SetContainerBounds(queued_container_type_->target_bounds().value(), + false /* contents_loaded */); + ShowKeyboard(false /* lock */); + } + + if (queued_display_change_) { + ShowKeyboardInDisplay(queued_display_change_->new_display().id()); + queued_display_change_ = nullptr; + } } } +void KeyboardController::ShowAnimationFinished() { + MarkKeyboardLoadFinished(); + NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea(); +} + void KeyboardController::SetContainerBehaviorInternal( const ContainerType type) { switch (type) { @@ -642,8 +662,9 @@ void KeyboardController::PopulateKeyboardContent(int64_t display_id, ui_->ShowKeyboardContainer(container_.get()); - animation_observer_ = std::make_unique<CallbackAnimationObserver>( - base::BindOnce(&MarkKeyboardLoadFinished)); + animation_observer_ = + std::make_unique<CallbackAnimationObserver>(base::BindOnce( + &KeyboardController::ShowAnimationFinished, base::Unretained(this))); ui::ScopedLayerAnimationSettings settings(container_animator); settings.AddObserver(animation_observer_.get()); @@ -654,7 +675,6 @@ void KeyboardController::PopulateKeyboardContent(int64_t display_id, queued_container_type_ = nullptr; ChangeState(KeyboardControllerState::SHOWN); - NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea(); } bool KeyboardController::WillHideKeyboard() const { @@ -772,14 +792,15 @@ bool KeyboardController::IsOverscrollAllowed() const { return container_behavior_->IsOverscrollAllowed(); } -void KeyboardController::HandlePointerEvent(const ui::LocatedEvent& event) { - container_behavior_->HandlePointerEvent( - event, container_->GetRootWindow()->bounds()); +bool KeyboardController::HandlePointerEvent(const ui::LocatedEvent& event) { + const display::Display& current_display = + display_util_.GetNearestDisplayToWindow(container_->GetRootWindow()); + return container_behavior_->HandlePointerEvent(event, current_display); } - void KeyboardController::SetContainerType( const ContainerType type, + base::Optional<gfx::Rect> target_bounds, base::OnceCallback<void(bool)> callback) { if (container_behavior_->GetType() == type) { std::move(callback).Run(false); @@ -789,13 +810,15 @@ void KeyboardController::SetContainerType( if (state_ == KeyboardControllerState::SHOWN) { // Keyboard is already shown. Hiding the keyboard at first then switching // container type. - queued_container_type_ = - std::make_unique<QueuedContainerType>(this, type, std::move(callback)); + queued_container_type_ = std::make_unique<QueuedContainerType>( + this, type, target_bounds, std::move(callback)); HideKeyboard(HIDE_REASON_AUTOMATIC); } else { // Keyboard is hidden. Switching the container type immediately and invoking // the passed callback now. SetContainerBehaviorInternal(type); + if (target_bounds) + SetContainerBounds(target_bounds.value(), false /* contents_loaded */); DCHECK(GetActiveContainerType() == type); std::move(callback).Run(true /* change_successful */); } diff --git a/chromium/ui/keyboard/keyboard_controller.h b/chromium/ui/keyboard/keyboard_controller.h index ef9356317a4..1b3d2ceb125 100644 --- a/chromium/ui/keyboard/keyboard_controller.h +++ b/chromium/ui/keyboard/keyboard_controller.h @@ -18,12 +18,14 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/keyboard/container_behavior.h" #include "ui/keyboard/container_type.h" +#include "ui/keyboard/display_util.h" #include "ui/keyboard/keyboard_event_filter.h" #include "ui/keyboard/keyboard_export.h" #include "ui/keyboard/keyboard_layout_delegate.h" #include "ui/keyboard/keyboard_util.h" #include "ui/keyboard/notification_manager.h" #include "ui/keyboard/queued_container_type.h" +#include "ui/keyboard/queued_display_change.h" namespace aura { class Window; @@ -168,7 +170,7 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // Handle mouse and touch events on the keyboard. The effects of this method // will not stop propagation to the keyboard extension. - void HandlePointerEvent(const ui::LocatedEvent& event); + bool HandlePointerEvent(const ui::LocatedEvent& event); // Moves an already loaded keyboard. void MoveKeyboard(const gfx::Rect new_bounds); @@ -177,11 +179,14 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // will trigger a hide animation and a subsequent show animation. Otherwise // the ContainerBehavior change is synchronous. void SetContainerType(const ContainerType type, + base::Optional<gfx::Rect> target_bounds, base::OnceCallback<void(bool)> callback); // Sets floating keyboard drggable rect. bool SetDraggableArea(const gfx::Rect& rect); + void MoveToDisplayWithTransition(display::Display display); + private: // For access to Observer methods for simulation. friend class KeyboardControllerTest; @@ -224,8 +229,10 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // Returns true if keyboard is scheduled to hide. bool WillHideKeyboard() const; - // Called when the hide animation finishes. + // Called when the hide animation finished. void HideAnimationFinished(); + // Called when the show animation finished. + void ShowAnimationFinished(); void NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea(); @@ -264,6 +271,7 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, std::unique_ptr<ContainerBehavior> container_behavior_; std::unique_ptr<QueuedContainerType> queued_container_type_; + std::unique_ptr<QueuedDisplayChange> queued_display_change_; // If true, show the keyboard window when keyboard UI content updates. bool show_on_content_update_; @@ -285,6 +293,8 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, base::Time time_of_last_blur_ = base::Time::UnixEpoch(); + DisplayUtil display_util_; + static KeyboardController* instance_; base::WeakPtrFactory<KeyboardController> weak_factory_report_lingering_state_; diff --git a/chromium/ui/keyboard/keyboard_controller_unittest.cc b/chromium/ui/keyboard/keyboard_controller_unittest.cc index 8ecafe94069..45793d12b79 100644 --- a/chromium/ui/keyboard/keyboard_controller_unittest.cc +++ b/chromium/ui/keyboard/keyboard_controller_unittest.cc @@ -8,7 +8,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" #include "build/build_config.h" @@ -385,45 +384,6 @@ TEST_F(KeyboardControllerTest, KeyboardSize) { VerifyKeyboardWindowSize(container, keyboard); } -// Flaky on Windows. See http://crbug.com/757044 -#if defined(OS_WIN) -#define MAYBE_KeyboardSizeMultiRootWindow DISABLED_KeyboardSizeMultiRootWindow -#else -#define MAYBE_KeyboardSizeMultiRootWindow KeyboardSizeMultiRootWindow -#endif - -TEST_F(KeyboardControllerTest, MAYBE_KeyboardSizeMultiRootWindow) { - aura::Window* container(controller()->GetContainerWindow()); - aura::Window* keyboard(ui()->GetContentsWindow()); - gfx::Rect screen_bounds = root_window()->bounds(); - root_window()->AddChild(container); - container->AddChild(keyboard); - const gfx::Rect& initial_bounds = container->bounds(); - // The container should be positioned at the bottom of screen and has 0 - // height. - ASSERT_EQ(0, initial_bounds.height()); - ASSERT_EQ(screen_bounds.height(), initial_bounds.y()); - VerifyKeyboardWindowSize(container, keyboard); - - // Adding new root window. - std::unique_ptr<aura::WindowTreeHost> secondary_tree_host = - base::WrapUnique<aura::WindowTreeHost>( - aura::WindowTreeHost::Create(gfx::Rect(0, 0, 1000, 500))); - secondary_tree_host->InitHost(); - EXPECT_EQ(1000, secondary_tree_host->window()->bounds().width()); - EXPECT_EQ(500, secondary_tree_host->window()->bounds().height()); - - // Move the keyboard into the secondary root window. - controller()->HideKeyboard( - KeyboardController::HideReason::HIDE_REASON_AUTOMATIC); - root_window()->RemoveChild(container); - secondary_tree_host->window()->AddChild(container); - - const gfx::Rect& new_bounds = container->bounds(); - EXPECT_EQ(500, new_bounds.y()); - VerifyKeyboardWindowSize(container, keyboard); -} - // Tests that tapping/clicking inside the keyboard does not give it focus. TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) { ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; @@ -709,11 +669,11 @@ TEST_F(KeyboardControllerAnimationTest, ContainerAnimation) { gfx::Transform transform; transform.Translate(0, keyboard::kFullWidthKeyboardAnimationDistance); EXPECT_EQ(transform, layer->transform()); - // animation occurs in a cloned layer, so the actual final bounds should - // already be applied to the container. - EXPECT_EQ(keyboard_container()->bounds(), notified_visible_bounds()); - EXPECT_EQ(keyboard_container()->bounds(), notified_occluding_bounds()); - EXPECT_TRUE(notified_is_available()); + // Actual final bounds should be notified after animation finishes to avoid + // flash of background being seen. + EXPECT_EQ(gfx::Rect(), notified_visible_bounds()); + EXPECT_EQ(gfx::Rect(), notified_occluding_bounds()); + EXPECT_FALSE(notified_is_available()); RunAnimationForLayer(layer); EXPECT_TRUE(keyboard_container()->IsVisible()); @@ -751,7 +711,7 @@ TEST_F(KeyboardControllerAnimationTest, ContainerAnimation) { EXPECT_FALSE(notified_is_available()); SetModeCallbackInvocationCounter invocation_counter; - controller()->SetContainerType(ContainerType::FLOATING, + controller()->SetContainerType(ContainerType::FLOATING, base::nullopt, invocation_counter.GetInvocationCallback()); EXPECT_EQ(1, invocation_counter.invocation_count_for_status(true)); EXPECT_EQ(0, invocation_counter.invocation_count_for_status(false)); @@ -767,12 +727,44 @@ TEST_F(KeyboardControllerAnimationTest, ContainerAnimation) { // callback should do nothing when container mode is set to the current active // container type. An unnecessary call gets registered synchronously as a // failure status to the callback. - controller()->SetContainerType(ContainerType::FLOATING, + controller()->SetContainerType(ContainerType::FLOATING, base::nullopt, invocation_counter.GetInvocationCallback()); EXPECT_EQ(1, invocation_counter.invocation_count_for_status(true)); EXPECT_EQ(1, invocation_counter.invocation_count_for_status(false)); } +TEST_F(KeyboardControllerAnimationTest, ChangeContainerModeWithBounds) { + ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; + SetModeCallbackInvocationCounter invocation_counter; + + ui::Layer* layer = keyboard_container()->layer(); + ShowKeyboard(); + RunAnimationForLayer(layer); + EXPECT_EQ(ContainerType::FULL_WIDTH, controller()->GetActiveContainerType()); + EXPECT_TRUE(keyboard_container()->IsVisible()); + EXPECT_TRUE(contents_window()->IsVisible()); + + // Changing the mode to another mode invokes hiding + showing. + const gfx::Rect target_bounds(0, 0, 1200, 600); + controller()->SetContainerType(ContainerType::FLOATING, + base::make_optional(target_bounds), + invocation_counter.GetInvocationCallback()); + // The container window shouldn't be resized until it's hidden even if the + // target bounds is passed to |SetContainerType|. + EXPECT_EQ(gfx::Rect(), notified_visible_bounds()); + EXPECT_EQ(0, invocation_counter.invocation_count_for_status(true)); + EXPECT_EQ(0, invocation_counter.invocation_count_for_status(false)); + RunAnimationForLayer(layer); + // Hiding animation finished. The container window should be resized to the + // target bounds. + EXPECT_EQ(keyboard_container()->bounds().size(), target_bounds.size()); + // Then showing animation automatically start. + layer = keyboard_container()->layer(); + RunAnimationForLayer(layer); + EXPECT_EQ(1, invocation_counter.invocation_count_for_status(true)); + EXPECT_EQ(0, invocation_counter.invocation_count_for_status(false)); +} + // Show keyboard during keyboard hide animation should abort the hide animation // and the keyboard should animate in. // Test for crbug.com/333284. diff --git a/chromium/ui/keyboard/keyboard_event_filter.cc b/chromium/ui/keyboard/keyboard_event_filter.cc index 47dd5040809..25ea969b750 100644 --- a/chromium/ui/keyboard/keyboard_event_filter.cc +++ b/chromium/ui/keyboard/keyboard_event_filter.cc @@ -23,17 +23,17 @@ void KeyboardEventFilter::OnGestureEvent(ui::GestureEvent* event) { } void KeyboardEventFilter::OnMouseEvent(ui::MouseEvent* event) { - ProcessPointerEvent(*event); + ProcessPointerEvent(event); } void KeyboardEventFilter::OnTouchEvent(ui::TouchEvent* event) { - ProcessPointerEvent(*event); + ProcessPointerEvent(event); } -void KeyboardEventFilter::ProcessPointerEvent(const ui::LocatedEvent& event) { +void KeyboardEventFilter::ProcessPointerEvent(ui::LocatedEvent* event) { KeyboardController* controller = KeyboardController::GetInstance(); - if (controller) - controller->HandlePointerEvent(event); + if (controller && controller->HandlePointerEvent(*event)) + event->SetHandled(); } } // nemespace keyboard diff --git a/chromium/ui/keyboard/keyboard_event_filter.h b/chromium/ui/keyboard/keyboard_event_filter.h index 3be7e45e20d..d3201328f13 100644 --- a/chromium/ui/keyboard/keyboard_event_filter.h +++ b/chromium/ui/keyboard/keyboard_event_filter.h @@ -25,7 +25,7 @@ class KEYBOARD_EXPORT KeyboardEventFilter : public ui::EventHandler { void OnMouseEvent(ui::MouseEvent* event) override; private: - void ProcessPointerEvent(const ui::LocatedEvent& event); + void ProcessPointerEvent(ui::LocatedEvent* event); DISALLOW_COPY_AND_ASSIGN(KeyboardEventFilter); }; diff --git a/chromium/ui/keyboard/keyboard_layout_manager.cc b/chromium/ui/keyboard/keyboard_layout_manager.cc index 8323fa1108b..237291fb70b 100644 --- a/chromium/ui/keyboard/keyboard_layout_manager.cc +++ b/chromium/ui/keyboard/keyboard_layout_manager.cc @@ -5,6 +5,7 @@ #include "ui/keyboard/keyboard_layout_manager.h" #include "ui/compositor/layer_animator.h" +#include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/keyboard/keyboard_controller.h" #include "ui/keyboard/keyboard_util.h" @@ -36,20 +37,29 @@ void KeyboardLayoutManager::SetChildBounds(aura::Window* child, // resized and covers the container window. Note the contents' bound is only // set in OnWindowResized. - const aura::Window* root_window = + aura::Window* root_window = controller_->GetContainerWindow()->GetRootWindow(); // If the keyboard has been deactivated, this reference will be null. if (!root_window) return; - const gfx::Rect new_bounds = controller_->AdjustSetBoundsRequest( - root_window->bounds(), requested_bounds); + DisplayUtil display_util; + const display::Display& display = + display_util.GetNearestDisplayToWindow(root_window); + const gfx::Vector2d display_offset = + display.bounds().origin().OffsetFromOrigin(); + + const gfx::Rect new_bounds = + controller_->AdjustSetBoundsRequest(display.bounds(), + requested_bounds + display_offset) - + display_offset; // Containar bounds should only be reset when the contents window bounds // actually change. Otherwise it interrupts the initial animation of showing // the keyboard. Described in crbug.com/356753. gfx::Rect old_bounds = contents_window_->GetTargetBounds(); + aura::Window::ConvertRectToTarget(contents_window_, root_window, &old_bounds); if (new_bounds == old_bounds) return; diff --git a/chromium/ui/keyboard/content/keyboard_content_util.cc b/chromium/ui/keyboard/keyboard_resource_util.cc index aec8659d455..70beddcd9c6 100644 --- a/chromium/ui/keyboard/content/keyboard_content_util.cc +++ b/chromium/ui/keyboard/keyboard_resource_util.cc @@ -1,30 +1,20 @@ -// Copyright 2015 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/keyboard/content/keyboard_content_util.h" +#include "ui/keyboard/keyboard_resource_util.h" -#include "base/lazy_instance.h" +#include "base/files/file_path.h" #include "base/macros.h" +#include "base/path_service.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/keyboard/grit/keyboard_resources.h" #include "ui/keyboard/grit/keyboard_resources_map.h" -#include "url/gurl.h" namespace keyboard { -namespace { -base::LazyInstance<GURL>::DestructorAtExit g_override_content_url = - LAZY_INSTANCE_INITIALIZER; - -} // namespace - -void SetOverrideContentUrl(const GURL& url) { - g_override_content_url.Get() = url; -} - -const GURL& GetOverrideContentUrl() { - return g_override_content_url.Get(); -} +const char kKeyboardURL[] = "chrome://keyboard"; +const char kKeyboardHost[] = "keyboard"; const GritResourceMap* GetKeyboardExtensionResources(size_t* size) { // This looks a lot like the contents of a resource map; however it is @@ -107,4 +97,18 @@ const GritResourceMap* GetKeyboardExtensionResources(size_t* size) { return kKeyboardResources; } +void InitializeKeyboardResources() { + static bool initialized = false; + if (initialized) + return; + initialized = true; + + base::FilePath pak_dir; + PathService::Get(base::DIR_MODULE, &pak_dir); + base::FilePath pak_file = + pak_dir.Append(FILE_PATH_LITERAL("keyboard_resources.pak")); + ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( + pak_file, ui::SCALE_FACTOR_100P); +} + } // namespace keyboard diff --git a/chromium/ui/keyboard/keyboard_resource_util.h b/chromium/ui/keyboard/keyboard_resource_util.h new file mode 100644 index 00000000000..b10dcc575fe --- /dev/null +++ b/chromium/ui/keyboard/keyboard_resource_util.h @@ -0,0 +1,34 @@ +// 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_KEYBOARD_KEYBOARD_RESOURCE_UTIL_H_ +#define UI_KEYBOARD_KEYBOARD_RESOURCE_UTIL_H_ + +#include <stddef.h> + +#include "ui/keyboard/keyboard_export.h" + +struct GritResourceMap; + +namespace keyboard { + +// The URL of the keyboard extension. +KEYBOARD_EXPORT extern const char kKeyboardURL[]; + +// The host of the keyboard extension URL. +KEYBOARD_EXPORT extern const char kKeyboardHost[]; + +// Get the list of keyboard resources. |size| is populated with the number of +// resources in the returned array. +KEYBOARD_EXPORT const GritResourceMap* GetKeyboardExtensionResources( + size_t* size); + +// Initializes the keyboard module. This includes adding the necessary pak files +// for loading resources used in for the virtual keyboard. This becomes a no-op +// after the first call. +KEYBOARD_EXPORT void InitializeKeyboardResources(); + +} // namespace keyboard + +#endif // UI_KEYBOARD_KEYBOARD_RESOURCE_UTIL_H_ diff --git a/chromium/ui/keyboard/keyboard_resources.grd b/chromium/ui/keyboard/keyboard_resources.grd index 60ee41edaf0..91ca2ba2c94 100644 --- a/chromium/ui/keyboard/keyboard_resources.grd +++ b/chromium/ui/keyboard/keyboard_resources.grd @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- NOTE: if you are adding resources here, you should probably also edit: - ui/keyboard/keyboard_ui_controller.cc - ui/keyboard/keyboard_util.cc + ui/keyboard/keyboard_resource_util.cc --> <grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> <outputs> diff --git a/chromium/ui/keyboard/keyboard_util_unittest.cc b/chromium/ui/keyboard/keyboard_util_unittest.cc index bcbc714b6ef..a4f4eb8a28a 100644 --- a/chromium/ui/keyboard/keyboard_util_unittest.cc +++ b/chromium/ui/keyboard/keyboard_util_unittest.cc @@ -5,7 +5,6 @@ #include "ui/keyboard/keyboard_util.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/keyboard/keyboard_controller.h" #include "ui/keyboard/keyboard_test_util.h" diff --git a/chromium/ui/keyboard/queued_container_type.cc b/chromium/ui/keyboard/queued_container_type.cc index 2cf7b882e24..d876b45c1ab 100644 --- a/chromium/ui/keyboard/queued_container_type.cc +++ b/chromium/ui/keyboard/queued_container_type.cc @@ -10,9 +10,11 @@ namespace keyboard { QueuedContainerType::QueuedContainerType( KeyboardController* controller, ContainerType container_type, + base::Optional<gfx::Rect> bounds, base::OnceCallback<void(bool success)> callback) : controller_(controller), container_type_(container_type), + bounds_(bounds), callback_(std::move(callback)){}; QueuedContainerType::~QueuedContainerType() { diff --git a/chromium/ui/keyboard/queued_container_type.h b/chromium/ui/keyboard/queued_container_type.h index ce58d930e28..aa678cd9ec6 100644 --- a/chromium/ui/keyboard/queued_container_type.h +++ b/chromium/ui/keyboard/queued_container_type.h @@ -6,6 +6,8 @@ #define UI_KEYBOARD_QUEUED_CONTAINER_TYPE_H_ #include "base/bind.h" +#include "base/optional.h" +#include "ui/gfx/geometry/rect.h" #include "ui/keyboard/container_type.h" namespace keyboard { @@ -22,13 +24,16 @@ class QueuedContainerType { public: QueuedContainerType(KeyboardController* controller, ContainerType container_type, + base::Optional<gfx::Rect> bounds, base::OnceCallback<void(bool success)> callback); ~QueuedContainerType(); ContainerType container_type() { return container_type_; } + base::Optional<gfx::Rect> target_bounds() { return bounds_; } private: KeyboardController* controller_; ContainerType container_type_; + base::Optional<gfx::Rect> bounds_; base::OnceCallback<void(bool success)> callback_; }; diff --git a/chromium/ui/keyboard/queued_display_change.cc b/chromium/ui/keyboard/queued_display_change.cc new file mode 100644 index 00000000000..ccc1d2f1973 --- /dev/null +++ b/chromium/ui/keyboard/queued_display_change.cc @@ -0,0 +1,17 @@ +// 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/keyboard/queued_display_change.h" +#include "base/bind.h" +#include "ui/display/display.h" +#include "ui/keyboard/keyboard_controller.h" + +namespace keyboard { + +QueuedDisplayChange::QueuedDisplayChange(const display::Display& display) + : new_display_(display){}; + +QueuedDisplayChange::~QueuedDisplayChange(){}; + +} // namespace keyboard diff --git a/chromium/ui/keyboard/queued_display_change.h b/chromium/ui/keyboard/queued_display_change.h new file mode 100644 index 00000000000..527a0df98aa --- /dev/null +++ b/chromium/ui/keyboard/queued_display_change.h @@ -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. + +#ifndef UI_KEYBOARD_QUEUED_DISPLAY_CHANGE_H_ +#define UI_KEYBOARD_QUEUED_DISPLAY_CHANGE_H_ + +#include "base/bind.h" +#include "base/optional.h" +#include "ui/display/display.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/keyboard/container_type.h" + +namespace keyboard { + +// TODO: refactor and merge this into QueuedContainerType and rename it to +// something like QueuedVisualChange or similar. +class QueuedDisplayChange { + public: + QueuedDisplayChange(const display::Display& display_info); + ~QueuedDisplayChange(); + + display::Display new_display() { return new_display_; } + + private: + display::Display new_display_; +}; + +} // namespace keyboard + +#endif // UI_KEYBOARD_QUEUED_DISPLAY_CHANGE_H_ diff --git a/chromium/ui/latency/BUILD.gn b/chromium/ui/latency/BUILD.gn index e5abf3bd958..2aeebd991ae 100644 --- a/chromium/ui/latency/BUILD.gn +++ b/chromium/ui/latency/BUILD.gn @@ -7,11 +7,19 @@ import("//testing/test.gni") jumbo_source_set("latency") { sources = [ + "fixed_point.cc", + "fixed_point.h", + "histograms.cc", + "histograms.h", "latency_histogram_macros.h", "latency_info.cc", "latency_info.h", "latency_tracker.cc", "latency_tracker.h", + "stream_analyzer.cc", + "stream_analyzer.h", + "windowed_analyzer.cc", + "windowed_analyzer.h", ] deps = [ @@ -37,7 +45,13 @@ jumbo_source_set("test_support") { test("latency_unittests") { sources = [ + "fixed_point_unittest.cc", + "frame_metrics_test_common.cc", + "frame_metrics_test_common.h", + "histograms_unittest.cc", "latency_info_unittest.cc", + "stream_analyzer_unittest.cc", + "windowed_analyzer_unittest.cc", ] deps = [ @@ -63,3 +77,21 @@ test("latency_unittests") { ] } } + +test("latency_perftests") { + sources = [ + "frame_metrics_test_common.cc", + "frame_metrics_test_common.h", + "histograms_perftest.cc", + ] + + deps = [ + ":latency", + "//base", + "//base/test:test_support", + "//mojo/edk/test:run_all_unittests", + "//testing/gmock", + "//testing/gtest", + "//testing/perf", + ] +} diff --git a/chromium/ui/latency/OWNERS b/chromium/ui/latency/OWNERS index b29cc1a125a..691efe52575 100644 --- a/chromium/ui/latency/OWNERS +++ b/chromium/ui/latency/OWNERS @@ -1 +1,5 @@ +# latency info tdresser@chromium.org + +# frame metrics +brianderson@chromium.org diff --git a/chromium/ui/latency/fixed_point.cc b/chromium/ui/latency/fixed_point.cc new file mode 100644 index 00000000000..fafcf9ee85e --- /dev/null +++ b/chromium/ui/latency/fixed_point.cc @@ -0,0 +1,61 @@ +// 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/latency/fixed_point.h" + +#include <cmath> +#include <limits> + +#include "base/logging.h" + +namespace ui { +namespace frame_metrics { + +namespace { + +constexpr uint64_t k2Pow32{1ULL << 32}; +constexpr uint64_t k32LsbMask{0xFFFFFFFF}; + +} // namespace + +Accumulator96b::Accumulator96b(uint32_t value_to_square, uint32_t weight) { + uint64_t square = static_cast<uint64_t>(value_to_square) * value_to_square; + uint64_t ms64b_temp = (square >> 32) * weight; + uint64_t ls32b_temp = (square & k32LsbMask) * weight; + ms64b = ms64b_temp + (ls32b_temp >> 32); + ls32b = ls32b_temp & k32LsbMask; +} + +void Accumulator96b::Add(const Accumulator96b& rhs) { + uint64_t ls32b_temp = static_cast<uint64_t>(ls32b) + rhs.ls32b; + DCHECK_LT((ls32b_temp >> 32), + std::numeric_limits<decltype(ms64b)>::max() - rhs.ms64b) + << "Accumulator96b overflow."; + uint64_t ms64b_add = rhs.ms64b + (ls32b_temp >> 32); + DCHECK_LT(ms64b_add, std::numeric_limits<decltype(ms64b)>::max() - ms64b) + << "Accumulator96b overflow."; + ms64b += ms64b_add; + ls32b = ls32b_temp & k32LsbMask; +} + +void Accumulator96b::Subtract(const Accumulator96b& rhs) { + uint64_t ls32b_temp = ls32b; + if (ls32b < rhs.ls32b) { + // Borrow from ms64b to ls32b. + ms64b--; + ls32b_temp |= k2Pow32; + } + DCHECK_GE(ms64b, rhs.ms64b) << "Accumulator96b underflow."; + DCHECK_GE(ls32b_temp, static_cast<uint64_t>(rhs.ls32b)) + << "Accumulator96b underflow."; + ms64b -= rhs.ms64b; + ls32b = ls32b_temp - rhs.ls32b; +} + +double Accumulator96b::ToDouble() const { + return (static_cast<double>(ms64b) * k2Pow32) + ls32b; +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/fixed_point.h b/chromium/ui/latency/fixed_point.h new file mode 100644 index 00000000000..11b419116b3 --- /dev/null +++ b/chromium/ui/latency/fixed_point.h @@ -0,0 +1,71 @@ +// 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_FRAME_METRICS_FIXED_POINT_H_ +#define UI_FRAME_METRICS_FIXED_POINT_H_ + +#include <cstdint> + +#include "base/macros.h" + +namespace ui { +namespace frame_metrics { + +// Use fixed point math so we can manage our precision explicitly and avoid +// accumulating error in our windowed accumulators. +// The 64-bit accumulators reserve 32-bits for weights, and 32-bits for values. +// The 32-bit values reserve 16 bits before and after the radix point. +constexpr int kFixedPointShift = 16; +constexpr int64_t kFixedPointMultiplier{1LL << kFixedPointShift}; + +// kFixedPointRootMultiplier is used to shift the bits before taking the square +// root and undoing that shift after squaring in the SMR calculation. +constexpr int kFixedPointRootShift = 32; +constexpr int64_t kFixedPointRootMultiplier{1LL << kFixedPointRootShift}; +constexpr int64_t kFixedPointRootMultiplierSqrt{1LL + << (kFixedPointRootShift / 2)}; + +// We need a huge range to accumulate values for RMS calculations, which +// need double the range internally compared to the range we are targeting +// after taking the square root of the accumulation. +// This efficiently emulates a 96-bit unsigned integer with weighted +// accumulation operations. +// 32-bits are reserved for weights and 64-bits for squared values. +// Overflow or underflow indicates something is seriously wrong with the higher +// level metrics logic, so this class will DCHECK if it anticipates overflow +// or underflow: +// * It doesn't need to support OVERFLOW since the frame metric classes will +// always reset the entire accumulator before the accumulated weights +// overflow. The accumulated weights correspond to a maximum of the number of +// microseconds since the last reset, which for a 32-bit weight is about +// 1 hour. We will gather and reset results much more often than every hour. +// * It doesn't need to support UNDERFLOW since only the windowed metrics use +// Subtract, and those only subtract values it has already added. +class Accumulator96b { + public: + Accumulator96b() = default; + Accumulator96b(uint32_t value_to_square, uint32_t weight); + + void Add(const Accumulator96b& rhs); + void Subtract(const Accumulator96b& rhs); + double ToDouble() const; + + public: + uint64_t ms64b{0}; + uint32_t ls32b{0}; +}; + +// Convenience function overloads for AsDouble, to help with templated code. +inline double AsDouble(const Accumulator96b& value) { + return value.ToDouble(); +} + +inline double AsDouble(double value) { + return value; +} + +} // namespace frame_metrics +} // namespace ui + +#endif // UI_FRAME_METRICS_FIXED_POINT_H_ diff --git a/chromium/ui/latency/fixed_point_unittest.cc b/chromium/ui/latency/fixed_point_unittest.cc new file mode 100644 index 00000000000..02548fd03da --- /dev/null +++ b/chromium/ui/latency/fixed_point_unittest.cc @@ -0,0 +1,149 @@ +// 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/latency/fixed_point.h" + +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ui { +namespace frame_metrics { + +// Verify range of a fixed point value stored as a uint32_t has enough range +// for our requirements. +TEST(FrameMetricsFixedPointTest, kFixedPointMultiplier) { + uint32_t max_fixed = std::numeric_limits<uint32_t>::max(); + double max_float = static_cast<double>(max_fixed) / kFixedPointMultiplier; + + // The maximum time delta between two frames we'd like to support. + double frame_delta = 64 * base::TimeTicks::kMicrosecondsPerSecond; + + // The minimum frame duration we'd like to support. + // 1kHz should give us plenty of headroom. + double frame_duration = base::TimeTicks::kMicrosecondsPerSecond / 1000; + + // Verify the resulting slope is within the range. + double frame_slope = frame_delta / frame_duration; + EXPECT_LE(frame_slope, max_float); +} + +// Some code will take the square root of a 32-bit value by shifting it left +// 32-bits beforehand. Verify this is okay and more accurate than not shifting +// at all. +TEST(FrameMetricsFixedPointTest, kFixedPointRootMultiplier) { + uint64_t value = 0xFFFFFFFF; + + // Calculate SMR with kFixedPointRootMultiplier. + // Truncate to 32 bits to verify multiplying by kFixedPointRootMultiplier + // will not result in overflow when stored as a 32 bit value. + uint32_t root1 = std::sqrt(value * kFixedPointRootMultiplier); + double value1 = + static_cast<uint64_t>(root1) * root1 / kFixedPointRootMultiplier; + double error1 = std::abs(value1 - value); + + // Calculate SMR without kFixedPointRootMultiplier. + uint32_t root2 = std::sqrt(value); + double value2 = root2 * root2; + double error2 = std::abs(value2 - value); + + // Verify using kFixedPointRootMultiplier is relatively more accurate. + EXPECT_LE(error1, error2); + + // Verify using kFixedPointRootMultiplier is accurate in an absolute sense. + EXPECT_LE(error1, 1); +} + +TEST(FrameMetricsFixedPointTest, kFixedPointRootMultiplierSqrt) { + EXPECT_EQ(kFixedPointRootMultiplierSqrt, + std::sqrt(kFixedPointRootMultiplier)); +} + +TEST(FrameMetricsFixedPointTest, kFixedPointRootShift) { + EXPECT_EQ(kFixedPointRootMultiplier, 1LL << kFixedPointRootShift); +} + +// Verify Accumulator96b's squared weight constructor. +TEST(FrameMetricsFixedPointTest, Accumulator96bConstructor) { + // A small value that fits in 32 bits. + uint64_t a = 13; + Accumulator96b a1(a, 2); + EXPECT_DOUBLE_EQ(a1.ToDouble(), a * a * 2); + + // A "medium" value that fits in 64 bits. + uint64_t b = 0x10000001; + Accumulator96b a2(b, 2); + EXPECT_DOUBLE_EQ(a2.ToDouble(), b * b * 2); + + // A large value that fits in 96 bits. + uint64_t c = 0x80000001; + Accumulator96b a3(c, c); + EXPECT_DOUBLE_EQ(a3.ToDouble(), std::pow(c, 3)); + + // The largest initial 96-bit value. + uint64_t d = 0xFFFFFFFF; + Accumulator96b a4(d, d); + EXPECT_DOUBLE_EQ(a4.ToDouble(), std::pow(d, 3)); + + // A mix of the two above. + double cf = c; + double df = d; + Accumulator96b a5(c, d); + EXPECT_DOUBLE_EQ(a5.ToDouble(), cf * cf * df); + Accumulator96b a6(d, c); + EXPECT_DOUBLE_EQ(a6.ToDouble(), df * df * cf); +} + +// Verify Accumulator96b::Add and Subtract. +TEST(FrameMetricsFixedPointTest, Accumulator96bAddSub) { + uint32_t v = 0xFFFFFFFF; + + // A small value that fits in 32 bits and would carry into + // upper most 64 bits during accumulation. + Accumulator96b a1(1, v); + Accumulator96b accum1; + for (int i = 0; i <= 0xFF; i++) { + accum1.Add(a1); + EXPECT_DOUBLE_EQ(accum1.ToDouble(), static_cast<double>(v) * (i + 1)); + } + for (int i = 0xFF; i >= 0; i--) { + accum1.Subtract(a1); + EXPECT_DOUBLE_EQ(accum1.ToDouble(), static_cast<double>(v) * i); + } + + // A larger value that fits in 64 bits and would carry into + // upper most 32 bits during accumulation. + Accumulator96b a2(v, 1); + Accumulator96b accum2; + for (int i = 0; i <= 0xFF; i++) { + accum2.Add(a2); + EXPECT_DOUBLE_EQ(accum2.ToDouble(), static_cast<double>(v) * v * (i + 1)); + } + for (int i = 0xFF; i >= 0; i--) { + accum2.Subtract(a2); + EXPECT_DOUBLE_EQ(accum2.ToDouble(), static_cast<double>(v) * v * i); + } +} + +// Verify Accumulator96b precision is always 1. +TEST(FrameMetricsFixedPointTest, Accumulator96bPrecision) { + uint32_t v = 0xFFFFFFFF; + Accumulator96b a1(1, 1); // 1. Smallest non-zero value possible. + Accumulator96b a2(v, v); // Largest initial value possible. + Accumulator96b a3(v, v); // Largest initial value possible, minus 1. + a3.Subtract(a1); + + // Verify that conversion to a double loses precision from a3. + double a2f = a2.ToDouble(); + double a3f = a3.ToDouble(); + EXPECT_DOUBLE_EQ(a2f, a3f); + EXPECT_DOUBLE_EQ(0, a2f - a3f); + + // Verify delta between a2 and a3 is 1 when computed internally. + Accumulator96b a4(a2); + a4.Subtract(a3); + EXPECT_DOUBLE_EQ(1, a4.ToDouble()); +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/frame_metrics_test_common.cc b/chromium/ui/latency/frame_metrics_test_common.cc new file mode 100644 index 00000000000..15125fca1eb --- /dev/null +++ b/chromium/ui/latency/frame_metrics_test_common.cc @@ -0,0 +1,92 @@ +// 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/latency/frame_metrics_test_common.h" + +#include "base/logging.h" + +namespace ui { +namespace frame_metrics { + +double TestStreamAnalyzerClient::TransformResult(double result) const { + return result * result_scale; +} + +template <> +void AddSamplesHelper(StreamAnalyzer* analyzer, + uint64_t value, + uint64_t weight, + size_t iterations) { + DCHECK_LE(value, std::numeric_limits<uint32_t>::max()); + DCHECK_LE(weight, std::numeric_limits<uint32_t>::max()); + for (size_t i = 0; i < iterations; i++) { + analyzer->AddSample(value, weight); + } +} + +TestRatioBoundaries::TestRatioBoundaries() { + const uint32_t one = kFixedPointMultiplier; + const uint32_t half = one / 2; + // [0, 2^-16) => 1 bucket. + int i = 0; + boundaries[i++] = 0; + // [2^-16,1) pow of 2 strides => 16 buckets. (16x1) + for (int j = 0; j < 16; j++) + boundaries[i++] = 1ULL << j; + // [1,16) stride 1/2 => 30 buckets. (2 + 4 + 8 + 16) + for (int j = 0; j < 30; j++) + boundaries[i++] = one + (j * half); + // [16,32) stride 1 => 16 buckets. + for (int j = 0; j < 16; j++) + boundaries[i++] = (16 + j) * one; + // [32,64) stride 2 => 16 buckets. + for (int j = 0; j < 16; j++) + boundaries[i++] = (32 + 2 * j) * one; + // [64,128) stride 8 => 8 buckets. + for (int j = 0; j < 8; j++) + boundaries[i++] = (64 + 8 * j) * one; + // [128, 256) stride 16 => 8 buckets. + for (int j = 0; j < 8; j++) + boundaries[i++] = (128 + 16 * j) * one; + // [256, 512) stride 64 => 4 buckets. + for (int j = 0; j < 4; j++) + boundaries[i++] = (256 + 64 * j) * one; + // [512, 1024) stride 128 => 4 buckets. + for (int j = 0; j < 4; j++) + boundaries[i++] = (512 + 128 * j) * one; + // [1024, 2048) stride 512 => 2 buckets. + for (int j = 0; j < 2; j++) + boundaries[i++] = (1024 + 512 * j) * one; + // [2048, 4096) stride 1024 => 2 buckets. + for (int j = 0; j < 2; j++) + boundaries[i++] = (2048 + 1024 * j) * one; + // [4096, 2^16) pow of 2 strides => 4 buckets. (4x1) + for (int j = 0; j < 4; j++) + boundaries[i++] = (4096ULL << j) * one; + boundaries[i++] = 1ULL << 32; + DCHECK_EQ(112, i); +} + +TestHistogram::TestHistogram() = default; +TestHistogram::~TestHistogram() = default; + +void TestHistogram::AddSample(uint32_t value, uint32_t weight) { + added_samples_.push_back({value, weight}); +} + +PercentileResults TestHistogram::ComputePercentiles() const { + return results_; +} + +std::vector<TestHistogram::ValueWeightPair> +TestHistogram::GetAndResetAllAddedSamples() { + return std::move(added_samples_); +} + +void TestHistogram::SetResults(PercentileResults results) { + results_ = results; +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/frame_metrics_test_common.h b/chromium/ui/latency/frame_metrics_test_common.h new file mode 100644 index 00000000000..7dbe380fbd6 --- /dev/null +++ b/chromium/ui/latency/frame_metrics_test_common.h @@ -0,0 +1,174 @@ +// 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_LATENCY_FRAME_METRICS_TEST_COMMON_H_ +#define UI_LATENCY_FRAME_METRICS_TEST_COMMON_H_ + +#include "ui/latency/fixed_point.h" +#include "ui/latency/histograms.h" +#include "ui/latency/stream_analyzer.h" +#include "ui/latency/windowed_analyzer.h" + +#include <array> + +// 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)); + +namespace ui { +namespace frame_metrics { + +// A simple client to verify it is actually used. +class TestStreamAnalyzerClient : public StreamAnalyzerClient { + public: + double TransformResult(double result) const override; + static constexpr double result_scale = 2.0; +}; + +using TestWindowedAnalyzerClient = TestStreamAnalyzerClient; + +// The WindowedAnalyzer expects the caller to give it some precomputed values, +// even though they are redundant. Precompute them with a helper function to +// remove boilerplate. +// A specialized version of this for StreamAnalyzer that doesn't pre compute +// the weighted values is defined in the implementation file. +template <typename AnalyzerType> +void AddSamplesHelper(AnalyzerType* analyzer, + uint64_t value, + uint64_t weight, + size_t iterations) { + DCHECK_LE(value, std::numeric_limits<uint32_t>::max()); + DCHECK_LE(weight, std::numeric_limits<uint32_t>::max()); + uint64_t weighted_value = weight * value; + uint64_t weighted_root = weight * std::sqrt(value << kFixedPointRootShift); + Accumulator96b weighted_square(value, weight); + for (size_t i = 0; i < iterations; i++) { + analyzer->AddSample(value, weight, weighted_value, weighted_root, + weighted_square); + } +} + +// A specialization of the templatized AddSamplesHelper above for +// the WindowedAnalyzer, which doesn't need to have it's weighted values +// pre computed. +template <> +void AddSamplesHelper(StreamAnalyzer* analyzer, + uint64_t value, + uint64_t weight, + size_t iterations); + +// Moves the |shared_client|'s window forward in time by 1 microsecond and +// adds all of the elements in |values| multipled by kFixedPointMultiplier. +template <typename AnalyzerType> +void AddPatternHelper(SharedWindowedAnalyzerClient* shared_client, + AnalyzerType* analyzer, + const std::vector<uint32_t>& values, + const uint32_t weight) { + for (auto i : values) { + shared_client->window_begin += base::TimeDelta::FromMicroseconds(1); + shared_client->window_end += base::TimeDelta::FromMicroseconds(1); + AddSamplesHelper(analyzer, i * kFixedPointMultiplier, weight, 1); + } +} + +// 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 +// properly), or 8 decimal places if that is less precise. +// When used with kFixedPointMultiplier, this gives us a total precision of +// 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); +} + +// This class initializes the ratio boundaries on construction in a way that +// is easier to follow than the procedural code in the RatioHistogram +// implementation. +class TestRatioBoundaries { + public: + TestRatioBoundaries(); + uint64_t operator[](size_t i) const { return boundaries[i]; } + size_t size() const { return boundaries.size(); } + + public: + // uint64_t since the last boundary needs 33 bits. + std::array<uint64_t, 112> boundaries; +}; + +// An explicit list of VSync boundaries to verify the procedurally generated +// ones in the implementation. +static constexpr std::array<uint32_t, 99> kTestVSyncBoundries = { + {// C0: [0,1) (1 bucket). + 0, + // C1: Powers of two from 1 to 2048 us @ 50% precision (12 buckets) + 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, + // C2: Every 8 Hz from 256 Hz to 128 Hz @ 3-6% precision (16 buckets) + 3906, 4032, 4167, 4310, 4464, 4630, 4808, 5000, 5208, 5435, 5682, 5952, + 6250, 6579, 6944, 7353, + // C3: Every 4 Hz from 128 Hz to 64 Hz @ 3-6% precision (16 buckets) + 7813, 8065, 8333, 8621, 8929, 9259, 9615, 10000, 10417, 10870, 11364, + 11905, 12500, 13158, 13889, 14706, + // C4: Every 2 Hz from 64 Hz to 32 Hz @ 3-6% precision (16 buckets) + 15625, 16129, 16667, 17241, 17857, 18519, 19231, 20000, 20833, 21739, + 22727, 23810, 25000, 26316, 27778, 29412, + // C5: Every 1 Hz from 32 Hz to 1 Hz @ 3-33% precision (31 buckets) + 31250, 32258, 33333, 34483, 35714, 37037, 38462, 40000, 41667, 43478, + 45455, 47619, 50000, 52632, 55556, 58824, 62500, 66667, 71429, 76923, + 83333, 90909, 100000, 111111, 125000, 142857, 166667, 200000, 250000, + 333333, 500000, + // C6: Powers of two from 1s to 32s @ 50% precision (6 buckets) + 1000000, 2000000, 4000000, 8000000, 16000000, 32000000, + // C7: Extra value to simplify estimate in Percentiles(). + 64000000}}; + +// A histogram that can be used for dependency injection in tests. +class TestHistogram : public Histogram { + public: + struct ValueWeightPair { + uint32_t value; + uint32_t weight; + }; + + TestHistogram(); + ~TestHistogram() override; + + // Histogram interface. + void AddSample(uint32_t value, uint32_t weight) override; + PercentileResults ComputePercentiles() const override; + void Reset() override{}; + + // Test interface. + std::vector<ValueWeightPair> GetAndResetAllAddedSamples(); + void SetResults(PercentileResults results); + + private: + PercentileResults results_; + std::vector<ValueWeightPair> added_samples_; +}; + +} // namespace frame_metrics +} // namespace ui + +#endif // UI_LATENCY_FRAME_METRICS_TEST_COMMON_H_ diff --git a/chromium/ui/latency/histograms.cc b/chromium/ui/latency/histograms.cc new file mode 100644 index 00000000000..e7cd947278f --- /dev/null +++ b/chromium/ui/latency/histograms.cc @@ -0,0 +1,385 @@ +// 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/latency/histograms.h" + +#include <cmath> +#include <limits> + +#include "base/bits.h" +#include "base/time/time.h" +#include "ui/latency/fixed_point.h" + +namespace { + +// Calculates percentiles in a way that can be shared by different histograms. +ui::PercentileResults PercentilesHelper( + ui::frame_metrics::BoundaryIterator* boundary_iterator, + const uint32_t* buckets_begin, + const uint32_t* buckets_end, + uint64_t total_samples) { + ui::PercentileResults result; + uint64_t boundary_left = 0; + uint64_t boundary_right = boundary_iterator->Next(); + + double thresholds[ui::PercentileResults::kCount]; + for (size_t i = 0; i < ui::PercentileResults::kCount; i++) { + thresholds[i] = ui::PercentileResults::kPercentiles[i] * total_samples; + } + + uint64_t accumulator = 0; + size_t result_index = 0; + for (const uint32_t* bucket = buckets_begin; bucket < buckets_end; bucket++) { + accumulator += *bucket; + // Multiple percentiles might be calculated from the same bucket, + // so we use a while loop here. + while (accumulator > thresholds[result_index]) { + const double overage = accumulator - thresholds[result_index]; + double b0_fraction = overage / (*bucket); + double b1_fraction = 1.0 - b0_fraction; + + // Use a linear interpolation between two buckets. + // This assumes the samples are evenly distributed within a bucket. + // TODO(brianderson): Consider neighboring bucket sizes and fit to a + // curve. http://crbug.com/821879 + const double estimate = + b0_fraction * boundary_left + b1_fraction * boundary_right; + result.values[result_index] = estimate; + result_index++; + if (result_index >= ui::PercentileResults::kCount) + return result; + } + + boundary_left = boundary_right; + boundary_right = boundary_iterator->Next(); + } + + NOTREACHED(); + return result; +} + +} // namespace + +namespace ui { + +constexpr double PercentileResults::kPercentiles[]; +constexpr size_t PercentileResults::kCount; + +namespace frame_metrics { + +constexpr size_t RatioHistogram::kBucketCount; +constexpr size_t VSyncHistogram::kBucketCount; + +// Ratio Histogram. +// The distribution of each category of buckets in this historam is +// exponential, however each category is divided into N linear buckets +// depending on how much precision we want for that category. How much +// precision a category gets depends on how often we expect that bucket to be +// used and how important those buckets are. +// +// Most of the precision is allocated for values just > 1 since most ratios, +// when not ~0 will be > 1. And since ratios are likely to be near whole +// numbers (some multiple of the vsync), we only give it a precision of 1/2. +// You can think about the stride of each category as the number of vsyncs of +// precision that category will have. +// +// There will be aliasing, but because of the vsync aligned linear division +// of each category, we won't get a bucket that represents fewer vsyncs than +// its fprevious bucket. +// +// This is in contrast to the default exponential distribution of UMA +// buckets, which result in a constant precision for each bucket and would +// allocate lots of very small buckets near 0 where we don't need the +// precision. + +namespace { + +constexpr size_t kBucketHalfStrideFirstBucketIndex = 17; + +// Within the range [16, 4096), there are 9 categories of buckets that each +// start with a power of 2. Within a category, successive buckets have a fixed +// stride. Across categories, the strides increase exponentionally, encoded +// as powers of 2 in |stride_shift|, which increases linearly. +struct RatioBucketCategory { + uint8_t first_bucket_index; + uint8_t stride_shift; +}; +using RatioCategoryHelper = std::array<RatioBucketCategory, 9>; +constexpr RatioCategoryHelper kCategories16to4096 = { + // first_bucket_index of each row below is the previous one + number of + // buckets. Each entry is {first_bucket_index, stride_shift}. + {{47, 0}, // [16, 32) stride 1 => 16 buckets. + {63, 1}, // [32, 64) stride 2 => 16 buckets. + {79, 3}, // [64, 128) stride 8 => 8 buckets. + {87, 4}, // [128, 256) stride 16 => 8 buckets. + {95, 6}, // [256, 512) stride 64 => 4 buckets + {99, 7}, // [512, 1024) stride 128 => 4 buckets. + {103, 9}, // [1024, 2048) stride 512 => 2 buckets. + {105, 10}, // [2048, 4096) stride 1024 => 2 buckets. + {107, 12}}}; // [4096, 8192) stride 4096 => 1 bucket. + +// The delegate RatioBoundary::Percentiles will pass to PercentilesHelper. +struct RatioBoundaryIterator : public BoundaryIterator { + ~RatioBoundaryIterator() override = default; + + size_t bucket = 0; + uint64_t boundary = 0; + RatioCategoryHelper::const_iterator b16to4096 = kCategories16to4096.begin(); + uint64_t next_boundary_to_change_category = + 32 * frame_metrics::kFixedPointMultiplier; + + uint64_t Next() override { + if (bucket == 0) { + // The first bucket is [0, 1). + boundary = 1; + } else if (bucket < kBucketHalfStrideFirstBucketIndex || + bucket >= kCategories16to4096.back().first_bucket_index) { + // The start and end buckets increase in size by powers of 2. + boundary *= 2; + } else if (bucket < kCategories16to4096.front().first_bucket_index) { + // The 30 buckets before 47 have a stride of .5 and represent the + // range [1, 16). + boundary += (frame_metrics::kFixedPointMultiplier / 2); + } else { + // The rest of the buckets are defined by kCategories16to4096. + DCHECK(b16to4096 < kCategories16to4096.end()); + boundary += + (frame_metrics::kFixedPointMultiplier << b16to4096->stride_shift); + // The category changes for every power of 2. + if (boundary >= next_boundary_to_change_category) { + next_boundary_to_change_category *= 2; + b16to4096++; + } + } + + bucket++; + return boundary; + } +}; + +} // namespace + +std::unique_ptr<BoundaryIterator> CreateRatioIteratorForTesting() { + return std::make_unique<RatioBoundaryIterator>(); +} + +RatioHistogram::RatioHistogram() = default; +RatioHistogram::~RatioHistogram() = default; + +void RatioHistogram::AddSample(uint32_t ratio, uint32_t weight) { + size_t bucket = 0; + + // Precomputed thresholds for the log base 2 of the ratio that help + // determine which category of buckets the sample should go in. + constexpr int kLog2HalfStrideStart = kFixedPointShift; + constexpr int kLog2Cats16to4096Start = kFixedPointShift + 4; // 2^4 = 16. + constexpr int kLog2_4096Pow2Start = kFixedPointShift + 12; // 2^12 = 4096. + + if (ratio == 0) { + bucket = 0; + } else { + int log2 = base::bits::Log2Floor(ratio); + DCHECK_GE(log2, 0); + if (log2 < kLog2HalfStrideStart) { + // [2^-16, 1) pow of 2 strides => 16 buckets. (16x1) + bucket = 1 + log2; + } else if (log2 < kLog2Cats16to4096Start) { + // [1, 16) stride 1/2 => 30 buckets. (2 + 4 + 8 + 16) + const int first_bucket_index = kBucketHalfStrideFirstBucketIndex; + const int category_start = kFixedPointMultiplier; + const int total_shift = kFixedPointShift - 1; // -1 multiplies by 2. + const int category_offset = (ratio - category_start) >> total_shift; + bucket = first_bucket_index + category_offset; + } else if (log2 < kLog2_4096Pow2Start) { + // [16, 32) stride 1 => 16 buckets. + // [32, 64) stride 2 => 16 buckets. + // [64, 128) stride 8 => 8 buckets. + // [128, 256) stride 16 => 8 buckets. + // [256, 512) stride 64 => 4 buckets. + // [512, 1024) stride 128 => 4 buckets. + // [1024, 2048) stride 512 => 2 buckets. + // [2048, 4096) stride 1024 => 2 buckets. + const int category = log2 - kLog2Cats16to4096Start; + const int category_start = 1 << log2; + const int total_shift = + (kFixedPointShift + kCategories16to4096[category].stride_shift); + const int category_offset = (ratio - category_start) >> total_shift; + bucket = + kCategories16to4096[category].first_bucket_index + category_offset; + } else { + // [4096, 2^16) pow of 2 strides => 4 buckets. (4x1) + const int category_offset = log2 - kLog2_4096Pow2Start; + bucket = kCategories16to4096.back().first_bucket_index + category_offset; + } + } + DCHECK_LT(bucket, kBucketCount); + + // Verify overflow isn't an issue. + DCHECK_LT(weight, std::numeric_limits<BucketArray::value_type>::max() - + buckets_[bucket]); + DCHECK_LT(weight, std::numeric_limits<decltype(total_samples_)>::max() - + total_samples_); + + buckets_[bucket] += weight; + total_samples_ += weight; +} + +PercentileResults RatioHistogram::ComputePercentiles() const { + RatioBoundaryIterator i; + return PercentilesHelper(&i, buckets_.data(), + buckets_.data() + buckets_.size(), total_samples_); +} + +void RatioHistogram::Reset() { + total_samples_ = 0; + buckets_.fill(0); +} + +// VSyncHistogram. +namespace { + +// The number of buckets in bucket categories 1 through 6. +constexpr std::array<uint8_t, 6> kVSyncBucketCounts = {{12, 16, 16, 16, 31, 6}}; + +// Some constants used to convert values to bucket categories. +constexpr size_t kVSync1stBucketC0 = 0; +constexpr size_t kVSync1stBucketC1 = kVSync1stBucketC0 + 1; +constexpr size_t kVSync1stBucketC2 = kVSync1stBucketC1 + kVSyncBucketCounts[0]; +constexpr size_t kVSync1stBucketC3 = kVSync1stBucketC2 + kVSyncBucketCounts[1]; +constexpr size_t kVSync1stBucketC4 = kVSync1stBucketC3 + kVSyncBucketCounts[2]; +constexpr size_t kVSync1stBucketC5 = kVSync1stBucketC4 + kVSyncBucketCounts[3]; +constexpr size_t kVSync1stBucketC6 = kVSync1stBucketC5 + kVSyncBucketCounts[4]; +constexpr size_t kVSyncBucketCountC6 = kVSyncBucketCounts[5]; + +// This iterates through the microsecond VSync boundaries. +struct VSyncBoundaryIterator : public BoundaryIterator { + ~VSyncBoundaryIterator() override = default; + + uint8_t category_ = 0; + uint8_t sub_bucket_ = 0; + + uint64_t Next() override { + uint32_t boundary = 0; + switch (category_) { + case 0: // Powers of two from 1 to 2048 us @ 50% precision + boundary = 1 << sub_bucket_; + break; + case 1: // Every 8 Hz from 256 Hz to 128 Hz @ 3-6% precision + case 2: // Every 4 Hz from 128 Hz to 64 Hz @ 3-6% precision + case 3: // Every 2 Hz from 64 Hz to 32 Hz @ 3-6% precision + case 4: { // Every 1 Hz from 32 Hz to 1 Hz @ 3-33% precision + int hz_start = 256 >> (category_ - 1); + int hz_stride = 8 >> (category_ - 1); + int hz = hz_start - hz_stride * sub_bucket_; + boundary = (base::TimeTicks::kMicrosecondsPerSecond + (hz / 2)) / hz; + break; + } + case 5: // Powers of two from 1s to 32s @ 50% precision + boundary = + static_cast<uint32_t>(base::TimeTicks::kMicrosecondsPerSecond) * + (1 << sub_bucket_); + break; + case 6: // The last boundary of 64s. + // Advancing would result in out-of-bounds access of + // kVSyncBucketCounts, so just return. + return 64 * base::TimeTicks::kMicrosecondsPerSecond; + default: + NOTREACHED(); + } + + if (++sub_bucket_ >= kVSyncBucketCounts[category_]) { + category_++; + sub_bucket_ = 0; + } + + return boundary; + } +}; + +} // namespace + +std::unique_ptr<BoundaryIterator> CreateVSyncIteratorForTesting() { + return std::make_unique<VSyncBoundaryIterator>(); +} + +VSyncHistogram::VSyncHistogram() = default; +VSyncHistogram::~VSyncHistogram() = default; + +// Optimized to minimize the number of memory accesses. +void VSyncHistogram::AddSample(uint32_t microseconds, uint32_t weight) { + size_t bucket = 0; + + static constexpr int k256HzPeriodInMicroseconds = + base::TimeTicks::kMicrosecondsPerSecond / 256; + + if (microseconds == 0) { + // bucket = 0; + } else if (microseconds < k256HzPeriodInMicroseconds) { + // Powers of two from 1 to 2048 us @ 50% precision + bucket = kVSync1stBucketC1 + base::bits::Log2Floor(microseconds); + } else if (microseconds < base::TimeTicks::kMicrosecondsPerSecond) { + // [256Hz, 1Hz) + int hz = base::TimeTicks::kMicrosecondsPerSecond / (microseconds + 0.5); + DCHECK_LT(hz, 256); + switch (hz / 32) { + // Every 1 Hz from 32 Hz to 1 Hz @ 3-33% precision + case 0: + bucket = kVSync1stBucketC6 - hz; + break; + // Every 2 Hz from 64 Hz to 32 Hz @ 3-6% precision + case 1: + bucket = kVSync1stBucketC5 - ((hz - 30) / 2); + break; + // Every 4 Hz from 128 Hz to 64 Hz @ 3-6% precision + case 2: + case 3: + bucket = kVSync1stBucketC4 - ((hz - 60) / 4); + break; + // Every 8 Hz from 256 Hz to 128 Hz @ 3-6% precision + case 4: + case 5: + case 6: + case 7: + bucket = kVSync1stBucketC3 - ((hz - 120) / 8); + break; + default: + NOTREACHED(); + return; + } + } else { + // Powers of two from 1s to 32s @ 50% precision + int seconds_log2 = base::bits::Log2Floor( + microseconds / base::TimeTicks::kMicrosecondsPerSecond); + DCHECK_GE(seconds_log2, 0); + size_t offset = std::min<size_t>(kVSyncBucketCountC6 - 1, seconds_log2); + bucket = kVSync1stBucketC6 + offset; + } + + DCHECK_GE(bucket, 0u); + DCHECK_LT(bucket, kVSync1stBucketC6 + kVSyncBucketCountC6); + DCHECK_LT(bucket, kBucketCount); + + // Verify overflow isn't an issue. + DCHECK_LT(weight, std::numeric_limits<BucketArray::value_type>::max() - + buckets_[bucket]); + DCHECK_LT(weight, std::numeric_limits<decltype(total_samples_)>::max() - + total_samples_); + + buckets_[bucket] += weight; + total_samples_ += weight; +} + +PercentileResults VSyncHistogram::ComputePercentiles() const { + VSyncBoundaryIterator i; + return PercentilesHelper(&i, buckets_.data(), + buckets_.data() + buckets_.size(), total_samples_); +} + +void VSyncHistogram::Reset() { + total_samples_ = 0; + buckets_.fill(0); +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/histograms.h b/chromium/ui/latency/histograms.h new file mode 100644 index 00000000000..bbe4ddb76a8 --- /dev/null +++ b/chromium/ui/latency/histograms.h @@ -0,0 +1,103 @@ +// 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_LATENCY_HISTOGRAMS_H_ +#define UI_LATENCY_HISTOGRAMS_H_ + +#include <array> +#include <memory> + +#include "base/macros.h" + +namespace ui { + +// Used to communicate percentile results to clients. +struct PercentileResults { + static constexpr double kPercentiles[] = {.50, .99}; + static constexpr size_t kCount = arraysize(kPercentiles); + + double values[kCount]{}; +}; + +namespace frame_metrics { + +// This is an interface different metrics will use to inject their ideal +// histogram implementations into the StreamAnalyzer. +class Histogram { + public: + Histogram() = default; + virtual ~Histogram() = default; + + // Increases the bucket that contains |value| by |weight|. + virtual void AddSample(uint32_t value, uint32_t weight) = 0; + + // Computes and returns the approximate percentiles based on the + // histogram distribution. + virtual PercentileResults ComputePercentiles() const = 0; + + // Resets all buckets in the histogram to 0. + // Higher level logic may periodically reset the the counts after it + // gathers the percentiles in order to avoid overflow. + virtual void Reset() = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(Histogram); +}; + +// Ratio histogram, with a range of [0, 2^32) and most of it's precision +// just above kFixedPointMultiplier (i.e. a fixed point of 1). +class RatioHistogram : public Histogram { + public: + RatioHistogram(); + ~RatioHistogram() override; + void AddSample(uint32_t ratio, uint32_t weight) override; + PercentileResults ComputePercentiles() const override; + void Reset() override; + + private: + static constexpr size_t kBucketCount = 111; + + uint64_t total_samples_ = 0; + using BucketArray = std::array<uint32_t, kBucketCount>; + BucketArray buckets_{}; + + DISALLOW_COPY_AND_ASSIGN(RatioHistogram); +}; + +// A histogram of 98 buckets from 0 to 64 seconds with extra precision +// around common vsync boundaries. +class VSyncHistogram : public Histogram { + public: + VSyncHistogram(); + ~VSyncHistogram() override; + void AddSample(uint32_t microseconds, uint32_t weight) override; + PercentileResults ComputePercentiles() const override; + void Reset() override; + + private: + static constexpr size_t kBucketCount = 98; + + uint64_t total_samples_ = 0; + using BucketArray = std::array<uint32_t, kBucketCount>; + BucketArray buckets_{}; + + DISALLOW_COPY_AND_ASSIGN(VSyncHistogram); +}; + +// An interface that allows PercentileHelper to iterate through the +// bucket boundaries of the delegating histogram. +// This is an implemenation detail, but is exposed here for testing purposes. +struct BoundaryIterator { + virtual ~BoundaryIterator() = default; + virtual uint64_t Next() = 0; +}; + +// These expose the internal iterators, so they can be verified in tests. +std::unique_ptr<BoundaryIterator> CreateRatioIteratorForTesting(); +std::unique_ptr<BoundaryIterator> CreateVSyncIteratorForTesting(); + +} // namespace frame_metrics +} // namespace ui + +#endif // UI_LATENCY_HISTOGRAMS_H_ diff --git a/chromium/ui/latency/histograms_perftest.cc b/chromium/ui/latency/histograms_perftest.cc new file mode 100644 index 00000000000..ab7480fc3fc --- /dev/null +++ b/chromium/ui/latency/histograms_perftest.cc @@ -0,0 +1,257 @@ +// 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/latency/histograms.h" + +#include <algorithm> + +#include "base/metrics/bucket_ranges.h" +#include "base/metrics/sample_vector.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/perf/perf_test.h" +#include "ui/latency/fixed_point.h" +#include "ui/latency/frame_metrics_test_common.h" + +namespace ui { +namespace frame_metrics { + +constexpr base::TimeDelta kTimeLimit = base::TimeDelta::FromSeconds(2); + +// A version of RatioHistogram based on the default implementations +// of base::BucketRanges and base::SampleVector. +class RatioHistogramBaseline : public Histogram { + public: + RatioHistogramBaseline() + : ratio_boundaries_(), + bucket_ranges_(ratio_boundaries_.size()), + sample_vector_(&bucket_ranges_) { + size_t i = 0; + for (const auto& b : ratio_boundaries_.boundaries) { + bucket_ranges_.set_range(i++, std::min<uint64_t>(b, INT_MAX)); + } + } + + ~RatioHistogramBaseline() override = default; + + void AddSample(uint32_t microseconds, uint32_t weight) override { + sample_vector_.Accumulate(microseconds, weight); + } + + PercentileResults ComputePercentiles() const override { + return PercentileResults(); + } + void Reset() override {} + + private: + TestRatioBoundaries ratio_boundaries_; + base::BucketRanges bucket_ranges_; + base::SampleVector sample_vector_; + + DISALLOW_COPY_AND_ASSIGN(RatioHistogramBaseline); +}; + +TEST(FrameMetricsHistogramsPerfTest, RatioEntireRange) { + const int kStride = 0x1000; + + RatioHistogramBaseline vh_base; + RatioHistogram vh_impl; + + base::TimeDelta impl_time; + base::TimeDelta base_time; + + base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit; + while (base::TimeTicks::Now() < finish_time) { + // Impl then Base + for (int i = 0; i < INT_MAX - kStride; i += kStride) { + int value = (i * 37) & 0x3FFFFFFF; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + impl_time += t1 - t0 - (t3 - t2); + base_time += t2 - t1 - (t3 - t2); + } + + // Base then Impl + for (int i = 0; i < INT_MAX - kStride; i += kStride) { + int value = (i * 37) & 0x3FFFFFFF; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + base_time += t1 - t0 - (t3 - t2); + impl_time += t2 - t1 - (t3 - t2); + } + } + + double X = base_time.InSecondsF() / impl_time.InSecondsF(); + perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true); +} + +TEST(FrameMetricsHistogramsPerfTest, RatioCommonRange) { + const int kStride = 0x100; + + RatioHistogramBaseline vh_base; + RatioHistogram vh_impl; + + base::TimeDelta impl_time; + base::TimeDelta base_time; + + base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit; + while (base::TimeTicks::Now() < finish_time) { + // Impl then Base + for (int i = 0; i < 4 * kFixedPointMultiplier; i += kStride) { + int value = i; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + impl_time += t1 - t0 - (t3 - t2); + base_time += t2 - t1 - (t3 - t2); + } + + // Base then Impl + for (int i = 0; i < 4 * kFixedPointMultiplier; i += kStride) { + int value = i; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + base_time += t1 - t0 - (t3 - t2); + impl_time += t2 - t1 - (t3 - t2); + } + } + + double X = base_time.InSecondsF() / impl_time.InSecondsF(); + perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true); +} + +// A version of VSyncHistogram based on the default implementations +// of base::BucketRanges and base::SampleVector. +class VSyncHistogramBaseline : public Histogram { + public: + VSyncHistogramBaseline() + : bucket_ranges_(kTestVSyncBoundries.size() + 1), + sample_vector_(&bucket_ranges_) { + size_t i = 0; + for (const auto& b : kTestVSyncBoundries) { + bucket_ranges_.set_range(i++, b); + } + // BucketRanges needs the last element set to INT_MAX. + bucket_ranges_.set_range(i++, INT_MAX); + } + + ~VSyncHistogramBaseline() override = default; + + void AddSample(uint32_t microseconds, uint32_t weight) override { + sample_vector_.Accumulate(microseconds, weight); + } + + PercentileResults ComputePercentiles() const override { + return PercentileResults(); + } + void Reset() override {} + + private: + base::BucketRanges bucket_ranges_; + base::SampleVector sample_vector_; + + DISALLOW_COPY_AND_ASSIGN(VSyncHistogramBaseline); +}; + +TEST(FrameMetricsHistogramsPerfTest, VSyncEntireRange) { + const int kStride = 0x1000; + + VSyncHistogramBaseline vh_base; + VSyncHistogram vh_impl; + + base::TimeDelta impl_time; + base::TimeDelta base_time; + + base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit; + while (base::TimeTicks::Now() < finish_time) { + // Impl then Base + for (int i = 0; i < INT_MAX - kStride; i += kStride) { + int value = (i * 37) % 64000000; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + impl_time += t1 - t0 - (t3 - t2); + base_time += t2 - t1 - (t3 - t2); + } + + // Base then Impl + for (int i = 0; i < INT_MAX - kStride; i += kStride) { + int value = (i * 37) % 64000000; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + base_time += t1 - t0 - (t3 - t2); + impl_time += t2 - t1 - (t3 - t2); + } + } + + double X = base_time.InSecondsF() / impl_time.InSecondsF(); + perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true); +} + +TEST(FrameMetricsHistogramsPerfTest, VSyncCommonRange) { + const int kStride = 0x100; + + VSyncHistogramBaseline vh_base; + VSyncHistogram vh_impl; + + base::TimeDelta impl_time; + base::TimeDelta base_time; + + base::TimeTicks finish_time = base::TimeTicks::Now() + kTimeLimit; + while (base::TimeTicks::Now() < finish_time) { + // Impl then Base + for (int i = 0; i < 100000; i += kStride) { + int value = i; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + impl_time += t1 - t0 - (t3 - t2); + base_time += t2 - t1 - (t3 - t2); + } + + // Base then Impl + for (int i = 0; i < 100000; i += kStride) { + int value = i; + base::TimeTicks t0 = base::TimeTicks::Now(); + vh_base.AddSample(value, 1); + base::TimeTicks t1 = base::TimeTicks::Now(); + vh_impl.AddSample(value, 1); + base::TimeTicks t2 = base::TimeTicks::Now(); + base::TimeTicks t3 = base::TimeTicks::Now(); + base_time += t1 - t0 - (t3 - t2); + impl_time += t2 - t1 - (t3 - t2); + } + } + + double X = base_time.InSecondsF() / impl_time.InSecondsF(); + perf_test::PrintResult(__FUNCTION__, "", __FUNCTION__, X, "x", true); +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/histograms_unittest.cc b/chromium/ui/latency/histograms_unittest.cc new file mode 100644 index 00000000000..4278f868733 --- /dev/null +++ b/chromium/ui/latency/histograms_unittest.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/latency/histograms.h" + +#include <algorithm> + +#include "base/metrics/bucket_ranges.h" +#include "base/metrics/sample_vector.h" +#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 { +namespace frame_metrics { + +// Verifies the ratio boundaries generated internally match the reference +// boundaries. +TEST(FrameMetricsHistogramsTest, RatioBoundariesDirect) { + const TestRatioBoundaries kTestRatioBoundaries; + std::unique_ptr<BoundaryIterator> ratio_impl = + CreateRatioIteratorForTesting(); + for (uint32_t boundary : kTestRatioBoundaries.boundaries) { + if (boundary == 0) + continue; + EXPECT_EQ(boundary, ratio_impl->Next()); + } +} + +// Verifies the VSync boundaries generated internally match the reference +// boundaries. +TEST(FrameMetricsHistogramsTest, VSyncBoundariesDirect) { + std::unique_ptr<BoundaryIterator> vsync_impl = + CreateVSyncIteratorForTesting(); + for (uint32_t boundary : kTestVSyncBoundries) { + if (boundary == 0) + continue; + EXPECT_EQ(boundary, vsync_impl->Next()); + } +} + +template <typename ReferenceBoundaryT> +void BoundaryTestCommon(const ReferenceBoundaryT& reference_boundaries, + std::unique_ptr<Histogram> histogram) { + PercentileResults percentiles; + + for (size_t i = 0; i < reference_boundaries.size() - 1; i++) { + uint64_t bucket_start = reference_boundaries[i]; + uint64_t bucket_end = reference_boundaries[i + 1]; + + // Verify values within the current bucket don't affect percentile. + // This also checks the first value in the bucket. + uint32_t stride = std::max<uint32_t>(1u, (bucket_end - bucket_start) / 8); + for (uint64_t value = bucket_start; value < bucket_end; value += stride) { + histogram->AddSample(value, 1); + percentiles = histogram->ComputePercentiles(); + histogram->Reset(); + EXPECT_LE(bucket_start, percentiles.values[0]); + EXPECT_GT(bucket_end, percentiles.values[0]); + } + + // Verify the value just before the next bucket doesn't affect percentile. + histogram->AddSample(bucket_end - 1, 1); + percentiles = histogram->ComputePercentiles(); + histogram->Reset(); + EXPECT_LE(bucket_start, percentiles.values[0]); + EXPECT_GT(bucket_end, percentiles.values[0]); + } +} + +TEST(FrameMetricsHistogramsTest, RatioBoundaries) { + const TestRatioBoundaries kTestRatioBoundaries; + BoundaryTestCommon(kTestRatioBoundaries, std::make_unique<RatioHistogram>()); +} + +TEST(FrameMetricsHistogramsTest, VSyncBoundaries) { + const TestRatioBoundaries kTestRatioBoundaries; + BoundaryTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>()); +} + +template <typename ReferenceBoundaryT> +void PercentilesTestCommon(const ReferenceBoundaryT& reference_boundaries, + std::unique_ptr<Histogram> histogram, + int percentile_index) { + double percentile = PercentileResults::kPercentiles[percentile_index]; + PercentileResults percentiles; + for (size_t i = 0; i < reference_boundaries.size() - 1; i++) { + uint64_t bucket_start = reference_boundaries[i]; + uint64_t bucket_end = reference_boundaries[i + 1]; + + // Add samples to current bucket. + // Where the samples are added in the current bucket should not affect the + // result. + uint32_t stride = std::max<uint32_t>(1u, (bucket_end - bucket_start) / 100); + int samples_added_inside = 0; + for (uint64_t value = bucket_start; value < bucket_end; value += stride) { + histogram->AddSample(value, 10); + samples_added_inside += 10; + } + + // Add samples to left and right of current bucket. + // Don't worry about doing this for the left most and right most buckets. + int samples_added_left = 0; + int samples_added_outside = 0; + if (i != 0 && i < reference_boundaries.size() - 2) { + samples_added_outside = 10000; + samples_added_left = samples_added_outside * percentile; + histogram->AddSample(bucket_start / 3, samples_added_left); + histogram->AddSample(bucket_start * 3, + samples_added_outside - samples_added_left); + } + + percentiles = histogram->ComputePercentiles(); + histogram->Reset(); + + double index = (samples_added_inside + samples_added_outside) * percentile - + samples_added_left; + double w = index / samples_added_inside; + double expected_value = bucket_end * w + bucket_start * (1.0 - w); + EXPECT_DOUBLE_EQ(expected_value, percentiles.values[percentile_index]); + } +} + +TEST(FrameMetricsHistogramsTest, RatioPercentiles50th) { + const TestRatioBoundaries kTestRatioBoundaries; + PercentilesTestCommon(kTestRatioBoundaries, + std::make_unique<RatioHistogram>(), 0); +} + +TEST(FrameMetricsHistogramsTest, RatioPercentiles99th) { + const TestRatioBoundaries kTestRatioBoundaries; + PercentilesTestCommon(kTestRatioBoundaries, + std::make_unique<RatioHistogram>(), 1); +} + +TEST(FrameMetricsHistogramsTest, VSyncPercentiles50th) { + const TestRatioBoundaries kTestRatioBoundaries; + PercentilesTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>(), + 0); +} + +TEST(FrameMetricsHistogramsTest, VSyncPercentiles99th) { + const TestRatioBoundaries kTestRatioBoundaries; + PercentilesTestCommon(kTestVSyncBoundries, std::make_unique<VSyncHistogram>(), + 1); +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/mojo/BUILD.gn b/chromium/ui/latency/mojo/BUILD.gn index 0840e693bd2..61d39750b7e 100644 --- a/chromium/ui/latency/mojo/BUILD.gn +++ b/chromium/ui/latency/mojo/BUILD.gn @@ -11,6 +11,7 @@ mojom("interfaces") { public_deps = [ "//mojo/common:common_custom_types", + "//mojo/public/mojom/base", "//ui/gfx/geometry/mojo", ] } diff --git a/chromium/ui/latency/mojo/latency_info.mojom b/chromium/ui/latency/mojo/latency_info.mojom index 9ebb386eb18..1b0e5e858cb 100644 --- a/chromium/ui/latency/mojo/latency_info.mojom +++ b/chromium/ui/latency/mojo/latency_info.mojom @@ -4,7 +4,7 @@ module ui.mojom; -import "mojo/common/time.mojom"; +import "mojo/public/mojom/base/time.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; enum LatencyComponentType { @@ -104,13 +104,13 @@ struct LatencyComponent { // component. int64 sequence_number; // Average time of events that happened in this component. - mojo.common.mojom.TimeTicks event_time; + mojo_base.mojom.TimeTicks event_time; // Count of events that happened in this component uint32 event_count; // Time of the oldest event that happened in this component. - mojo.common.mojom.TimeTicks first_event_time; + mojo_base.mojom.TimeTicks first_event_time; // Time of the most recent event that happened in this component. - mojo.common.mojom.TimeTicks last_event_time; + mojo_base.mojom.TimeTicks last_event_time; }; struct LatencyComponentPair { @@ -128,5 +128,5 @@ struct LatencyInfo { bool began; bool terminated; SourceEventType source_event_type; - mojo.common.mojom.TimeDelta expected_queueing_time_on_dispatch; + mojo_base.mojom.TimeDelta expected_queueing_time_on_dispatch; }; diff --git a/chromium/ui/latency/mojo/latency_info_struct_traits.cc b/chromium/ui/latency/mojo/latency_info_struct_traits.cc index 4f4d2316157..2e44de61973 100644 --- a/chromium/ui/latency/mojo/latency_info_struct_traits.cc +++ b/chromium/ui/latency/mojo/latency_info_struct_traits.cc @@ -4,7 +4,7 @@ #include "ui/latency/mojo/latency_info_struct_traits.h" -#include "mojo/common/time_struct_traits.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" namespace mojo { diff --git a/chromium/ui/latency/mojo/latency_info_struct_traits.h b/chromium/ui/latency/mojo/latency_info_struct_traits.h index 53e465ab55f..4fc347e777a 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 "mojo/common/common_custom_types_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 new file mode 100644 index 00000000000..2ecd094744a --- /dev/null +++ b/chromium/ui/latency/stream_analyzer.cc @@ -0,0 +1,186 @@ +// 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/latency/stream_analyzer.h" + +namespace ui { +namespace frame_metrics { + +StreamAnalyzer::StreamAnalyzer( + const StreamAnalyzerClient* client, + const SharedWindowedAnalyzerClient* shared_client, + std::vector<uint32_t> thresholds, + std::unique_ptr<Histogram> histogram) + : client_(client), + histogram_(std::move(histogram)), + windowed_analyzer_(client, shared_client) { + thresholds_.reserve(thresholds.size()); + for (const uint32_t& t : thresholds) + thresholds_.emplace_back(t); +} + +StreamAnalyzer::~StreamAnalyzer() = default; + +void StreamAnalyzer::Reset() { + StartNewReportPeriod(); + windowed_analyzer_.ResetHistory(); +} + +void StreamAnalyzer::StartNewReportPeriod() { + histogram_->Reset(); + windowed_analyzer_.ResetWorstValues(); + for (auto& t : thresholds_) + t.ResetAccumulators(); + + total_weight_ = 0; + accumulator_ = 0; + root_accumulator_ = 0; + square_accumulator_ = Accumulator96b(); +} + +void StreamAnalyzer::AddSample(const uint32_t value, const uint32_t weight) { + DCHECK_GT(weight, 0u); + + uint64_t weighted_value = static_cast<uint64_t>(weight) * value; + uint64_t weighted_root = weight * std::sqrt(static_cast<double>(value) * + kFixedPointRootMultiplier); + Accumulator96b weighted_square(value, weight); + + // Verify overflow isn't an issue. + // square_accumulator_ has DCHECKs internally, so we don't worry about + // checking that here. + DCHECK_LT(weighted_value, + std::numeric_limits<decltype(accumulator_)>::max() - accumulator_); + DCHECK_LT(weighted_root, + std::numeric_limits<decltype(root_accumulator_)>::max() - + root_accumulator_); + DCHECK_LT(weight, std::numeric_limits<decltype(total_weight_)>::max() - + total_weight_); + + histogram_->AddSample(value, weight); + windowed_analyzer_.AddSample(value, weight, weighted_value, weighted_root, + weighted_square); + + for (auto& t : thresholds_) { + if (value >= t.threshold) + t.ge_weight += weight; + else + t.lt_weight += weight; + } + + total_weight_ += weight; + accumulator_ += weighted_value; + root_accumulator_ += weighted_root; + square_accumulator_.Add(weighted_square); +} + +double StreamAnalyzer::ComputeMean() const { + double result = static_cast<double>(accumulator_) / total_weight_; + return client_->TransformResult(result); +} + +double StreamAnalyzer::ComputeRMS() const { + double mean_square = square_accumulator_.ToDouble() / total_weight_; + double result = std::sqrt(mean_square); + return client_->TransformResult(result); +} + +double StreamAnalyzer::ComputeSMR() const { + double mean_root = static_cast<double>(root_accumulator_) / total_weight_; + double result = (mean_root * mean_root) / kFixedPointRootMultiplier; + return client_->TransformResult(result); +} + +double StreamAnalyzer::VarianceHelper(double accum, double square_accum) const { + double mean = accum / total_weight_; + double mean_squared = mean * mean; + double mean_square = square_accum / total_weight_; + double variance = mean_square - mean_squared; + // This approach to calculating the standard deviation isn't numerically + // stable if the variance is very small relative to the mean, which might + // result in a negative variance. Clamp it to 0. + return std::max(0.0, variance); +} + +double StreamAnalyzer::ComputeStdDev() const { + double variance = + VarianceHelper(accumulator_, square_accumulator_.ToDouble()); + double std_dev = std::sqrt(variance); + return client_->TransformResult(std_dev); +} + +double StreamAnalyzer::ComputeVarianceOfRoots() const { + double normalized_root = + static_cast<double>(root_accumulator_) / kFixedPointRootMultiplierSqrt; + double variance = VarianceHelper(normalized_root, accumulator_); + return client_->TransformResult(variance); +} + +void StreamAnalyzer::ThresholdState::ResetAccumulators() { + ge_weight = 0; + lt_weight = 0; +} + +std::vector<ThresholdResult> StreamAnalyzer::ComputeThresholds() const { + std::vector<ThresholdResult> results; + results.reserve(thresholds_.size()); + for (const auto& t : thresholds_) { + double threshold = client_->TransformResult(t.threshold); + double ge_fraction = + static_cast<double>(t.ge_weight) / (t.ge_weight + t.lt_weight); + results.push_back({threshold, ge_fraction}); + } + return results; +} + +PercentileResults StreamAnalyzer::ComputePercentiles() const { + PercentileResults result; + result = histogram_->ComputePercentiles(); + for (size_t i = 0; i < PercentileResults::kCount; i++) { + result.values[i] = client_->TransformResult(result.values[i]); + } + return result; +} + +std::unique_ptr<base::trace_event::ConvertableToTraceFormat> +StreamAnalyzer::AsValue() const { + auto state = std::make_unique<base::trace_event::TracedValue>(); + AsValueInto(state.get()); + return std::move(state); +} + +void StreamAnalyzer::AsValueInto(base::trace_event::TracedValue* state) const { + state->SetDouble("mean", ComputeMean()); + + state->SetDouble("rms", ComputeRMS()); + state->SetDouble("smr", ComputeSMR()); + + state->SetDouble("std_dev", ComputeStdDev()); + state->SetDouble("variance_of_roots", ComputeVarianceOfRoots()); + + state->BeginArray("percentiles"); + PercentileResults result = ComputePercentiles(); + for (size_t i = 0; i < PercentileResults::kCount; i++) { + state->BeginArray(); + state->AppendDouble(PercentileResults::kPercentiles[i]); + state->AppendDouble(result.values[i]); + state->EndArray(); + } + state->EndArray(); + + state->BeginArray("thresholds"); + std::vector<ThresholdResult> thresholds(ComputeThresholds()); + for (const auto& t : thresholds) { + state->BeginArray(); + state->AppendDouble(t.threshold); + state->AppendDouble(t.ge_fraction); + state->EndArray(); + } + state->EndArray(); + + windowed_analyzer_.AsValueInto(state); +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/stream_analyzer.h b/chromium/ui/latency/stream_analyzer.h new file mode 100644 index 00000000000..45982fbb888 --- /dev/null +++ b/chromium/ui/latency/stream_analyzer.h @@ -0,0 +1,128 @@ +// 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_LATENCY_STREAM_ANALYZER_H_ +#define UI_LATENCY_STREAM_ANALYZER_H_ + +#include <cstdint> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "base/trace_event/trace_event_argument.h" +#include "ui/latency/fixed_point.h" +#include "ui/latency/histograms.h" +#include "ui/latency/windowed_analyzer.h" + +namespace ui { + +// Used to communicate fraction of time the value of a metric was greater than +// or equal to the threshold. +struct ThresholdResult { + double threshold = 0.0; + double ge_fraction = 0.0; +}; + +namespace frame_metrics { + +// The StreamAnalyzerClient interface is currently the same as +// WindowedAnalyzerClient and can rely on the same implementation. +using StreamAnalyzerClient = WindowedAnalyzerClient; + +// Tracks the overall mean, RMS, and SMR for a metric and also owns +// the Histogram and WindowedAnalyzer. +class StreamAnalyzer { + public: + StreamAnalyzer(const StreamAnalyzerClient* client, + const SharedWindowedAnalyzerClient* shared_client, + std::vector<uint32_t> thresholds, + std::unique_ptr<Histogram> histogram); + ~StreamAnalyzer(); + + // Resets all statistics and history. + void Reset(); + + // Resets the statistics without throwing away recent sample history in the + // WindowedAnalyzer. + void StartNewReportPeriod(); + + // To play well with the histogram range, |value| should be within the + // range [0,64000000]. If the units are milliseconds, that's 64 seconds. + // Otherwise, the histogram will clip the result. + // |weight| may be the duration the frame was active in microseconds + // or it may be 1 in case every frame is to be weighed equally. + void AddSample(const uint32_t value, const uint32_t weight); + + // The mean, root-mean-squared, and squared-mean-root of all samples + // received since the last call to StartNewReportPeriod(). + // The units are the same as the values added in AddSample(). + double ComputeMean() const; + double ComputeRMS() const; + double ComputeSMR() const; + + // StdDev calculates the standard deviation of all values in the stream. + // The units are the same as the values added in AddSample(). + // The work to track this is the same as RMS, so we effectively get this for + // free. Given two of the Mean, RMS, and StdDev, we can calculate the third. + double ComputeStdDev() const; + + // VarianceOfRoots calculates the variance of all square roots of values. + // The units end up being the same as the values added in AddSample(). + // The work to track this is the same as SMR. + // Given two of the Mean, SMR, and VarianceOfRoots, we can calculate the + // third. Note: We don't track something like RootStdDevOfSquares since it + // would be difficult to track values raised to the fourth power. + // TODO(brianderon): Remove VarianceOfRoots if it's not useful. + double ComputeVarianceOfRoots() const; + + // Thresholds returns a percentile for threshold values given to the + // constructor. This is useful for tracking improvements in really good + // sources, but it's dynamic range is limited, which prevents it from + // detecting improvements in sources where most of the frames are "bad". + std::vector<ThresholdResult> ComputeThresholds() const; + + // CalculatePercentiles returns a value for certain percentiles. + // It is only an estimate, since the values are calculated from a histogram + // rather than from the entire history of actual values. + // This is useful for tracking improvements even in really bad sources + // since it's dynamic range includes all possible values. + PercentileResults ComputePercentiles() const; + + // Expose the WindowedAnalyzer as const to make it's accessors + // available directly. + const WindowedAnalyzer& window() const { return windowed_analyzer_; } + + std::unique_ptr<base::trace_event::ConvertableToTraceFormat> AsValue() const; + void AsValueInto(base::trace_event::TracedValue* state) const; + + protected: + double VarianceHelper(double accum, double square_accum) const; + + struct ThresholdState { + explicit ThresholdState(uint32_t value) : threshold(value) {} + void ResetAccumulators(); + + uint32_t threshold; + uint32_t ge_weight = 0; + uint32_t lt_weight = 0; + }; + + const StreamAnalyzerClient* const client_; + + std::vector<ThresholdState> thresholds_; + std::unique_ptr<Histogram> histogram_; + WindowedAnalyzer windowed_analyzer_; + + uint64_t total_weight_ = 0; + uint64_t accumulator_ = 0; + uint64_t root_accumulator_ = 0; + Accumulator96b square_accumulator_; + + DISALLOW_COPY_AND_ASSIGN(StreamAnalyzer); +}; + +} // namespace frame_metrics +} // namespace ui + +#endif // UI_LATENCY_STREAM_ANALYZER_H_ diff --git a/chromium/ui/latency/stream_analyzer_unittest.cc b/chromium/ui/latency/stream_analyzer_unittest.cc new file mode 100644 index 00000000000..08410961907 --- /dev/null +++ b/chromium/ui/latency/stream_analyzer_unittest.cc @@ -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. + +#include "ui/latency/stream_analyzer.h" + +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "ui/latency/frame_metrics_test_common.h" + +namespace ui { +namespace frame_metrics { +namespace { + +class StreamAnalyzerTest : public testing::Test { + public: + StreamAnalyzerTest() { NewAnalyzer(10, {2, 7, 10}); } + + void SetUp() override {} + + StreamAnalyzer* analyzer() { return analyzer_.get(); } + + void NewAnalyzer(size_t window_size, std::vector<uint32_t> thresholds) { + shared_client_.max_window_size = window_size; + for (auto& t : thresholds) { + t *= kFixedPointMultiplier; + } + thresholds_ = std::move(thresholds); + std::unique_ptr<TestHistogram> histogram = + std::make_unique<TestHistogram>(); + histogram_ = histogram.get(); + analyzer_ = std::make_unique<StreamAnalyzer>( + &client_, &shared_client_, thresholds_, std::move(histogram)); + } + + protected: + size_t window_size; + TestStreamAnalyzerClient client_; + SharedWindowedAnalyzerClient shared_client_; + std::vector<uint32_t> thresholds_; + TestHistogram* histogram_; + std::unique_ptr<StreamAnalyzer> analyzer_; +}; + +TEST_F(StreamAnalyzerTest, AllResultsTheSame) { + // 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. + for (uint64_t value = 1; value < 0x100000000ULL; value *= 2) { + // Adding too many samples can result in overflow when multiplied by the + // weight. Divide by samples to avoid overflow. + for (uint64_t weight = 1; weight < 0x100000000ULL / samples; + weight *= 2) { + analyzer()->Reset(); + AddSamplesHelper(analyzer(), value, weight, samples); + 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); + + // 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); + } + } + } + + // All min/max combinations of value and weight. + for (uint64_t value : {0u, 0xFFFFFFFFu}) { + for (uint64_t weight : {1u, 0xFFFFFFFFu}) { + const size_t kSamplesToAdd = weight == 1 ? 100 : 1; + analyzer()->Reset(); + AddSamplesHelper(analyzer(), value, weight, kSamplesToAdd); + + // TestWindowedAnalyzerClient scales the result by 2. + 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); + + // 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); + } + } +} + +// This applies a pattern of 2 values that are easy to calculate the expected +// results for. It verifies the mean, rms, smr, standard deviation, +// variance of the roots, and thresholds are calculated properly. +// This doesn't check histogram or windowed analyzer related values since they +// are tested separately and other unit tests verify their interactions +// with StreamAnalyzer. +TEST_F(StreamAnalyzerTest, AllResultsDifferent) { + const uint32_t kSampleWeight = 100; + + const std::vector<uint32_t> pattern49 = {4, 9, 4, 9, 4, 9}; + const std::vector<uint32_t> pattern4 = {4, 4, 4, 4, 4, 4}; + const std::vector<uint32_t> pattern9 = {9, 9, 9, 9, 9, 9}; + + // Calculate the expected values for an equal number of 4's and 9's. + const double expected_mean = (4 + 9) * .5 * kFixedPointMultiplier * + TestStreamAnalyzerClient::result_scale; + const double expected_rms = std::sqrt((16 + 81) * .5) * + kFixedPointMultiplier * + TestStreamAnalyzerClient::result_scale; + const double mean_root = (2 + 3) * .5; + const double expected_smr = mean_root * mean_root * kFixedPointMultiplier * + TestStreamAnalyzerClient::result_scale; + const double expected_std_dev = (9 - 4) * .5 * kFixedPointMultiplier * + TestStreamAnalyzerClient::result_scale; + const double std_dev_of_roots = (3 - 2) * .5; + const double expected_variance_of_roots = + std_dev_of_roots * std_dev_of_roots * kFixedPointMultiplier * + TestStreamAnalyzerClient::result_scale; + + std::vector<ThresholdResult> thresholds; + + // Alternate 4 and 9. + 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()); + } + thresholds = analyzer_->ComputeThresholds(); + ASSERT_EQ(3u, thresholds.size()); + 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); + EXPECT_EQ(1.0, thresholds[0].ge_fraction); + EXPECT_EQ(0.5, thresholds[1].ge_fraction); + EXPECT_EQ(0.0, thresholds[2].ge_fraction); + + // 4's then 9's. + analyzer()->Reset(); + for (size_t i = 0; i < 500; i++) { + AddPatternHelper(&shared_client_, analyzer(), pattern4, kSampleWeight); + } + for (size_t i = 0; i < 500; i++) { + AddPatternHelper(&shared_client_, analyzer(), pattern9, kSampleWeight); + } + 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_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); + EXPECT_EQ(1.0, thresholds[0].ge_fraction); + EXPECT_EQ(0.5, thresholds[1].ge_fraction); + EXPECT_EQ(0.0, thresholds[2].ge_fraction); + + // 9's then 4's. + analyzer()->Reset(); + for (size_t i = 0; i < 500; i++) { + AddPatternHelper(&shared_client_, analyzer(), pattern9, kSampleWeight); + } + for (size_t i = 0; i < 500; i++) { + AddPatternHelper(&shared_client_, analyzer(), pattern4, kSampleWeight); + } + 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_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); + EXPECT_EQ(1.0, thresholds[0].ge_fraction); + EXPECT_EQ(0.5, thresholds[1].ge_fraction); + EXPECT_EQ(0.0, thresholds[2].ge_fraction); +} + +TEST_F(StreamAnalyzerTest, SamplesForwardedToHistogram) { + const uint32_t kSampleWeight = 123; + const std::vector<uint32_t> pattern = {4, 9, 16, 25, 36, 49}; + AddPatternHelper(&shared_client_, analyzer(), pattern, kSampleWeight); + std::vector<TestHistogram::ValueWeightPair> samples( + histogram_->GetAndResetAllAddedSamples()); + ASSERT_EQ(pattern.size(), samples.size()); + for (size_t i = 0; i < samples.size(); i++) { + EXPECT_EQ(pattern[i] * kFixedPointMultiplier, samples[i].value); + EXPECT_EQ(kSampleWeight, samples[i].weight); + } +} + +TEST_F(StreamAnalyzerTest, PercentilesModifiedByClient) { + double result0 = 7; + double result1 = 11; + histogram_->SetResults({{result0, result1}}); + PercentileResults results = analyzer()->ComputePercentiles(); + EXPECT_EQ(client_.TransformResult(result0), results.values[0]); + EXPECT_EQ(client_.TransformResult(result1), results.values[1]); +} + +// StreamAnalyzerNaive is a subset of stream analyzer that only uses single +// precision floating point accumulators and can accumulate error. +// This is used to verify patterns that accumulate error, so we can then verify +// those patterns don't result in acculated error in the actual implementation. +struct StreamAnalyzerNaive { + void AddSample(uint32_t value, + uint32_t weight, + uint64_t weighted_value, + uint64_t weighted_root, + const Accumulator96b& weighted_square) { + accumulator_ += static_cast<double>(weight) * value; + root_accumulator_ += static_cast<double>(weight) * std::sqrt(value); + square_accumulator_ += static_cast<double>(weight) * value * value; + total_weight_ += weight; + } + + double ComputeMean() { + return client_.TransformResult(accumulator_ / total_weight_); + } + double ComputeRMS() { + return client_.TransformResult( + std::sqrt(square_accumulator_ / total_weight_)); + } + double ComputeSMR() { + double mean_root = root_accumulator_ / total_weight_; + return client_.TransformResult(mean_root * mean_root); + } + + float total_weight_ = 0; + float accumulator_ = 0; + float root_accumulator_ = 0; + float square_accumulator_ = 0; + + TestStreamAnalyzerClient client_; +}; + +// Unlike the WindowedAnalyzer, there aren't patterns of input that would +// affect the precision of our results very much with double precision floating +// point accumulators. This is because we aren't subtracting values like the +// WindowedAnalyzer does. Nevertheless, there can be issues if the accumulators +// are only single precision. +TEST_F(StreamAnalyzerTest, Precision) { + StreamAnalyzerNaive naive_analyzer; + + uint32_t large_value = 20 * base::TimeTicks::kMicrosecondsPerSecond; + uint32_t large_weight = large_value; + size_t large_sample_count = 1; + AddSamplesHelper(&naive_analyzer, large_value, large_weight, + large_sample_count); + AddSamplesHelper(analyzer(), large_value, large_weight, large_sample_count); + + uint32_t small_value = 1 * base::TimeTicks::kMicrosecondsPerMillisecond; + uint32_t small_weight = small_value; + size_t small_sample_count = 60 * 60 * 60; // 1hr of 60Hz frames. + AddSamplesHelper(&naive_analyzer, small_value, small_weight, + small_sample_count); + AddSamplesHelper(analyzer(), small_value, small_weight, small_sample_count); + + double total_weight = static_cast<double>(large_sample_count) * large_weight + + static_cast<double>(small_sample_count) * small_weight; + + double large_value_f = large_value; + double small_value_f = small_value; + + double expected_mean = client_.TransformResult( + (large_value_f * large_weight + + small_sample_count * small_value_f * small_weight) / + total_weight); + EXPECT_ABS_LT(expected_mean * .001, + expected_mean - naive_analyzer.ComputeMean()); + EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean()); + + double large_value_squared = large_value_f * large_value_f * large_weight; + double small_value_squared = small_value_f * small_value_f * small_weight; + double mean_square = + (large_value_squared + small_sample_count * small_value_squared) / + total_weight; + 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()); + + double large_value_root = std::sqrt(large_value_f) * large_weight; + double small_value_root = std::sqrt(small_value_f) * small_weight; + double mean_root = + (large_value_root + small_sample_count * small_value_root) / total_weight; + 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); +} + +} // namespace +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/windowed_analyzer.cc b/chromium/ui/latency/windowed_analyzer.cc new file mode 100644 index 00000000000..01e61c9b841 --- /dev/null +++ b/chromium/ui/latency/windowed_analyzer.cc @@ -0,0 +1,146 @@ +// 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/latency/windowed_analyzer.h" + +namespace ui { + +void FrameRegionResult::AsValueInto( + base::trace_event::TracedValue* state) const { + state->SetDouble("value", value); + state->SetDouble("start", window_begin.since_origin().InMillisecondsF()); + state->SetDouble("duration", (window_end - window_begin).InMillisecondsF()); +} + +namespace frame_metrics { + +WindowedAnalyzer::WindowedAnalyzer( + const WindowedAnalyzerClient* client, + const SharedWindowedAnalyzerClient* shared_client) + : client_(client), shared_client_(shared_client) { + window_queue_.reserve(shared_client->max_window_size); +} + +WindowedAnalyzer::~WindowedAnalyzer() = default; + +void WindowedAnalyzer::ResetWorstValues() { + results_.reset(); +} + +void WindowedAnalyzer::ResetHistory() { + total_weight_ = 0; + accumulator_ = 0; + root_accumulator_ = 0; + square_accumulator_ = Accumulator96b(); + window_queue_.resize(0); +} + +void WindowedAnalyzer::AddSample(uint32_t value, + uint32_t weight, + uint64_t weighted_value, + uint64_t weighted_root, + const Accumulator96b& weighted_square) { + DCHECK_GT(weight, 0u); + DCHECK_EQ(weighted_value, static_cast<uint64_t>(weight) * value); + + // Remove old values from the accumulators. + if (window_queue_.size() >= shared_client_->max_window_size) { + const uint32_t old_value = window_queue_.front().value; + const uint32_t old_weight = window_queue_.front().weight; + window_queue_.pop_front(); + + // Re-calculate some of the old values here. Although squared and root are + // passed in, we've only stored the original value to reduce memory usage. + total_weight_ -= old_weight; + 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)); + square_accumulator_.Subtract(Accumulator96b(old_value, old_weight)); + } + + // Verify overflow isn't an issue. + // square_accumulator_ has DCHECKs internally, so we don't worry about + // checking that here. + DCHECK_LT(weighted_value, + std::numeric_limits<decltype(accumulator_)>::max() - accumulator_); + DCHECK_LT(weighted_root, + std::numeric_limits<decltype(root_accumulator_)>::max() - + root_accumulator_); + DCHECK_LT(weight, std::numeric_limits<decltype(total_weight_)>::max() - + total_weight_); + + window_queue_.push_back({value, weight}); + total_weight_ += weight; + accumulator_ += weighted_value; + root_accumulator_ += weighted_root; + square_accumulator_.Add(weighted_square); + if (window_queue_.size() >= shared_client_->max_window_size) { + bool initialize_results = !results_; + if (initialize_results) + results_.emplace(); + UpdateWorst(accumulator_, &results_->mean, initialize_results); + UpdateWorst(root_accumulator_, &results_->root, initialize_results); + UpdateWorst(square_accumulator_, &results_->square, initialize_results); + } +} + +FrameRegionResult WindowedAnalyzer::ComputeWorstMean() const { + FrameRegionResult result; + if (results_) { + result = results_->mean; + } else { + UpdateWorst(accumulator_, &result, true); + } + result.value = client_->TransformResult(result.value); + return result; +} + +FrameRegionResult WindowedAnalyzer::ComputeWorstRMS() const { + FrameRegionResult result; + if (results_) { + result = results_->square; + } else { + UpdateWorst(square_accumulator_, &result, true); + } + result.value = client_->TransformResult(std::sqrt(result.value)); + return result; +} + +FrameRegionResult WindowedAnalyzer::ComputeWorstSMR() const { + FrameRegionResult result; + if (results_) { + result = results_->root; + } else { + UpdateWorst(root_accumulator_, &result, true); + } + result.value = client_->TransformResult((result.value * result.value) / + kFixedPointRootMultiplier); + return result; +} + +void WindowedAnalyzer::AsValueInto( + base::trace_event::TracedValue* state) const { + FrameRegionResult region; + + region = ComputeWorstMean(); + state->BeginDictionary("worst_mean"); + region.AsValueInto(state); + state->EndDictionary(); + + region = ComputeWorstSMR(); + state->BeginDictionary("worst_smr"); + region.AsValueInto(state); + state->EndDictionary(); + + region = ComputeWorstRMS(); + state->BeginDictionary("worst_rms"); + region.AsValueInto(state); + state->EndDictionary(); +} + +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/latency/windowed_analyzer.h b/chromium/ui/latency/windowed_analyzer.h new file mode 100644 index 00000000000..248b6cc446d --- /dev/null +++ b/chromium/ui/latency/windowed_analyzer.h @@ -0,0 +1,161 @@ +// 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_LATENCY_WINDOWED_ANALYZER_H_ +#define UI_LATENCY_WINDOWED_ANALYZER_H_ + +#include <cstdint> + +#include "base/containers/circular_deque.h" +#include "base/macros.h" +#include "base/optional.h" +#include "base/time/time.h" +#include "base/trace_event/trace_event_argument.h" +#include "ui/latency/fixed_point.h" + +namespace ui { + +// FrameRegionResult encodes window of time where a metric was worst. +// The |sample_count| is the number of samples/frames within the time window +// used to calculate the result. It is reported in case the client wants to +// assess the confidence of the result. +struct FrameRegionResult { + double value = 0; + size_t sample_count = 0; + base::TimeTicks window_begin; + base::TimeTicks window_end; + + void AsValueInto(base::trace_event::TracedValue* state) const; +}; + +namespace frame_metrics { + +// Client delegates that are specific to each WindowedAnalyzer. +class WindowedAnalyzerClient { + public: + // The WorstMean,RMS,SMR methods will give TransformResult() a chance to + // modify the results via this delegate. + // This can be used to undo any tranformations applied to values added + // to AddSample, such as conversions to fixed point. + virtual double TransformResult(double result) const = 0; + + // TODO(brianderson): Replace WindowedAnalyzer::window_queue_ with a client + // interface here. All latency derived metrics should be able to share a + // common history of values. http://crbug.com/822054 +}; + +// Client delegates that can be shared by multiple WindowedAnalyzers. +// Tracks the current window of time that can be stored as the worst +// window of time if a metric detects it as such. +struct SharedWindowedAnalyzerClient { + SharedWindowedAnalyzerClient() : max_window_size(0) {} + + explicit SharedWindowedAnalyzerClient(size_t max_window_size) + : max_window_size(max_window_size) {} + + SharedWindowedAnalyzerClient(size_t max_window_size, + base::TimeTicks window_begin, + base::TimeTicks window_end) + : max_window_size(max_window_size), + window_begin(window_begin), + window_end(window_end) {} + + // Maximum window size in number of samples. + size_t max_window_size; + + // Current window of time for the samples being added. + base::TimeTicks window_begin; + base::TimeTicks window_end; +}; + +// Detects the worst windows of time for a metric. +// Tracks the current values of the current window of time for the +// mean, RMS, and SMR of a single metric. It maintains a history +// of the recent samples and, for each new sample, updates it's accumulators +// using the oldest and newest samples, without looking at any of the other +// samples in between. +class WindowedAnalyzer { + public: + WindowedAnalyzer(const WindowedAnalyzerClient* client, + const SharedWindowedAnalyzerClient* shared_client); + virtual ~WindowedAnalyzer(); + + // ResetWosrtValues only resets the memory of worst values encountered, + // without resetting recent sample history. + void ResetWorstValues(); + + // ResetHistory only resets recent sample history without resetting memory + // of the worst values ecnountered. + void ResetHistory(); + + // Callers of AddSample will already have calculated weighted values to + // track cumulative results, so just let them pass in the values here + // rather than re-calculating them. + void AddSample(uint32_t value, + uint32_t weight, + uint64_t weighted_value, + uint64_t weighted_root, + const Accumulator96b& weighted_square); + + // Returns the worst regions encountered so far. + FrameRegionResult ComputeWorstMean() const; + FrameRegionResult ComputeWorstRMS() const; + FrameRegionResult ComputeWorstSMR() const; + + void AsValueInto(base::trace_event::TracedValue* state) const; + + protected: + struct QueueEntry { + uint32_t value = 0; + uint32_t weight = 0; + }; + + // Updates the result with the current value, if it is worse than the + // value in |result| or if |initialize| is true. + template <typename AccumulatorT> + void UpdateWorst(const AccumulatorT& accumulator, + FrameRegionResult* result, + bool initialize) const { + double current_mean = AsDouble(accumulator) / total_weight_; + if (initialize || current_mean > result->value) { + result->value = current_mean; + result->sample_count = window_queue_.size(); + result->window_begin = shared_client_->window_begin; + result->window_end = shared_client_->window_end; + } + } + + const WindowedAnalyzerClient* const client_; + const SharedWindowedAnalyzerClient* const shared_client_; + + // We need to maintain a history of values so we can + // remove old samples from the accumulators. + base::circular_deque<QueueEntry> window_queue_; + + uint64_t total_weight_ = 0; + uint64_t accumulator_ = 0; + uint64_t root_accumulator_ = 0; + Accumulator96b square_accumulator_; + + // Internal results that track the worst region so far. + // The time region is stored correctly, however the results are intermediate + // and must be adjusted by result_transform_ and fixed_point_multipler before + // exposure to the client. Furthermore, RMS needs to square root the result + // and SMR needs to square the result. + struct InternalResults { + FrameRegionResult mean; + FrameRegionResult root; + FrameRegionResult square; + }; + // Optional since they aren't valid until we've seen enough samples. + // This delay prevents the first couple samples from dominating the result. + base::Optional<InternalResults> results_; + + DISALLOW_COPY_AND_ASSIGN(WindowedAnalyzer); +}; + +} // namespace frame_metrics +} // namespace ui + +#endif // UI_LATENCY_WINDOWED_ANALYZER_H_ diff --git a/chromium/ui/latency/windowed_analyzer_unittest.cc b/chromium/ui/latency/windowed_analyzer_unittest.cc new file mode 100644 index 00000000000..71b42480548 --- /dev/null +++ b/chromium/ui/latency/windowed_analyzer_unittest.cc @@ -0,0 +1,470 @@ +// 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/latency/windowed_analyzer.h" + +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/latency/frame_metrics_test_common.h" + +namespace ui { +namespace frame_metrics { +namespace { + +// Verify that the worst values for Mean, SMR, and RMS are all the same if +// every value added is the same. Makes for a nice sanity check. +TEST(FrameMetricsWindowedAnalyzerTest, AllResultsTheSame) { + // For this test, we don't care about the timeline, so just keep it constant. + TestWindowedAnalyzerClient client; + SharedWindowedAnalyzerClient shared_client( + 60, base::TimeTicks(), + base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + + // 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. + for (uint64_t value = 1; value < 0x100000000ULL; value *= 2) { + // Adding too many samples can result in overflow when multiplied by the + // weight. Divide by samples to avoid overflow. + for (uint64_t weight = 1; weight < 0x100000000ULL / samples; + weight *= 2) { + WindowedAnalyzer analyzer(&client, &shared_client); + AddSamplesHelper(&analyzer, value, weight, samples); + uint64_t expected_value = + value * TestWindowedAnalyzerClient::result_scale; + EXPECT_EQ(analyzer.ComputeWorstMean().value, expected_value) + << value << " x " << weight; + EXPECT_EQ(analyzer.ComputeWorstRMS().value, expected_value) + << value << " x " << weight; + EXPECT_NEAR_SMR(analyzer.ComputeWorstSMR().value, expected_value, + weight) + << value << " x " << weight; + } + } + } + + // All min/max combinations of value and weight. + for (uint64_t value : {0u, 0xFFFFFFFFu}) { + for (uint64_t weight : {1u, 0xFFFFFFFFu}) { + const size_t kSamplesToAdd = weight == 1 ? 100 : 1; + WindowedAnalyzer analyzer(&client, &shared_client); + AddSamplesHelper(&analyzer, value, weight, kSamplesToAdd); + + // TestWindowedAnalyzerClient scales the result by 2. + uint64_t expected_value = + value * TestWindowedAnalyzerClient::result_scale; + // Makes sure our precision is good enough. + EXPECT_EQ(analyzer.ComputeWorstMean().value, expected_value) + << value << " x " << weight; + EXPECT_EQ(analyzer.ComputeWorstRMS().value, expected_value) + << value << " x " << weight; + EXPECT_NEAR_SMR(analyzer.ComputeWorstSMR().value, expected_value, weight) + << value << " x " << weight; + } + } +} + +// Verify that the worst values and their time regions are properly tracked +// seperately for mean, SMR, and RMS. +TEST(FrameMetricsWindowedAnalyzerTest, AllResultsDifferent) { + const size_t kMaxWindowSize = 6; // Same as the pattern length. + const uint32_t kSampleWeight = 100; + + TestWindowedAnalyzerClient client; + SharedWindowedAnalyzerClient shared_client( + kMaxWindowSize, base::TimeTicks(), + base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + WindowedAnalyzer analyzer(&client, &shared_client); + + // Used to "clear" all the windowed accumulators. + const std::vector<uint32_t> pattern_clear = {0, 0, 0, 0, 0, 0}; + // Worst mean pattern: mean of 3, smr of 1.5, rms of ~4.2. + const std::vector<uint32_t> pattern_max_mean = {0, 6, 0, 6, 0, 6}; + double expected_worst_mean = + 3 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale; + // Lots of small janks maximizes the SMR. + // Worst SMR pattern: mean of 2, smr of 2, rms of 2. + const std::vector<uint32_t> pattern_max_smr = {2, 2, 2, 2, 2, 2}; + double expected_worst_smr = + 2 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale; + // A few big janks dominate RMS. + // Worst RMS pattern: Mean of 2, smr of ~.3, rms of ~4.9 + const std::vector<uint32_t> pattern_max_rms = {0, 0, 0, 0, 0, 12}; + double expected_worst_rms = std::sqrt((12 * 12) / 6) * kFixedPointMultiplier * + TestWindowedAnalyzerClient::result_scale; + + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_max_mean, kSampleWeight); + SharedWindowedAnalyzerClient worst_mean_client(shared_client); + + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_max_smr, kSampleWeight); + SharedWindowedAnalyzerClient worst_smr_client(shared_client); + + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_max_rms, kSampleWeight); + SharedWindowedAnalyzerClient worst_rms_client(shared_client); + + // If there is a tie, the first window detected wins. + // This can go wrong if there's any accumulation of error because the + // values added aren't exactly the same as the values removed. + // This only catches accumulation of error in one direction, so isn't + // thorough, but it does help improve coverage. + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_max_mean, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_max_smr, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_max_rms, kSampleWeight); + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + + FrameRegionResult worst_mean = analyzer.ComputeWorstMean(); + EXPECT_DOUBLE_EQ(expected_worst_mean, worst_mean.value); + EXPECT_EQ(worst_mean_client.window_begin, worst_mean.window_begin); + 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_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_EQ(worst_rms_client.window_begin, worst_rms.window_begin); + EXPECT_EQ(worst_rms_client.window_end, worst_rms.window_end); +} + +// Verify that the worst values and their time regions are properly tracked +// even before a full window's worth is available. +TEST(FrameMetricsWindowedAnalyzerTest, SmallSampleSize) { + const size_t kMaxWindowSize = 6; // Bigger than the pattern length. + const uint32_t kSampleWeight = 100; + + TestWindowedAnalyzerClient client; + SharedWindowedAnalyzerClient shared_client( + kMaxWindowSize, base::TimeTicks(), + base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + WindowedAnalyzer analyzer(&client, &shared_client); + + const std::vector<uint32_t> pattern_short = {2, 2, 2}; + double expected_initial_value = + 2 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale; + AddPatternHelper(&shared_client, &analyzer, pattern_short, kSampleWeight); + SharedWindowedAnalyzerClient short_client(shared_client); + + FrameRegionResult worst_mean = analyzer.ComputeWorstMean(); + EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value); + EXPECT_EQ(short_client.window_begin, worst_mean.window_begin); + 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_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_EQ(short_client.window_begin, worst_rms.window_begin); + EXPECT_EQ(short_client.window_end, worst_rms.window_end); +} + +// Verify that a few bad values at the start don't dominate the result. +TEST(FrameMetricsWindowedAnalyzerTest, BadFirstSamples) { + const size_t kMaxWindowSize = 6; + const uint32_t kSampleWeight = 100; + FrameRegionResult worst_mean, worst_smr, worst_rms; + + TestWindowedAnalyzerClient client; + SharedWindowedAnalyzerClient shared_client( + kMaxWindowSize, base::TimeTicks(), + base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + WindowedAnalyzer analyzer(&client, &shared_client); + + // The 7's at the start will dominate the result if the implemenationd + // doesn't only start remembering the worst values after receiving at least + // a window's worth of samples. + const std::vector<uint32_t> pattern_short = {7, 7}; + double expected_initial_value = + 7 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale; + AddPatternHelper(&shared_client, &analyzer, pattern_short, kSampleWeight); + SharedWindowedAnalyzerClient short_client(shared_client); + + worst_mean = analyzer.ComputeWorstMean(); + EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value); + EXPECT_EQ(short_client.window_begin, worst_mean.window_begin); + 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_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_EQ(short_client.window_begin, worst_rms.window_begin); + EXPECT_EQ(short_client.window_end, worst_rms.window_end); + + // Clear the window. + const std::vector<uint32_t> pattern_clear = {0, 0, 0, 0, 0, 0}; + AddPatternHelper(&shared_client, &analyzer, pattern_clear, kSampleWeight); + + // Make sure a new worst window with results less than 7 is detected. + const std::vector<uint32_t> pattern_long = {6, 6, 6, 6, 6, 6}; + double expected_final_value = + 6 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale; + AddPatternHelper(&shared_client, &analyzer, pattern_long, kSampleWeight); + SharedWindowedAnalyzerClient long_client(shared_client); + + worst_mean = analyzer.ComputeWorstMean(); + EXPECT_DOUBLE_EQ(expected_final_value, worst_mean.value); + EXPECT_EQ(long_client.window_begin, worst_mean.window_begin); + 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_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_EQ(long_client.window_begin, worst_rms.window_begin); + EXPECT_EQ(long_client.window_end, worst_rms.window_end); +} + +// Verify ResetAccumulators is continuous across the reset boundary. +TEST(FrameMetricsWindowedAnalyzerTest, ResetWorstValues) { + const size_t kMaxWindowSize = 6; // Same as the pattern length. + const uint32_t kSampleWeight = 100; + FrameRegionResult worst_mean, worst_smr, worst_rms; + + TestWindowedAnalyzerClient client; + SharedWindowedAnalyzerClient shared_client( + kMaxWindowSize, base::TimeTicks(), + base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + WindowedAnalyzer analyzer(&client, &shared_client); + + // Start off with the worst pattern. + const std::vector<uint32_t> pattern1 = {9, 9, 9, 9, 9, 9}; + double expected_initial_value = + 9 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale; + AddPatternHelper(&shared_client, &analyzer, pattern1, kSampleWeight); + SharedWindowedAnalyzerClient initial_client(shared_client); + + worst_mean = analyzer.ComputeWorstMean(); + EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value); + EXPECT_EQ(initial_client.window_begin, worst_mean.window_begin); + 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_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_EQ(initial_client.window_begin, worst_rms.window_begin); + EXPECT_EQ(initial_client.window_end, worst_rms.window_end); + + // The 4's below will affect the window, even after a reset, but + // won't affect the current worst values. + const std::vector<uint32_t> pattern2 = {4, 4, 4, 4, 4, 4}; + AddPatternHelper(&shared_client, &analyzer, pattern2, kSampleWeight); + + worst_mean = analyzer.ComputeWorstMean(); + EXPECT_DOUBLE_EQ(expected_initial_value, worst_mean.value); + EXPECT_EQ(initial_client.window_begin, worst_mean.window_begin); + 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_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_EQ(initial_client.window_begin, worst_rms.window_begin); + EXPECT_EQ(initial_client.window_end, worst_rms.window_end); + + // Reset the worst value. This should not destroy sample history or + // any accumulators. + analyzer.ResetWorstValues(); + + // The first 4 below will be included with the previous 4's to detect an + // entire window of results, even though we've reset the worst values. + const std::vector<uint32_t> pattern3 = {4}; + double expected_final_value = + 4 * kFixedPointMultiplier * TestWindowedAnalyzerClient::result_scale; + AddPatternHelper(&shared_client, &analyzer, pattern3, kSampleWeight); + SharedWindowedAnalyzerClient final_client(shared_client); + + // Add a window of 1's here to verify it does not affect the window of 4's. + const std::vector<uint32_t> pattern4 = {1, 1, 1, 1, 1, 1}; + AddPatternHelper(&shared_client, &analyzer, pattern4, kSampleWeight); + + worst_mean = analyzer.ComputeWorstMean(); + EXPECT_DOUBLE_EQ(expected_final_value, worst_mean.value); + EXPECT_EQ(final_client.window_begin, worst_mean.window_begin); + 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_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_EQ(final_client.window_begin, worst_rms.window_begin); + EXPECT_EQ(final_client.window_end, worst_rms.window_end); +} + +// WindowedAnalyzerNaive is a version of WindowedAnalyzer that doesn't use +// fixed point math and can accumulate error, even with double precision +// accumulators. This is used to verify patterns that accumulate error without +// fixed point math, so we can then verify those patterns don't result in +// acculated error in the actual implementation. +class WindowedAnalyzerNaive { + public: + WindowedAnalyzerNaive(size_t max_window_size) + : max_window_size_(max_window_size) {} + + void AddSample(uint32_t value, + uint32_t weight, + uint64_t weighted_value, + uint64_t weighted_root, + const Accumulator96b& weighted_square) { + if (history_.size() >= max_window_size_) { + Sample old = history_.front(); + history_.pop_front(); + naive_accumulator_ -= static_cast<double>(old.weight) * old.value; + naive_root_accumulator_ -= + static_cast<double>(old.weight) * std::sqrt(old.value); + naive_square_accumulator_ -= + static_cast<double>(old.weight) * old.value * old.value; + naive_total_weight_ -= old.weight; + } + + history_.push_back({value, weight}); + naive_accumulator_ += static_cast<double>(weight) * value; + naive_root_accumulator_ += static_cast<double>(weight) * std::sqrt(value); + naive_square_accumulator_ += static_cast<double>(weight) * value * value; + naive_total_weight_ += weight; + } + + struct Sample { + uint32_t value; + uint32_t weight; + }; + + const size_t max_window_size_; + double naive_accumulator_ = 0; + double naive_root_accumulator_ = 0; + double naive_square_accumulator_ = 0; + double naive_total_weight_ = 0; + base::circular_deque<Sample> history_; +}; + +// A version of the WindowedAnalyzer that allows us to inspect the internal +// state for testing purposes. +class TestWindowedAnalyzer : public WindowedAnalyzer { + public: + TestWindowedAnalyzer(const WindowedAnalyzerClient* client, + const SharedWindowedAnalyzerClient* shared_client) + : WindowedAnalyzer(client, shared_client) {} + ~TestWindowedAnalyzer() override {} + + double CurrentAccumulator() { return accumulator_; } + double CurrentRootAccumulator() { return root_accumulator_; } + double CurrentSquareAccumulator() { return square_accumulator_.ToDouble(); } +}; + +// This test verifies that it's easy to blow the dynamic range of a floating +// point accumulator with a particular pattern. Then it verifies that same +// pattern does not result in error in the actual implementation. +void TestNoAccumulatedPrecisionError(uint32_t big_value, + uint32_t small_value, + double naive_root_error_floor, + double naive_square_error_floor) { + const size_t kRuns = 1000; + const size_t kMaxWindowSize = 6; // Same as the pattern length. + const std::vector<uint32_t> pattern_clear = {0, 0, 0, 0, 0, 0}; + const std::vector<uint32_t> pattern_bad = {big_value, small_value, + small_value, small_value, + small_value, small_value}; + + // Set up the actual WindowedAnalyzer implementation. + TestWindowedAnalyzerClient client_impl; + SharedWindowedAnalyzerClient shared_client_impl( + kMaxWindowSize, base::TimeTicks(), + base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + TestWindowedAnalyzer analyzer_impl(&client_impl, &shared_client_impl); + + // Set up the naive WindowedAnalyzer implementation. + SharedWindowedAnalyzerClient shared_client_naive( + kMaxWindowSize, base::TimeTicks(), + base::TimeTicks() + base::TimeDelta::FromSeconds(1)); + WindowedAnalyzerNaive analyzer_naive(kMaxWindowSize); + + // Verify error keeps accumulating each time the bad pattern is applied. + // Note: We don't expect error in the mean accumulator since the values added + // are already truncated to whole integers before being passed in via + // AddSamples(). + 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); + EXPECT_EQ(0, analyzer_naive.naive_accumulator_); + EXPECT_ABS_LT(naive_root_accumulator_prev, + analyzer_naive.naive_root_accumulator_); + EXPECT_ABS_LT(naive_square_accumulator_prev, + analyzer_naive.naive_square_accumulator_); + naive_root_accumulator_prev = analyzer_naive.naive_root_accumulator_; + naive_square_accumulator_prev = analyzer_naive.naive_square_accumulator_; + } + // Verify naive error is bigger than some threshold after kRuns. + EXPECT_ABS_LE(naive_root_error_floor * kRuns, + 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 +// double accumulator. Doubles have 53 significand bits. When cubed, the +// difference between the small and big values below require just over 53 bits. +TEST(FrameMetricsWindowedAnalyzerTest, NoAccumulatedPrecisionErrorBasic) { + constexpr uint32_t big = 1 << 19; + constexpr uint32_t small = 2; + TestNoAccumulatedPrecisionError(big, small, 5e-8, 60); +} + +// This is a more realistic scenario with orders of magnitude we are likely +// to see in actual data. The error is small, but can become significant over +// time. +TEST(FrameMetricsWindowedAnalyzerTest, NoAccumulatedPrecisionErrorBig) { + constexpr uint32_t big = 1 * base::TimeTicks::kMicrosecondsPerSecond; + constexpr uint32_t small = 1 * base::TimeTicks::kMicrosecondsPerMillisecond; + TestNoAccumulatedPrecisionError(big, small, 7e-8, 256); +} + +// This is a scenario with orders of magnitude that we can see in our data, +// but that will be rare. Even after a single bad pattern, the error is +// significant. +TEST(FrameMetricsWindowedAnalyzerTest, NoAccumulatedPrecisionErrorBigger) { + constexpr uint32_t big = 20 * base::TimeTicks::kMicrosecondsPerSecond; + constexpr uint32_t small = 1 * base::TimeTicks::kMicrosecondsPerMillisecond; + TestNoAccumulatedPrecisionError(big, small, 2e-5, 1e6); +} + +} // namespace +} // namespace frame_metrics +} // namespace ui diff --git a/chromium/ui/login/account_picker/md_screen_account_picker.css b/chromium/ui/login/account_picker/md_screen_account_picker.css index 39b9828bebf..258a6cad035 100644 --- a/chromium/ui/login/account_picker/md_screen_account_picker.css +++ b/chromium/ui/login/account_picker/md_screen_account_picker.css @@ -7,7 +7,8 @@ transition: width 180ms ease, height 180ms ease; } -#bubble { +#bubble, +#bubble-persistent { margin-top: 16px; z-index: 1; } @@ -45,6 +46,30 @@ html[dir=rtl] #signin-banner { right: -50%; } +#signin-banner.warning { + -webkit-padding-start: 128px; + background-color: transparent; + color: rgb(215, 68, 57); + font-size: 10px; +} + +#signin-banner.warning::before { + background-image: url(../../webui/resources/images/icon_error_outline.svg); + background-size: 20px; + content: ''; + height: 20px; + left: 24px; + position: absolute; + transform: translateY(-50%); + top: 50%; + width: 20px; +} + +html[dir=rtl] #signin-banner.warning::before { + left: auto; + right: 24px; +} + html[screen=user-adding] #signin-banner { background-color: rgba(0, 0, 0, 0.34); display: inline-block; @@ -55,6 +80,11 @@ html[screen=login] #signin-banner { padding: 20px 24px; } +html[screen=user-adding] #signin-banner.warning, +html[screen=login] #signin-banner.warning { + -webkit-padding-start: 52px; /* 24 + 20(icon width) + 8 (icon padding). */ +} + html[screen=login] #signin-banner, html[screen=lock] #signin-banner { display: inline-block; @@ -117,4 +147,4 @@ html[screen=lock] #signin-banner.message-set { .small-pod-container-mask.rotate { transform: rotate(180deg); -}
\ No newline at end of file +} diff --git a/chromium/ui/login/account_picker/md_screen_account_picker.js b/chromium/ui/login/account_picker/md_screen_account_picker.js index f143c032eb2..441f996b2fd 100644 --- a/chromium/ui/login/account_picker/md_screen_account_picker.js +++ b/chromium/ui/login/account_picker/md_screen_account_picker.js @@ -14,6 +14,14 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { */ var MAX_LOGIN_ATTEMPTS_IN_POD = 3; + /** + * Time after which the sign-in error bubble should be hidden if it is + * overlayed over the detachable base change warning bubble (to ensure that + * the detachable base warning is not obscured indefinitely). + * @const {number} + */ + var SIGNIN_ERROR_OVER_DETACHABLE_BASE_WARNING_TIMEOUT_MS = 5000; + return { EXTERNAL_API: [ 'loadUsers', @@ -30,6 +38,7 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { 'hideUserPodCustomIcon', 'setUserPodFingerprintIcon', 'removeUserPodFingerprintIcon', + 'selectPodForDetachableBaseWarningBubble', 'setPinEnabledForUser', 'setAuthType', 'setTabletModeState', @@ -173,6 +182,7 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { */ onBeforeHide: function() { $('pod-row').clearFocusedPod(); + $('bubble-persistent').hide(); this.showing_ = false; chrome.send('loginUIStateChanged', ['account-picker', false]); $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN; @@ -205,11 +215,66 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { } // Update the pod row display if incorrect password. $('pod-row').setFocusedPodErrorDisplay(true); - activatedPod.showBubble(error); + + // If a warning that the detachable base is different than the one + // previously used by the user is shown for the pod, make sure that the + // sign-in error gets hidden reasonably soon. + // If the detachable base was changed maliciously while the user was + // away, the attacker might attempt to use the sign-in error but to + // obscure the detachable base warning hoping that the user will miss it + // when they get back to the device. + var timeout = activatedPod.showingDetachableBaseWarningBubble() ? + SIGNIN_ERROR_OVER_DETACHABLE_BASE_WARNING_TIMEOUT_MS : + undefined; + activatedPod.showBubble(error, {timeout: timeout}); } }, /** + * Ensures that a user pod is selected and focused, and thus ready to show a + * warning bubble for detachable base change. This is needed for two + * reasons: + * 1. The detachable base state is associated with a user, so a user pod + * has to be selected in order to know for which user the detachable + * base state should be considered (e.g. there might be two large user + * pods in the account picker). + * 2. The warning bubble is attached to the pod's auth element, which is + * only shown if the pod is focused. The bubble anchor should be + * visible in order to properly calculate the bubble position. + */ + selectPodForDetachableBaseWarningBubble: function() { + $('pod-row').maybePreselectPod(); + }, + + /** + * Shows a persistent bubble warning to the user that the current detachable + * base is different than the one they were last using, and that it might + * not be trusted. + * + * @param {string} username The username of the user under whose user pod + * the warning should be displayed. + * @param {HTMLElement} content The warning bubble content. + */ + showDetachableBaseWarningBubble: function(username, content) { + var podRow = $('pod-row'); + var pod = podRow.pods.find(pod => pod.user.username == username); + if (pod) + pod.showDetachableBaseWarningBubble(content); + }, + + /** + * Hides the detachable base warning for the user. + * + * @param {string} username The username that identifies the user pod from + * under which the detachable base warning bubble should be removed. + */ + hideDetachableBaseWarningBubble: function(username) { + var pod = $('pod-row').pods.find(pod => pod.user.username == username); + if (pod) + pod.hideDetachableBaseWarningBubble(); + }, + + /** * Loads given users in pod row. * @param {array} users Array of user. * @param {boolean} showGuest Whether to show guest session button. @@ -301,9 +366,10 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { * this function updates the message in the banner. This function is used * by the chrome.screenlockPrivate.showMessage API. * @param {string} message Text to be displayed or empty to hide the banner. + * @param {boolean} isWarning True if the given message is a warning. */ - showBannerMessage: function(message) { - $('pod-row').showBannerMessage(message); + showBannerMessage: function(message, isWarning) { + $('pod-row').showBannerMessage(message, isWarning); }, /** diff --git a/chromium/ui/login/account_picker/md_user_pod_row.css b/chromium/ui/login/account_picker/md_user_pod_row.css index eca51094800..75a143df1d3 100644 --- a/chromium/ui/login/account_picker/md_user_pod_row.css +++ b/chromium/ui/login/account_picker/md_user_pod_row.css @@ -33,13 +33,27 @@ pin-keyboard { } .pod .pin-container { - left: 36px; position: absolute; - top: 300px; width: 234px; z-index: 3; } +.pod .pin-container.pin-enabled { + left: 36px; + opacity: 1; + top: 300px; + transition: opacity 200ms ease-in-out 180ms; + visibility: visible; +} + +.pod .pin-container.pin-disabled { + opacity: 0; /* Opacity is set to 1 after the pin element is loaded. */ + transition: none; + visibility: hidden; /* Needed because pin-keyboard's offsetHeight is + checked to determine if loaded. */ + z-index: -1; +} + .pod.faded { opacity: .75; } @@ -634,7 +648,7 @@ html[dir=rtl] .user-type-icon-area { } .fingerprint-icon-container.failed .fingerprint-icon-image { - background-image: url(../../webui/resources/images/fingerprint_failed.svg); + background-image: url(../../webui/resources/images/icon_error_outline.svg); } .action-box-menu { diff --git a/chromium/ui/login/account_picker/md_user_pod_row.js b/chromium/ui/login/account_picker/md_user_pod_row.js index 299342e0366..8d328140653 100644 --- a/chromium/ui/login/account_picker/md_user_pod_row.js +++ b/chromium/ui/login/account_picker/md_user_pod_row.js @@ -30,6 +30,7 @@ cr.define('login', function() { var SCROLL_MASK_HEIGHT = 112; var CROS_POD_HEIGHT_WITH_PIN = 618; var PUBLIC_SESSION_ICON_WIDTH = 12; + var CROS_POD_WARNING_BANNER_OFFSET_Y = 270; /** * The maximum number of users that each pod placement method can handle. @@ -777,6 +778,15 @@ cr.define('login', function() { */ pinEnabled: false, + /** + * If set, a function which hides a persistent detachable base warning + * bubble. This will be set if a detachable base warning bubble is shown for + * this pod. + * @type {?function()} + * @private + */ + detachableBaseWarningBubbleHider_: null, + /** @override */ decorate: function() { this.tabIndex = UserPodTabOrder.POD_INPUT; @@ -1573,7 +1583,7 @@ cr.define('login', function() { // global focus change event. Sometimes focus requests are ignored while // loading the page. See crbug.com/725622. if (opt_ensureFocus) { - var INTERVAL_REPEAT_MS = 10 + var INTERVAL_REPEAT_MS = 10; var input = this.mainInput; var intervalId = setInterval(function() { input.focus(); @@ -1674,30 +1684,64 @@ cr.define('login', function() { }, /** + * Returns the element that should be used as the anchor for error bubbles + * associated with the pod. + * + * @return {HTMLElement} The anchor for error bubbles. + * @private + */ + getBubbleAnchor_: function() { + var bubbleAnchor = this.getElementsByClassName('auth-container')[0]; + if (!bubbleAnchor) { + console.error('auth-container not found!'); + bubbleAnchor = this.mainInput; + } + return bubbleAnchor; + }, + + /** * Shows a bubble under the auth-container of the user pod. * @param {HTMLElement} content Content to show in bubble. - */ - showBubble: function(content) { + * @param {!{bubble: (HTMLElement|undefined), + * anchor: (HTMLElement|undefined), + * timeout: (number|undefined)}|undefined} opt_options The custom + * options describing how the bubble should be shown: + * <ul> + * <li>bubble: The element that hosts the bubble content.</li> + * <li> + * anchor: The element to which the bubble should be anchored. + * </li> + * <li> + * timeout: Amount of time in ms after which the bubble + * should be hidden. Note: this should only be used for + * {@code $('bubble')} bubble element. The timeout will get + * cleared if the bubble is shown again. + * </li> + * </ul> + * @return {function()} Function that, when called, hides the shown bubble. + */ + showBubble: function(content, opt_options) { /** @const */ var BUBBLE_OFFSET = 25; // -8 = 4(BUBBLE_POD_OFFSET) - 2(bubble margin) // - 10(internal bubble adjustment) var bubblePositioningPadding = -8; - var bubbleAnchor; - var attachment; - // Anchor the bubble to the input field. - bubbleAnchor = this.getElementsByClassName('auth-container')[0]; - if (!bubbleAnchor) { - console.error('auth-container not found!'); - bubbleAnchor = this.mainInput; + var options = opt_options || {}; + var bubble = options.bubble || $('bubble'); + + // Make sure bubble timeout is changed only for $('bubble') element. + if (options.timeout && bubble != $('bubble')) { + console.error('Timeout can be set only when showing #bubble element.'); + return; } + + var bubbleAnchor = options.anchor || this.getBubbleAnchor_(); + var attachment; if (this.pinContainer && this.pinContainer.style.visibility == 'visible') attachment = cr.ui.Bubble.Attachment.RIGHT; else attachment = cr.ui.Bubble.Attachment.BOTTOM; - var bubble = $('bubble'); - // Cannot use cr.ui.LoginUITools.get* on bubble until it is attached to // the element. getMaxHeight/Width rely on the correct up/left element // side positioning that doesn't happen until bubble is attached. @@ -1734,14 +1778,89 @@ cr.define('login', function() { attachment = cr.ui.Bubble.Attachment.LEFT; } } + + if (bubble == $('bubble')) + this.clearBubbleHideTimeout_(); + + var state = {shown: false, hidden: false}; + var showBubbleCallback = function() { this.removeEventListener('transitionend', showBubbleCallback); - $('bubble').showContentForElement( + // If the bubble was requested to be hidden while the transition was in + // progress, do not show the bubble. + if (state.hidden) + return; + + state.shown = true; + + bubble.showContentForElement( bubbleAnchor, attachment, content, BUBBLE_OFFSET, bubblePositioningPadding, true); - }; + + if (options.timeout != undefined) { + this.hideBubbleTimeout_ = setTimeout(() => { + this.hideBubbleTimeout_ = undefined; + bubble.hideForElement(bubbleAnchor); + }, options.timeout); + } + }.bind(this); this.addEventListener('transitionend', showBubbleCallback); ensureTransitionEndEvent(this); + + return function() { + if (state.hidden) + return; + + state.hidden = true; + if (state.shown) + bubble.hideForElement(bubbleAnchor); + }; + }, + + /** + * Clears the timeout to hide a bubble, if a bubble timeout was set. + * @private + */ + clearBubbleHideTimeout_: function() { + if (this.hideBubbleTimeout_) { + clearTimeout(this.hideBubbleTimeout_); + this.hideBubbleTimeout_ = null; + } + }, + + /** + * Shows persistent bubble for detachable base change warning. + * @param {HTMLElement} content The bubble contens. + */ + showDetachableBaseWarningBubble: function(content) { + var anchor = this.getBubbleAnchor_(); + if (!anchor) + return; + this.clearBubbleHideTimeout_(); + $('bubble').hideForElement(anchor); + this.detachableBaseWarningBubbleHider_ = this.showBubble( + content, {bubble: $('bubble-persistent'), anchor: anchor}); + }, + + /** + * If a peristent bubble for detachable base change warning is shown (and + * anchored at this pod), hides the bubble. + */ + hideDetachableBaseWarningBubble: function() { + if (this.detachableBaseWarningBubbleHider_) { + this.detachableBaseWarningBubbleHider_(); + this.detachableBaseWarningBubbleHider_ = null; + } + }, + + /** + * Whether a detachable base warning bubble is being shown for this pod. + * @return {boolean} + */ + showingDetachableBaseWarningBubble: function() { + return this.detachableBaseWarningBubbleHider_ && + !$('bubble-persistent').hidden && + $('bubble-persistent').anchor == this.getBubbleAnchor_(); }, /** @@ -1756,8 +1875,8 @@ cr.define('login', function() { this.classList.toggle('signing-in', false); if (takeFocus) { if (!this.multiProfilesPolicyApplied) { - // This will set a custom tab order. - this.focusInput(true /*opt_ensureFocus*/); + this.focusInput( + this.mainInput.tagName == 'INPUT' /*opt_ensureFocus*/); } } else @@ -4067,7 +4186,12 @@ cr.define('login', function() { var bannerContainer = $('signin-banner-container1'); if (bannerContainer.hidden) return; - bannerContainer.style.top = cr.ui.toCssPx(this.mainPod_.top / 2); + if ($('signin-banner').classList.contains('warning')) { + bannerContainer.style.top = + cr.ui.toCssPx(this.mainPod_.top + CROS_POD_WARNING_BANNER_OFFSET_Y); + } else { + bannerContainer.style.top = cr.ui.toCssPx(this.mainPod_.top / 2); + } if (this.pods.length <= POD_ROW_LIMIT) { bannerContainer.style.left = cr.ui.toCssPx( (this.screenSize.width - bannerContainer.offsetWidth) / 2); @@ -4211,11 +4335,13 @@ cr.define('login', function() { * Displays a banner containing |message|. If the banner is already present * this function updates the message in the banner. * @param {string} message Text to be displayed or empty to hide the banner. + * @param {boolean} isWarning True if the given message is a warning. */ - showBannerMessage: function(message) { + showBannerMessage: function(message, isWarning) { var banner = $('signin-banner'); banner.textContent = message; banner.classList.toggle('message-set', !!message); + banner.classList.toggle('warning', isWarning); $('signin-banner-container1').hidden = banner.textContent.length == 0; this.updateSigninBannerPosition_(); }, diff --git a/chromium/ui/login/account_picker/screen_account_picker.js b/chromium/ui/login/account_picker/screen_account_picker.js index dd272b971c4..ed42b4a2c1f 100644 --- a/chromium/ui/login/account_picker/screen_account_picker.js +++ b/chromium/ui/login/account_picker/screen_account_picker.js @@ -21,506 +21,502 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { */ var BUBBLE_POD_OFFSET = 4; - return { - EXTERNAL_API: [ - 'loadUsers', - 'runAppForTesting', - 'setApps', - 'setShouldShowApps', - 'showAppError', - 'updateUserImage', - 'setCapsLockState', - 'forceLockedUserPodFocus', - 'removeUser', - 'showBannerMessage', - 'showUserPodCustomIcon', - 'hideUserPodCustomIcon', - 'setUserPodFingerprintIcon', - 'removeUserPodFingerprintIcon', - 'setPinEnabledForUser', - 'setAuthType', - 'setTabletModeState', - 'setPublicSessionDisplayName', - 'setPublicSessionLocales', - 'setPublicSessionKeyboardLayouts', - 'setLockScreenAppsState', - ], - - preferredWidth_: 0, - preferredHeight_: 0, - - // Whether this screen is shown for the first time. - firstShown_: true, - - // Whether this screen is currently being shown. - showing_: false, - - // Last reported lock screen app activity state. - lockScreenAppsState_: LOCK_SCREEN_APPS_STATE.NONE, - - /** @override */ - decorate: function() { - login.PodRow.decorate($('pod-row')); - this.ownerDocument.addEventListener('click', - this.handleOwnerDocClick_.bind(this)); - }, - - /** @override */ - getPreferredSize: function() { - return {width: this.preferredWidth_, height: this.preferredHeight_}; - }, - - /** @override */ - onWindowResize: function() { - $('pod-row').onWindowResize(); - - // Reposition the error bubble, if it is showing. Since we are just - // moving the bubble, the number of login attempts tried doesn't matter. - var errorBubble = $('bubble'); - if (errorBubble && !errorBubble.hidden) - this.showErrorBubble(0, undefined /* Reuses the existing message. */); - }, - - /** - * Sets preferred size for account picker screen. - */ - setPreferredSize: function(width, height) { - this.preferredWidth_ = width; - this.preferredHeight_ = height; - }, - - /** - * When the account picker is being used to lock the screen, pressing the - * exit accelerator key will sign out the active user as it would when - * they are signed in. - */ - exit: function() { - // Check and disable the sign out button so that we can never have two - // sign out requests generated in a row. - if ($('pod-row').lockedPod && !$('sign-out-user-button').disabled) { - $('sign-out-user-button').disabled = true; - chrome.send('signOutUser'); - } - }, - - /* Cancel user adding if ESC was pressed. - */ - cancel: function() { - if (Oobe.getInstance().displayType == DISPLAY_TYPE.USER_ADDING) - chrome.send('cancelUserAdding'); - }, - - /** - * Event handler that is invoked just after the frame is shown. - * @param {string} data Screen init payload. - */ - onAfterShow: function(data) { - $('pod-row').handleAfterShow(); - }, - - /** - * Event handler that is invoked just before the frame is shown. - * @param {string} data Screen init payload. - */ - onBeforeShow: function(data) { - this.showing_ = true; - chrome.send('loginUIStateChanged', ['account-picker', true]); - $('login-header-bar').signinUIState = SIGNIN_UI_STATE.ACCOUNT_PICKER; - // Header bar should be always visible on Account Picker screen. - Oobe.getInstance().headerHidden = false; - chrome.send('hideCaptivePortal'); - var podRow = $('pod-row'); - podRow.handleBeforeShow(); - - // In case of the preselected pod onShow will be called once pod - // receives focus. - if (!podRow.preselectedPod) - this.onShow(); - }, - - /** - * Event handler invoked when the page is shown and ready. - */ - onShow: function() { - if (!this.showing_) { - // This method may be called asynchronously when the pod row finishes - // initializing. However, at that point, the screen may have been hidden - // again already. If that happens, ignore the onShow() call. - return; - } - chrome.send('getTabletModeState'); - if (!this.firstShown_) return; - this.firstShown_ = false; - - // Ensure that login is actually visible. - window.requestAnimationFrame(function() { - chrome.send('accountPickerReady'); - chrome.send('loginVisible', ['account-picker']); - }); - }, - - /** - * Event handler that is invoked just before the frame is hidden. - */ - onBeforeHide: function() { - $('pod-row').clearFocusedPod(); - this.showing_ = false; - chrome.send('loginUIStateChanged', ['account-picker', false]); - $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN; - $('pod-row').handleHide(); - }, - - /** - * Shows sign-in error bubble. - * @param {number} loginAttempts Number of login attemps tried. - * @param {HTMLElement} content Content to show in bubble. - */ - showErrorBubble: function(loginAttempts, error) { - var activatedPod = $('pod-row').activatedPod; - if (!activatedPod) { - $('bubble').showContentForElement($('pod-row'), - cr.ui.Bubble.Attachment.RIGHT, - error); - return; - } - // Show web authentication if this is not a supervised user. - if (loginAttempts > MAX_LOGIN_ATTEMPTS_IN_POD && - !activatedPod.user.supervisedUser) { - chrome.send('maxIncorrectPasswordAttempts', - [activatedPod.user.emailAddress]); - activatedPod.showSigninUI(); - } else { - if (loginAttempts == 1) { - chrome.send('firstIncorrectPasswordAttempt', - [activatedPod.user.emailAddress]); - } - // Update the pod row display if incorrect password. - $('pod-row').setFocusedPodErrorDisplay(true); - - /** @const */ var BUBBLE_OFFSET = 25; - // -8 = 4(BUBBLE_POD_OFFSET) - 2(bubble margin) - // - 10(internal bubble adjustment) - var bubblePositioningPadding = -8; - - var bubbleAnchor; - var attachment; - if (activatedPod.pinContainer && - activatedPod.pinContainer.style.visibility == 'visible') { - // Anchor the bubble to the input field. - bubbleAnchor = ( - activatedPod.getElementsByClassName('auth-container'))[0]; - if (!bubbleAnchor) { - console.error('auth-container not found!'); - bubbleAnchor = activatedPod.mainInput; - } - attachment = cr.ui.Bubble.Attachment.RIGHT; - } else { - // Anchor the bubble to the pod instead of the input. - bubbleAnchor = activatedPod; - attachment = cr.ui.Bubble.Attachment.BOTTOM; - } - - var bubble = $('bubble'); - - // Cannot use cr.ui.LoginUITools.get* on bubble until it is attached to - // the element. getMaxHeight/Width rely on the correct up/left element - // side positioning that doesn't happen until bubble is attached. - var maxHeight = - cr.ui.LoginUITools.getMaxHeightBeforeShelfOverlapping(bubbleAnchor) - - bubbleAnchor.offsetHeight - BUBBLE_POD_OFFSET; - var maxWidth = cr.ui.LoginUITools.getMaxWidthToFit(bubbleAnchor) - - bubbleAnchor.offsetWidth - BUBBLE_POD_OFFSET; - - // Change bubble visibility temporary to calculate height. - var bubbleVisibility = bubble.style.visibility; - bubble.style.visibility = 'hidden'; - bubble.hidden = false; - // Now we need the bubble to have the new content before calculating - // size. Undefined |error| == reuse old content. - if (error !== undefined) - bubble.replaceContent(error); - - // Get bubble size. - var bubbleOffsetHeight = parseInt(bubble.offsetHeight); - var bubbleOffsetWidth = parseInt(bubble.offsetWidth); - // Restore attributes. - bubble.style.visibility = bubbleVisibility; - bubble.hidden = true; - - if (attachment == cr.ui.Bubble.Attachment.BOTTOM) { - // Move error bubble if it overlaps the shelf. - if (maxHeight < bubbleOffsetHeight) - attachment = cr.ui.Bubble.Attachment.TOP; - } else { - // Move error bubble if it doesn't fit screen. - if (maxWidth < bubbleOffsetWidth) { - bubblePositioningPadding = 2; - attachment = cr.ui.Bubble.Attachment.LEFT; - } - } - var showBubbleCallback = function() { - activatedPod.removeEventListener("transitionend", - showBubbleCallback); - $('bubble').showContentForElement(bubbleAnchor, - attachment, - error, - BUBBLE_OFFSET, - bubblePositioningPadding, true); - }; - activatedPod.addEventListener("transitionend", - showBubbleCallback); - ensureTransitionEndEvent(activatedPod); - } - }, - - /** - * Loads given users in pod row. - * @param {array} users Array of user. - * @param {boolean} showGuest Whether to show guest session button. - */ - loadUsers: function(users, showGuest) { - $('pod-row').loadPods(users); - $('login-header-bar').showGuestButton = showGuest; - // On Desktop, #login-header-bar has a shadow if there are 8+ profiles. - if (Oobe.getInstance().displayType == DISPLAY_TYPE.DESKTOP_USER_MANAGER) - $('login-header-bar').classList.toggle('shadow', users.length > 8); - }, - - /** - * Runs app with a given id from the list of loaded apps. - * @param {!string} app_id of an app to run. - * @param {boolean=} opt_diagnostic_mode Whether to run the app in - * diagnostic mode. Default is false. - */ - runAppForTesting: function(app_id, opt_diagnostic_mode) { - $('pod-row').findAndRunAppForTesting(app_id, opt_diagnostic_mode); - }, - - /** - * Adds given apps to the pod row. - * @param {array} apps Array of apps. - */ - setApps: function(apps) { - $('pod-row').setApps(apps); - }, - - /** - * Sets the flag of whether app pods should be visible. - * @param {boolean} shouldShowApps Whether to show app pods. - */ - setShouldShowApps: function(shouldShowApps) { - $('pod-row').setShouldShowApps(shouldShowApps); - }, - - /** - * Shows the given kiosk app error message. - * @param {!string} message Error message to show. - */ - showAppError: function(message) { - // TODO(nkostylev): Figure out a way to show kiosk app launch error - // pointing to the kiosk app pod. - /** @const */ var BUBBLE_PADDING = 12; - $('bubble').showTextForElement($('pod-row'), - message, - cr.ui.Bubble.Attachment.BOTTOM, - $('pod-row').offsetWidth / 2, - BUBBLE_PADDING); - }, - - /** - * Updates current image of a user. - * @param {string} username User for which to update the image. - */ - updateUserImage: function(username) { - $('pod-row').updateUserImage(username); - }, - - /** - * Updates Caps Lock state (for Caps Lock hint in password input field). - * @param {boolean} enabled Whether Caps Lock is on. - */ - setCapsLockState: function(enabled) { - $('pod-row').classList.toggle('capslock-on', enabled); - }, - - /** - * Enforces focus on user pod of locked user. - */ - forceLockedUserPodFocus: function() { - var row = $('pod-row'); - if (row.lockedPod) - row.focusPod(row.lockedPod, true); - }, - - /** - * Remove given user from pod row if it is there. - * @param {string} user name. - */ - removeUser: function(username) { - $('pod-row').removeUserPod(username); - }, - - /** - * Displays a banner containing |message|. If the banner is already present - * this function updates the message in the banner. This function is used - * by the chrome.screenlockPrivate.showMessage API. - * @param {string} message Text to be displayed or empty to hide the banner. - */ - showBannerMessage: function(message) { - var banner = $('signin-banner'); - banner.textContent = message; - banner.classList.toggle('message-set', !!message); - }, - - /** - * Shows a custom icon in the user pod of |username|. This function - * is used by the chrome.screenlockPrivate API. - * @param {string} username Username of pod to add button - * @param {!{id: !string, - * hardlockOnClick: boolean, - * isTrialRun: boolean, - * tooltip: ({text: string, autoshow: boolean} | undefined)}} icon - * The icon parameters. - */ - showUserPodCustomIcon: function(username, icon) { - $('pod-row').showUserPodCustomIcon(username, icon); - }, - - /** - * Hides the custom icon in the user pod of |username| added by - * showUserPodCustomIcon(). This function is used by the - * chrome.screenlockPrivate API. - * @param {string} username Username of pod to remove button - */ - hideUserPodCustomIcon: function(username) { - $('pod-row').hideUserPodCustomIcon(username); - }, - - /** - * Set a fingerprint icon in the user pod of |username|. - * @param {string} username Username of the selected user - * @param {number} state Fingerprint unlock state - */ - setUserPodFingerprintIcon: function(username, state) { - $('pod-row').setUserPodFingerprintIcon(username, state); - }, - - /** - * Removes the fingerprint icon in the user pod of |username|. - * @param {string} username Username of the selected user. - */ - removeUserPodFingerprintIcon: function(username) { - $('pod-row').removeUserPodFingerprintIcon(username); - }, - - /** - * Sets the authentication type used to authenticate the user. - * @param {string} username Username of selected user - * @param {number} authType Authentication type, must be a valid value in - * the AUTH_TYPE enum in user_pod_row.js. - * @param {string} value The initial value to use for authentication. - */ - setAuthType: function(username, authType, value) { - $('pod-row').setAuthType(username, authType, value); - }, - - /** - * Sets the state of tablet mode. - * @param {boolean} isTabletModeEnabled true if the mode is on. - */ - setTabletModeState: function(isTabletModeEnabled) { - $('pod-row').setTabletModeState(isTabletModeEnabled); - }, - - /** - * Enables or disables the pin keyboard for the given user. This may change - * pin keyboard visibility. - * @param {!string} user - * @param {boolean} enabled - */ - setPinEnabledForUser: function(user, enabled) { - $('pod-row').setPinEnabled(user, enabled); - }, - - /** - * Updates the display name shown on a public session pod. - * @param {string} userID The user ID of the public session - * @param {string} displayName The new display name - */ - setPublicSessionDisplayName: function(userID, displayName) { - $('pod-row').setPublicSessionDisplayName(userID, displayName); - }, - - /** - * Updates the list of locales available for a public session. - * @param {string} userID The user ID of the public session - * @param {!Object} locales The list of available locales - * @param {string} defaultLocale The locale to select by default - * @param {boolean} multipleRecommendedLocales Whether |locales| contains - * two or more recommended locales - */ - setPublicSessionLocales: function(userID, - locales, - defaultLocale, - multipleRecommendedLocales) { - $('pod-row').setPublicSessionLocales(userID, - locales, - defaultLocale, - multipleRecommendedLocales); - }, - - /** - * Updates the list of available keyboard layouts for a public session pod. - * @param {string} userID The user ID of the public session - * @param {string} locale The locale to which this list of keyboard layouts - * applies - * @param {!Object} list List of available keyboard layouts - */ - setPublicSessionKeyboardLayouts: function(userID, locale, list) { - $('pod-row').setPublicSessionKeyboardLayouts(userID, locale, list); - }, - - /** - * Updates UI based on the provided lock screen apps state. - * - * @param {LOCK_SCREEN_APPS_STATE} state The current lock screen apps state. - */ - setLockScreenAppsState: function(state) { - if (Oobe.getInstance().displayType != DISPLAY_TYPE.LOCK || - state == this.lockScreenAppsState_) { - return; - } - - this.lockScreenAppsState_ = state; - $('login-header-bar').lockScreenAppsState = state; - // When an lock screen app window is in background - i.e. visible behind - // the lock screen UI - dim the lock screen background, so it's more - // noticeable that the app widow in background is not actionable. - $('background').classList.toggle( - 'dimmed-background', state == LOCK_SCREEN_APPS_STATE.BACKGROUND); - - if (state === LOCK_SCREEN_APPS_STATE.FOREGROUND) - $('pod-row').clearFocusedPod(); - - }, - - /** - * Handles clicks on the document which displays the account picker UI. - * If the click event target is outer container - i.e. background portion of - * UI with no other UI elements, and lock screen apps are in background, a - * request is issued to chrome to move lock screen apps to foreground. - * @param {Event} event The click event. - */ - handleOwnerDocClick_: function(event) { - if (this.lockScreenAppsState_ != LOCK_SCREEN_APPS_STATE.BACKGROUND || - event.target != $('outer-container')) { - return; - } - chrome.send('setLockScreenAppsState', - [LOCK_SCREEN_APPS_STATE.FOREGROUND]); - - event.preventDefault(); - event.stopPropagation(); - }, - }; + return { + EXTERNAL_API: [ + 'loadUsers', + 'runAppForTesting', + 'setApps', + 'setShouldShowApps', + 'showAppError', + 'updateUserImage', + 'setCapsLockState', + 'forceLockedUserPodFocus', + 'removeUser', + 'showBannerMessage', + 'showUserPodCustomIcon', + 'hideUserPodCustomIcon', + 'setUserPodFingerprintIcon', + 'removeUserPodFingerprintIcon', + 'setPinEnabledForUser', + 'setAuthType', + 'setTabletModeState', + 'setPublicSessionDisplayName', + 'setPublicSessionLocales', + 'setPublicSessionKeyboardLayouts', + 'setLockScreenAppsState', + ], + + preferredWidth_: 0, + preferredHeight_: 0, + + // Whether this screen is shown for the first time. + firstShown_: true, + + // Whether this screen is currently being shown. + showing_: false, + + // Last reported lock screen app activity state. + lockScreenAppsState_: LOCK_SCREEN_APPS_STATE.NONE, + + /** @override */ + decorate: function() { + login.PodRow.decorate($('pod-row')); + this.ownerDocument.addEventListener( + 'click', this.handleOwnerDocClick_.bind(this)); + }, + + /** @override */ + getPreferredSize: function() { + return {width: this.preferredWidth_, height: this.preferredHeight_}; + }, + + /** @override */ + onWindowResize: function() { + $('pod-row').onWindowResize(); + + // Reposition the error bubble, if it is showing. Since we are just + // moving the bubble, the number of login attempts tried doesn't matter. + var errorBubble = $('bubble'); + if (errorBubble && !errorBubble.hidden) + this.showErrorBubble(0, undefined /* Reuses the existing message. */); + }, + + /** + * Sets preferred size for account picker screen. + */ + setPreferredSize: function(width, height) { + this.preferredWidth_ = width; + this.preferredHeight_ = height; + }, + + /** + * When the account picker is being used to lock the screen, pressing the + * exit accelerator key will sign out the active user as it would when + * they are signed in. + */ + exit: function() { + // Check and disable the sign out button so that we can never have two + // sign out requests generated in a row. + if ($('pod-row').lockedPod && !$('sign-out-user-button').disabled) { + $('sign-out-user-button').disabled = true; + chrome.send('signOutUser'); + } + }, + + /* Cancel user adding if ESC was pressed. + */ + cancel: function() { + if (Oobe.getInstance().displayType == DISPLAY_TYPE.USER_ADDING) + chrome.send('cancelUserAdding'); + }, + + /** + * Event handler that is invoked just after the frame is shown. + * @param {string} data Screen init payload. + */ + onAfterShow: function(data) { + $('pod-row').handleAfterShow(); + }, + + /** + * Event handler that is invoked just before the frame is shown. + * @param {string} data Screen init payload. + */ + onBeforeShow: function(data) { + this.showing_ = true; + chrome.send('loginUIStateChanged', ['account-picker', true]); + $('login-header-bar').signinUIState = SIGNIN_UI_STATE.ACCOUNT_PICKER; + // Header bar should be always visible on Account Picker screen. + Oobe.getInstance().headerHidden = false; + chrome.send('hideCaptivePortal'); + var podRow = $('pod-row'); + podRow.handleBeforeShow(); + + // In case of the preselected pod onShow will be called once pod + // receives focus. + if (!podRow.preselectedPod) + this.onShow(); + }, + + /** + * Event handler invoked when the page is shown and ready. + */ + onShow: function() { + if (!this.showing_) { + // This method may be called asynchronously when the pod row finishes + // initializing. However, at that point, the screen may have been + // hidden again already. If that happens, ignore the onShow() call. + return; + } + chrome.send('getTabletModeState'); + if (!this.firstShown_) + return; + this.firstShown_ = false; + + // Ensure that login is actually visible. + window.requestAnimationFrame(function() { + chrome.send('accountPickerReady'); + chrome.send('loginVisible', ['account-picker']); + }); + }, + + /** + * Event handler that is invoked just before the frame is hidden. + */ + onBeforeHide: function() { + $('pod-row').clearFocusedPod(); + this.showing_ = false; + chrome.send('loginUIStateChanged', ['account-picker', false]); + $('login-header-bar').signinUIState = SIGNIN_UI_STATE.HIDDEN; + $('pod-row').handleHide(); + }, + + /** + * Shows sign-in error bubble. + * @param {number} loginAttempts Number of login attemps tried. + * @param {HTMLElement} content Content to show in bubble. + */ + showErrorBubble: function(loginAttempts, error) { + var activatedPod = $('pod-row').activatedPod; + if (!activatedPod) { + $('bubble').showContentForElement( + $('pod-row'), cr.ui.Bubble.Attachment.RIGHT, error); + return; + } + // Show web authentication if this is not a supervised user. + if (loginAttempts > MAX_LOGIN_ATTEMPTS_IN_POD && + !activatedPod.user.supervisedUser) { + chrome.send( + 'maxIncorrectPasswordAttempts', [activatedPod.user.emailAddress]); + activatedPod.showSigninUI(); + } else { + if (loginAttempts == 1) { + chrome.send( + 'firstIncorrectPasswordAttempt', + [activatedPod.user.emailAddress]); + } + // Update the pod row display if incorrect password. + $('pod-row').setFocusedPodErrorDisplay(true); + + /** @const */ var BUBBLE_OFFSET = 25; + // -8 = 4(BUBBLE_POD_OFFSET) - 2(bubble margin) + // - 10(internal bubble adjustment) + var bubblePositioningPadding = -8; + + var bubbleAnchor; + var attachment; + if (activatedPod.pinContainer && + activatedPod.pinContainer.style.visibility == 'visible') { + // Anchor the bubble to the input field. + bubbleAnchor = + (activatedPod.getElementsByClassName('auth-container'))[0]; + if (!bubbleAnchor) { + console.error('auth-container not found!'); + bubbleAnchor = activatedPod.mainInput; + } + attachment = cr.ui.Bubble.Attachment.RIGHT; + } else { + // Anchor the bubble to the pod instead of the input. + bubbleAnchor = activatedPod; + attachment = cr.ui.Bubble.Attachment.BOTTOM; + } + + var bubble = $('bubble'); + + // Cannot use cr.ui.LoginUITools.get* on bubble until it is attached to + // the element. getMaxHeight/Width rely on the correct up/left element + // side positioning that doesn't happen until bubble is attached. + var maxHeight = cr.ui.LoginUITools.getMaxHeightBeforeShelfOverlapping( + bubbleAnchor) - + bubbleAnchor.offsetHeight - BUBBLE_POD_OFFSET; + var maxWidth = cr.ui.LoginUITools.getMaxWidthToFit(bubbleAnchor) - + bubbleAnchor.offsetWidth - BUBBLE_POD_OFFSET; + + // Change bubble visibility temporary to calculate height. + var bubbleVisibility = bubble.style.visibility; + bubble.style.visibility = 'hidden'; + bubble.hidden = false; + // Now we need the bubble to have the new content before calculating + // size. Undefined |error| == reuse old content. + if (error !== undefined) + bubble.replaceContent(error); + + // Get bubble size. + var bubbleOffsetHeight = parseInt(bubble.offsetHeight); + var bubbleOffsetWidth = parseInt(bubble.offsetWidth); + // Restore attributes. + bubble.style.visibility = bubbleVisibility; + bubble.hidden = true; + + if (attachment == cr.ui.Bubble.Attachment.BOTTOM) { + // Move error bubble if it overlaps the shelf. + if (maxHeight < bubbleOffsetHeight) + attachment = cr.ui.Bubble.Attachment.TOP; + } else { + // Move error bubble if it doesn't fit screen. + if (maxWidth < bubbleOffsetWidth) { + bubblePositioningPadding = 2; + attachment = cr.ui.Bubble.Attachment.LEFT; + } + } + var showBubbleCallback = function() { + activatedPod.removeEventListener( + 'transitionend', showBubbleCallback); + $('bubble').showContentForElement( + bubbleAnchor, attachment, error, BUBBLE_OFFSET, + bubblePositioningPadding, true); + }; + activatedPod.addEventListener('transitionend', showBubbleCallback); + ensureTransitionEndEvent(activatedPod); + } + }, + + /** + * Loads given users in pod row. + * @param {array} users Array of user. + * @param {boolean} showGuest Whether to show guest session button. + */ + loadUsers: function(users, showGuest) { + $('pod-row').loadPods(users); + $('login-header-bar').showGuestButton = showGuest; + // On Desktop, #login-header-bar has a shadow if there are 8+ profiles. + if (Oobe.getInstance().displayType == DISPLAY_TYPE.DESKTOP_USER_MANAGER) + $('login-header-bar').classList.toggle('shadow', users.length > 8); + }, + + /** + * Runs app with a given id from the list of loaded apps. + * @param {!string} app_id of an app to run. + * @param {boolean=} opt_diagnostic_mode Whether to run the app in + * diagnostic mode. Default is false. + */ + runAppForTesting: function(app_id, opt_diagnostic_mode) { + $('pod-row').findAndRunAppForTesting(app_id, opt_diagnostic_mode); + }, + + /** + * Adds given apps to the pod row. + * @param {array} apps Array of apps. + */ + setApps: function(apps) { + $('pod-row').setApps(apps); + }, + + /** + * Sets the flag of whether app pods should be visible. + * @param {boolean} shouldShowApps Whether to show app pods. + */ + setShouldShowApps: function(shouldShowApps) { + $('pod-row').setShouldShowApps(shouldShowApps); + }, + + /** + * Shows the given kiosk app error message. + * @param {!string} message Error message to show. + */ + showAppError: function(message) { + // TODO(nkostylev): Figure out a way to show kiosk app launch error + // pointing to the kiosk app pod. + /** @const */ var BUBBLE_PADDING = 12; + $('bubble').showTextForElement( + $('pod-row'), message, cr.ui.Bubble.Attachment.BOTTOM, + $('pod-row').offsetWidth / 2, BUBBLE_PADDING); + }, + + /** + * Updates current image of a user. + * @param {string} username User for which to update the image. + */ + updateUserImage: function(username) { + $('pod-row').updateUserImage(username); + }, + + /** + * Updates Caps Lock state (for Caps Lock hint in password input field). + * @param {boolean} enabled Whether Caps Lock is on. + */ + setCapsLockState: function(enabled) { + $('pod-row').classList.toggle('capslock-on', enabled); + }, + + /** + * Enforces focus on user pod of locked user. + */ + forceLockedUserPodFocus: function() { + var row = $('pod-row'); + if (row.lockedPod) + row.focusPod(row.lockedPod, true); + }, + + /** + * Remove given user from pod row if it is there. + * @param {string} user name. + */ + removeUser: function(username) { + $('pod-row').removeUserPod(username); + }, + + /** + * Displays a banner containing |message|. If the banner is already present + * this function updates the message in the banner. This function is used + * by the chrome.screenlockPrivate.showMessage API. + * @param {string} message Text to be displayed or empty to hide the + * banner. + * @param {boolean} isWarning True if the given message is a warning. + */ + showBannerMessage: function(message, isWarning) { + var banner = $('signin-banner'); + banner.textContent = message; + banner.classList.toggle('message-set', !!message); + }, + + /** + * Shows a custom icon in the user pod of |username|. This function + * is used by the chrome.screenlockPrivate API. + * @param {string} username Username of pod to add button + * @param {!{id: !string, + * hardlockOnClick: boolean, + * isTrialRun: boolean, + * tooltip: ({text: string, autoshow: boolean} | undefined)}} + * icon The icon parameters. + */ + showUserPodCustomIcon: function(username, icon) { + $('pod-row').showUserPodCustomIcon(username, icon); + }, + + /** + * Hides the custom icon in the user pod of |username| added by + * showUserPodCustomIcon(). This function is used by the + * chrome.screenlockPrivate API. + * @param {string} username Username of pod to remove button + */ + hideUserPodCustomIcon: function(username) { + $('pod-row').hideUserPodCustomIcon(username); + }, + + /** + * Set a fingerprint icon in the user pod of |username|. + * @param {string} username Username of the selected user + * @param {number} state Fingerprint unlock state + */ + setUserPodFingerprintIcon: function(username, state) { + $('pod-row').setUserPodFingerprintIcon(username, state); + }, + + /** + * Removes the fingerprint icon in the user pod of |username|. + * @param {string} username Username of the selected user. + */ + removeUserPodFingerprintIcon: function(username) { + $('pod-row').removeUserPodFingerprintIcon(username); + }, + + /** + * Sets the authentication type used to authenticate the user. + * @param {string} username Username of selected user + * @param {number} authType Authentication type, must be a valid value in + * the AUTH_TYPE enum in user_pod_row.js. + * @param {string} value The initial value to use for authentication. + */ + setAuthType: function(username, authType, value) { + $('pod-row').setAuthType(username, authType, value); + }, + + /** + * Sets the state of tablet mode. + * @param {boolean} isTabletModeEnabled true if the mode is on. + */ + setTabletModeState: function(isTabletModeEnabled) { + $('pod-row').setTabletModeState(isTabletModeEnabled); + }, + + /** + * Enables or disables the pin keyboard for the given user. This may change + * pin keyboard visibility. + * @param {!string} user + * @param {boolean} enabled + */ + setPinEnabledForUser: function(user, enabled) { + $('pod-row').setPinEnabled(user, enabled); + }, + + /** + * Updates the display name shown on a public session pod. + * @param {string} userID The user ID of the public session + * @param {string} displayName The new display name + */ + setPublicSessionDisplayName: function(userID, displayName) { + $('pod-row').setPublicSessionDisplayName(userID, displayName); + }, + + /** + * Updates the list of locales available for a public session. + * @param {string} userID The user ID of the public session + * @param {!Object} locales The list of available locales + * @param {string} defaultLocale The locale to select by default + * @param {boolean} multipleRecommendedLocales Whether |locales| contains + * two or more recommended locales + */ + setPublicSessionLocales: function( + userID, locales, defaultLocale, multipleRecommendedLocales) { + $('pod-row').setPublicSessionLocales( + userID, locales, defaultLocale, multipleRecommendedLocales); + }, + + /** + * Updates the list of available keyboard layouts for a public session pod. + * @param {string} userID The user ID of the public session + * @param {string} locale The locale to which this list of keyboard layouts + * applies + * @param {!Object} list List of available keyboard layouts + */ + setPublicSessionKeyboardLayouts: function(userID, locale, list) { + $('pod-row').setPublicSessionKeyboardLayouts(userID, locale, list); + }, + + /** + * Updates UI based on the provided lock screen apps state. + * + * @param {LOCK_SCREEN_APPS_STATE} state The current lock screen apps + * state. + */ + setLockScreenAppsState: function(state) { + if (Oobe.getInstance().displayType != DISPLAY_TYPE.LOCK || + state == this.lockScreenAppsState_) { + return; + } + + this.lockScreenAppsState_ = state; + $('login-header-bar').lockScreenAppsState = state; + // When an lock screen app window is in background - i.e. visible behind + // the lock screen UI - dim the lock screen background, so it's more + // noticeable that the app widow in background is not actionable. + $('background') + .classList.toggle( + 'dimmed-background', state == LOCK_SCREEN_APPS_STATE.BACKGROUND); + + if (state === LOCK_SCREEN_APPS_STATE.FOREGROUND) + $('pod-row').clearFocusedPod(); + + }, + + /** + * Handles clicks on the document which displays the account picker UI. + * If the click event target is outer container - i.e. background portion + * of UI with no other UI elements, and lock screen apps are in background, + * a request is issued to chrome to move lock screen apps to foreground. + * @param {Event} event The click event. + */ + handleOwnerDocClick_: function(event) { + if (this.lockScreenAppsState_ != LOCK_SCREEN_APPS_STATE.BACKGROUND || + event.target != $('outer-container')) { + return; + } + chrome.send( + 'setLockScreenAppsState', [LOCK_SCREEN_APPS_STATE.FOREGROUND]); + + event.preventDefault(); + event.stopPropagation(); + }, + }; }); diff --git a/chromium/ui/login/account_picker/user_pod_row.css b/chromium/ui/login/account_picker/user_pod_row.css index 29bc6f66bf4..8ec451da2fa 100644 --- a/chromium/ui/login/account_picker/user_pod_row.css +++ b/chromium/ui/login/account_picker/user_pod_row.css @@ -575,7 +575,7 @@ html[dir=rtl] .user-type-icon-area { } .fingerprint-icon-container.failed .fingerprint-icon-image { - background-image: url(../../webui/resources/images/fingerprint_failed.svg); + background-image: url(../../webui/resources/images/icon_error_outline.svg); } .pod input[type='password'].hidden::-webkit-input-placeholder { diff --git a/chromium/ui/login/bubble.js b/chromium/ui/login/bubble.js index ff8d91a3220..13bebb738a3 100644 --- a/chromium/ui/login/bubble.js +++ b/chromium/ui/login/bubble.js @@ -55,6 +55,8 @@ cr.define('cr.ui', function() { // Whether to hide bubble when key is pressed. hideOnKeyPress_: true, + persistent_: false, + /** @override */ decorate: function() { this.docKeyDownHandler_ = this.handleDocKeyDown_.bind(this); @@ -93,6 +95,24 @@ cr.define('cr.ui', function() { }, /** + * Whether the bubble should remain shown on user action events (e.g. on + * user clicking, or scrolling outside the bubble). Note that + * {@code this.hideOnKeyPress} has precedence. + * @type {boolean} + */ + set persistent(value) { + this.persistent_ = value + }, + + /** + * If set, the element at which the bubble is anchored. + * @type {HTMLElement|undefined} + */ + get anchor() { + return this.anchor_; + }, + + /** * Element that should be focused on tab of last bubble element * to create artificial closed tab-cycle through bubble. * Same element as first focused on bubble opening. @@ -218,9 +238,9 @@ cr.define('cr.ui', function() { * @param {boolean=} opt_oldstyle Optional flag to force old style bubble, * i.e. pre-MD-style. */ - showContentForElement: function(el, attachment, opt_content, - opt_offset, opt_padding, opt_match_width, - opt_oldstyle) { + showContentForElement: function( + el, attachment, opt_content, opt_offset, opt_padding, opt_match_width, + opt_oldstyle) { /** @const */ var ARROW_OFFSET = 25; /** @const */ var DEFAULT_PADDING = 18; @@ -308,8 +328,8 @@ cr.define('cr.ui', function() { * half of its weight/height. * @param {number=} opt_padding Optional padding of the bubble. */ - showTextForElement: function(el, text, attachment, - opt_offset, opt_padding) { + showTextForElement: function( + el, text, attachment, opt_offset, opt_padding) { var span = this.ownerDocument.createElement('span'); span.textContent = text; this.showContentForElement(el, attachment, span, opt_offset, opt_padding); @@ -349,7 +369,7 @@ cr.define('cr.ui', function() { * @private */ handleScroll_: function(e) { - if (!this.hidden) + if (!this.hidden && !this.persistent_) this.hide(); }, @@ -362,7 +382,7 @@ cr.define('cr.ui', function() { if (e.target == this.anchor_) return; - if (!this.hidden) + if (!this.hidden && !this.persistent_) this.hide(); }, @@ -391,10 +411,9 @@ cr.define('cr.ui', function() { e.preventDefault(); } // Close bubble on ESC or on hitting spacebar or Enter at close-button. - if (e.key == Keys.ESC || - ((e.key == Keys.ENTER || - e.key == Keys.SPACE) && - e.target && e.target.classList.contains('close-button'))) + if ((e.key == Keys.ESC && !this.persistent_) || + ((e.key == Keys.ENTER || e.key == Keys.SPACE) && e.target && + e.target.classList.contains('close-button'))) this.hide(); }, @@ -403,7 +422,7 @@ cr.define('cr.ui', function() { * @private */ handleWindowBlur_: function(e) { - if (!this.hidden) + if (!this.hidden && !this.persistent_) this.hide(); } }; diff --git a/chromium/ui/login/display_manager.js b/chromium/ui/login/display_manager.js index 3102fbfef19..69b6d98443d 100644 --- a/chromium/ui/login/display_manager.js +++ b/chromium/ui/login/display_manager.js @@ -14,6 +14,7 @@ /** @const */ var SCREEN_OOBE_UPDATE = 'update'; /** @const */ var SCREEN_OOBE_RESET = 'reset'; /** @const */ var SCREEN_OOBE_ENROLLMENT = 'oauth-enrollment'; +/** @const */ var SCREEN_OOBE_DEMO_SETUP = 'demo-setup'; /** @const */ var SCREEN_OOBE_KIOSK_ENABLE = 'kiosk-enable'; /** @const */ var SCREEN_OOBE_AUTO_ENROLLMENT_CHECK = 'auto-enrollment-check'; /** @const */ var SCREEN_GAIA_SIGNIN = 'gaia-signin'; @@ -57,6 +58,7 @@ 'app_launch_network_config'; /** @const */ var ACCELERATOR_BOOTSTRAPPING_SLAVE = "bootstrapping_slave"; /** @const */ var ACCELERATOR_DEMO_MODE = "demo_mode"; +/** @const */ var ACCELERATOR_SEND_FEEDBACK = "send_feedback"; /* Signin UI state constants. Used to control header bar UI. */ /** @const */ var SIGNIN_UI_STATE = { @@ -383,12 +385,12 @@ cr.define('cr.ui.login', function() { * @param {string} name Accelerator name. */ handleAccelerator: function(name) { - if (this.currentScreen.ignoreAccelerators) { + if (this.currentScreen && this.currentScreen.ignoreAccelerators) { return; } var currentStepId = this.screens_[this.currentStep_]; if (name == ACCELERATOR_CANCEL) { - if (this.currentScreen.cancel) { + if (this.currentScreen && this.currentScreen.cancel) { this.currentScreen.cancel(); } } else if (name == ACCELERATOR_ENABLE_DEBBUGING) { @@ -446,6 +448,8 @@ cr.define('cr.ui.login', function() { -1) { chrome.send('setupDemoMode'); } + } else if (name == ACCELERATOR_SEND_FEEDBACK) { + chrome.send('sendFeedback'); } }, @@ -1015,14 +1019,14 @@ cr.define('cr.ui.login', function() { }; /** - * Shows sign-in error bubble. - * @param {number} loginAttempts Number of login attemps tried. - * @param {string} message Error message to show. + * Creates a div element used to display error message in an error bubble. + * + * @param {string} message The error message. * @param {string} link Text to use for help link. * @param {number} helpId Help topic Id associated with help link. + * @return {!HTMLElement} The error bubble content. */ - DisplayManager.showSignInError = function(loginAttempts, message, link, - helpId) { + DisplayManager.createErrorElement_ = function(message, link, helpId) { var error = document.createElement('div'); var messageDiv = document.createElement('div'); @@ -1044,6 +1048,19 @@ cr.define('cr.ui.login', function() { } error.setAttribute('aria-live', 'assertive'); + return error; + }; + + /** + * Shows sign-in error bubble. + * @param {number} loginAttempts Number of login attemps tried. + * @param {string} message Error message to show. + * @param {string} link Text to use for help link. + * @param {number} helpId Help topic Id associated with help link. + */ + DisplayManager.showSignInError = function( + loginAttempts, message, link, helpId) { + var error = DisplayManager.createErrorElement_(message, link, helpId); var currentScreen = Oobe.getInstance().currentScreen; if (currentScreen && typeof currentScreen.showErrorBubble === 'function') { @@ -1053,6 +1070,43 @@ cr.define('cr.ui.login', function() { }; /** + * Shows a warning to the user that the detachable base (keyboard) different + * than the one previously used by the user got attached to the device. It + * warn the user that the attached base might be untrusted. + * + * @param {string} username The username of the user with which the error + * bubble is associated. For example, in the account picker screen, it + * identifies the user pod under which the error bubble should be shown. + * @param {string} message Error message to show. + * @param {string} link Text to use for help link. + * @param {number} helpId Help topic Id associated with help link. + */ + DisplayManager.showDetachableBaseChangedWarning = function( + username, message, link, helpId) { + var error = DisplayManager.createErrorElement_(message, link, helpId); + + var currentScreen = Oobe.getInstance().currentScreen; + if (currentScreen && + typeof currentScreen.showDetachableBaseWarningBubble === 'function') { + currentScreen.showDetachableBaseWarningBubble(username, error); + } + }; + + /** + * Hides the warning bubble shown by {@code showDetachableBaseChangedWarning}. + * + * @param {string} username The username of the user with wich the warning was + * associated. + */ + DisplayManager.hideDetachableBaseChangedWarning = function(username) { + var currentScreen = Oobe.getInstance().currentScreen; + if (currentScreen && + typeof currentScreen.hideDetachableBaseWarningBubble === 'function') { + currentScreen.hideDetachableBaseWarningBubble(username); + } + }; + + /** * Shows password changed screen that offers migration. * @param {boolean} showError Whether to show the incorrect password error. * @param {string} email What user does reauth. Being used for display in the diff --git a/chromium/ui/login/md_screen_container.css b/chromium/ui/login/md_screen_container.css index bdac3bbc5ad..c4ac19e2c26 100644 --- a/chromium/ui/login/md_screen_container.css +++ b/chromium/ui/login/md_screen_container.css @@ -22,19 +22,6 @@ perspective: 600px; } -.pin-container.pin-enabled { - opacity: 1; - transition: opacity 200ms ease-in-out 180ms; - visibility: visible; -} - -.pin-container.pin-disabled { - opacity: 0; /* Opacity is set to 1 after the pin element is loaded. */ - transition: none; - visibility: hidden; /* Needed because pin-keyboard's offsetHeight is - checked to determine if loaded. */ -} - #scroll-container { bottom: 0; /* Allows content overlap with control bar. */ left: 0; diff --git a/chromium/ui/message_center/BUILD.gn b/chromium/ui/message_center/BUILD.gn index ed816055fb4..e7b7e35f858 100644 --- a/chromium/ui/message_center/BUILD.gn +++ b/chromium/ui/message_center/BUILD.gn @@ -13,12 +13,10 @@ aggregate_vector_icons("message_center_vector_icons") { icon_directory = "vector_icons" icons = [ - "notification_close_button.1x.icon", "notification_close_button.icon", "notification_expand_less.icon", "notification_expand_more.icon", "notification_inline_reply.icon", - "notification_settings_button.1x.icon", "notification_settings_button.icon", "product.icon", ] @@ -49,6 +47,7 @@ jumbo_component("message_center") { "//ui/accessibility", "//ui/display", "//ui/events", + "//ui/events:gesture_detection", "//ui/gfx", "//ui/gfx/geometry", "//ui/native_theme", @@ -209,7 +208,7 @@ if (enable_message_center) { ":test_support", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gmock", "//testing/gtest", @@ -236,7 +235,7 @@ if (enable_message_center) { if (is_chromeos) { sources += [ "public/mojo/struct_traits_unittest.cc" ] deps += [ - "//mojo/edk/system", + "//mojo/edk", "//ui/message_center/public/mojo:test_interfaces", ] } diff --git a/chromium/ui/message_center/message_center_impl.cc b/chromium/ui/message_center/message_center_impl.cc index 53485f966a1..b5d9e1385b6 100644 --- a/chromium/ui/message_center/message_center_impl.cc +++ b/chromium/ui/message_center/message_center_impl.cc @@ -12,7 +12,6 @@ #include "base/auto_reset.h" #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/observer_list.h" #include "base/stl_util.h" #include "base/strings/string_util.h" @@ -76,15 +75,6 @@ void MessageCenterImpl::OnBlockingStateChanged(NotificationBlocker* blocker) { NotificationList::PopupNotifications popups = notification_list_->GetPopupNotifications(blockers_, &blocked); - // Close already displayed pop-ups that are blocked now. - for (const std::string& notification_id : blocked) { - // Do not call MessageCenterImpl::MarkSinglePopupAsShown() directly here - // just for performance reason. MessageCenterImpl::MarkSinglePopupAsShown() - // calls NotificationList::MarkSinglePopupAsShown(), but the whole cache - // will be recreated below. - if (FindVisibleNotificationById(notification_id)->IsRead()) - notification_list_->MarkSinglePopupAsShown(notification_id, true); - } visible_notifications_ = notification_list_->GetVisibleNotifications(blockers_); @@ -319,9 +309,9 @@ void MessageCenterImpl::ClickOnNotification(const std::string& id) { scoped_refptr<NotificationDelegate> delegate = notification_list_->GetNotificationDelegate(id); for (auto& observer : observer_list_) - observer.OnNotificationClicked(id); - if (delegate.get()) - delegate->Click(); + observer.OnNotificationClicked(id, base::nullopt, base::nullopt); + if (delegate) + delegate->Click(base::nullopt, base::nullopt); } void MessageCenterImpl::ClickOnNotificationButton(const std::string& id, @@ -336,9 +326,9 @@ void MessageCenterImpl::ClickOnNotificationButton(const std::string& id, scoped_refptr<NotificationDelegate> delegate = notification_list_->GetNotificationDelegate(id); for (auto& observer : observer_list_) - observer.OnNotificationButtonClicked(id, button_index); - if (delegate.get()) - delegate->ButtonClick(button_index); + observer.OnNotificationClicked(id, button_index, base::nullopt); + if (delegate) + delegate->Click(button_index, base::nullopt); } void MessageCenterImpl::ClickOnNotificationButtonWithReply( @@ -355,9 +345,9 @@ void MessageCenterImpl::ClickOnNotificationButtonWithReply( scoped_refptr<NotificationDelegate> delegate = notification_list_->GetNotificationDelegate(id); for (auto& observer : observer_list_) - observer.OnNotificationButtonClickedWithReply(id, button_index, reply); - if (delegate.get()) - delegate->ButtonClickWithReply(button_index, reply); + observer.OnNotificationClicked(id, button_index, reply); + if (delegate) + delegate->Click(button_index, reply); } void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) { diff --git a/chromium/ui/message_center/message_center_observer.h b/chromium/ui/message_center/message_center_observer.h index 9ef0ec65c84..c88c1166571 100644 --- a/chromium/ui/message_center/message_center_observer.h +++ b/chromium/ui/message_center/message_center_observer.h @@ -7,6 +7,7 @@ #include <string> +#include "base/optional.h" #include "ui/message_center/message_center_export.h" #include "ui/message_center/message_center_types.h" @@ -34,20 +35,13 @@ class MESSAGE_CENTER_EXPORT MessageCenterObserver { virtual void OnNotificationUpdated(const std::string& notification_id) {} // Called when a click event happens on the notification associated with - // |notification_id|. - virtual void OnNotificationClicked(const std::string& notification_id) {} - - // Called when a click event happens on a button indexed by |button_index| - // of the notification associated with |notification_id|. - virtual void OnNotificationButtonClicked(const std::string& notification_id, - int button_index) {} - - // Called when a click event happens on a button with an input indexed by - // |button_index| of the notification associated with |notification_id|. - virtual void OnNotificationButtonClickedWithReply( + // |notification_id|. |button_index| will be nullopt if the click occurred on + // the body of the notification. |reply| will be filled in only if there was + // an input field associated with the button. + virtual void OnNotificationClicked( const std::string& notification_id, - int button_index, - const base::string16& reply) {} + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) {} // Called when notification settings button is clicked. The |handled| argument // indicates whether the notification delegate already handled the operation. diff --git a/chromium/ui/message_center/message_center_stats_collector.cc b/chromium/ui/message_center/message_center_stats_collector.cc index 210e0bf85f5..c6503a3a9ed 100644 --- a/chromium/ui/message_center/message_center_stats_collector.cc +++ b/chromium/ui/message_center/message_center_stats_collector.cc @@ -92,24 +92,17 @@ void MessageCenterStatsCollector::OnNotificationUpdated( } void MessageCenterStatsCollector::OnNotificationClicked( - const std::string& notification_id) { - StatsCollection::iterator iter = stats_.find(notification_id); - if (iter == stats_.end()) - return; - NotificationStats& notification_stat = iter->second; - - notification_stat.CollectAction(NOTIFICATION_ACTION_CLICK); -} - -void MessageCenterStatsCollector::OnNotificationButtonClicked( const std::string& notification_id, - int button_index) { + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) { StatsCollection::iterator iter = stats_.find(notification_id); if (iter == stats_.end()) return; NotificationStats& notification_stat = iter->second; - notification_stat.CollectAction(NOTIFICATION_ACTION_BUTTON_CLICK); + notification_stat.CollectAction(button_index + ? NOTIFICATION_ACTION_BUTTON_CLICK + : NOTIFICATION_ACTION_CLICK); } void MessageCenterStatsCollector::OnNotificationSettingsClicked(bool handled) { diff --git a/chromium/ui/message_center/message_center_stats_collector.h b/chromium/ui/message_center/message_center_stats_collector.h index 6906f65d42f..c2e212c3111 100644 --- a/chromium/ui/message_center/message_center_stats_collector.h +++ b/chromium/ui/message_center/message_center_stats_collector.h @@ -66,9 +66,10 @@ class MessageCenterStatsCollector : public MessageCenterObserver { void OnNotificationRemoved(const std::string& notification_id, bool by_user) override; void OnNotificationUpdated(const std::string& notification_id) override; - void OnNotificationClicked(const std::string& notification_id) override; - void OnNotificationButtonClicked(const std::string& notification_id, - int button_index) override; + void OnNotificationClicked( + const std::string& notification_id, + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) override; void OnNotificationSettingsClicked(bool handled) override; void OnNotificationDisplayed(const std::string& notification_id, const DisplaySource source) override; diff --git a/chromium/ui/message_center/notification_list.cc b/chromium/ui/message_center/notification_list.cc index 03882443b45..a461bbff53f 100644 --- a/chromium/ui/message_center/notification_list.cc +++ b/chromium/ui/message_center/notification_list.cc @@ -34,7 +34,7 @@ bool ShouldShowNotificationAsPopup( } // namespace bool ComparePriorityTimestampSerial::operator()(Notification* n1, - Notification* n2) { + Notification* n2) const { if (n1->priority() > n2->priority()) // Higher pri go first. return true; if (n1->priority() < n2->priority()) @@ -42,7 +42,8 @@ bool ComparePriorityTimestampSerial::operator()(Notification* n1, return CompareTimestampSerial()(n1, n2); } -bool CompareTimestampSerial::operator()(Notification* n1, Notification* n2) { +bool CompareTimestampSerial::operator()(Notification* n1, + Notification* n2) const { if (n1->timestamp() > n2->timestamp()) // Newer come first. return true; if (n1->timestamp() < n2->timestamp()) @@ -54,6 +55,11 @@ bool CompareTimestampSerial::operator()(Notification* n1, Notification* n2) { return false; } +bool NotificationList::NotificationState::operator!=( + const NotificationState& other) const { + return shown_as_popup != other.shown_as_popup || is_read != other.is_read; +} + NotificationList::NotificationList(MessageCenter* message_center) : message_center_(message_center), quiet_mode_(false) { @@ -67,14 +73,13 @@ void NotificationList::SetNotificationsShown( std::set<std::string>* updated_ids) { Notifications notifications = GetVisibleNotifications(blockers); - for (auto iter = notifications.begin(); iter != notifications.end(); ++iter) { - Notification* notification = *iter; - bool was_popup = notification->shown_as_popup(); - bool was_read = notification->IsRead(); + for (Notification* notification : notifications) { + NotificationState* state = &GetNotification(notification->id())->second; + const NotificationState original_state = *state; if (notification->priority() < SYSTEM_PRIORITY) - notification->set_shown_as_popup(true); - notification->set_is_read(true); - if (updated_ids && !(was_popup && was_read)) + state->shown_as_popup = true; + state->is_read = true; + if (updated_ids && (original_state != *state)) updated_ids->insert(notification->id()); } } @@ -91,17 +96,17 @@ void NotificationList::UpdateNotificationMessage( if (iter == notifications_.end()) return; - new_notification->CopyState(iter->get()); + Notification* notification = iter->first.get(); + NotificationState state = iter->second; // Handles priority promotion. If the notification is already dismissed but // the updated notification has higher priority, it should re-appear as a // toast. Notifications coming from websites through the Web Notification API // will always re-appear on update. - if (((*iter)->priority() < new_notification->priority() || + if ((notification->priority() < new_notification->priority() || new_notification->notifier_id().type == NotifierId::WEB_PAGE) && !quiet_mode_) { - new_notification->set_is_read(false); - new_notification->set_shown_as_popup(false); + state = NotificationState(); } // Do not use EraseNotification and PushNotification, since we don't want to @@ -110,7 +115,7 @@ void NotificationList::UpdateNotificationMessage( // We really don't want duplicate IDs. DCHECK(GetNotification(new_notification->id()) == notifications_.end()); - notifications_.insert(std::move(new_notification)); + notifications_.emplace(std::move(new_notification), state); } void NotificationList::RemoveNotification(const std::string& id) { @@ -120,9 +125,10 @@ void NotificationList::RemoveNotification(const std::string& id) { NotificationList::Notifications NotificationList::GetNotificationsByNotifierId( const NotifierId& notifier_id) { Notifications notifications; - for (const auto& notification : notifications_) { + for (const auto& tuple : notifications_) { + Notification* notification = tuple.first.get(); if (notification->notifier_id() == notifier_id) - notifications.insert(notification.get()); + notifications.insert(notification); } return notifications; } @@ -132,7 +138,7 @@ bool NotificationList::SetNotificationIcon(const std::string& notification_id, auto iter = GetNotification(notification_id); if (iter == notifications_.end()) return false; - (*iter)->set_icon(image); + iter->first->set_icon(image); return true; } @@ -141,7 +147,7 @@ bool NotificationList::SetNotificationImage(const std::string& notification_id, auto iter = GetNotification(notification_id); if (iter == notifications_.end()) return false; - (*iter)->set_image(image); + iter->first->set_image(image); return true; } @@ -151,7 +157,7 @@ bool NotificationList::SetNotificationButtonIcon( auto iter = GetNotification(notification_id); if (iter == notifications_.end()) return false; - (*iter)->SetButtonIcon(button_index, image); + iter->first->SetButtonIcon(button_index, image); return true; } @@ -161,16 +167,16 @@ bool NotificationList::HasNotificationOfType(const std::string& id, if (iter == notifications_.end()) return false; - return (*iter)->type() == type; + return iter->first->type() == type; } bool NotificationList::HasPopupNotifications( const NotificationBlockers& blockers) { - for (const auto& notification : notifications_) { - if (notification->priority() < DEFAULT_PRIORITY) + for (const auto& tuple : notifications_) { + if (tuple.first->priority() < DEFAULT_PRIORITY) break; - if (!notification->shown_as_popup() && - ShouldShowNotificationAsPopup(*notification.get(), blockers)) { + if (!tuple.second.shown_as_popup && + ShouldShowNotificationAsPopup(*tuple.first, blockers)) { return true; } } @@ -186,8 +192,9 @@ NotificationList::GetPopupNotifications(const NotificationBlockers& blockers, // Collect notifications that should be shown as popups. Start from oldest. for (auto iter = notifications_.rbegin(); iter != notifications_.rend(); iter++) { - Notification* notification = iter->get(); - if (notification->shown_as_popup()) + NotificationState* state = &iter->second; + Notification* notification = iter->first.get(); + if (state->shown_as_popup) continue; // No popups for LOW/MIN priority. @@ -195,6 +202,8 @@ NotificationList::GetPopupNotifications(const NotificationBlockers& blockers, continue; if (!ShouldShowNotificationAsPopup(*notification, blockers)) { + if (state->is_read) + state->shown_as_popup = true; if (blocked) blocked->push_back(notification->id()); continue; @@ -218,17 +227,20 @@ void NotificationList::MarkSinglePopupAsShown( auto iter = GetNotification(id); DCHECK(iter != notifications_.end()); - if ((*iter)->shown_as_popup()) + NotificationState* state = &iter->second; + const Notification& notification = *iter->first; + + if (iter->second.shown_as_popup) return; // System notification is marked as shown only when marked as read. - if ((*iter)->priority() != SYSTEM_PRIORITY || mark_notification_as_read) - (*iter)->set_shown_as_popup(true); + if (notification.priority() != SYSTEM_PRIORITY || mark_notification_as_read) + state->shown_as_popup = true; // The popup notification is already marked as read when it's displayed. - // Set the is_read() back to false if necessary. + // Set the is_read back to false if necessary. if (!mark_notification_as_read) - (*iter)->set_is_read(false); + state->is_read = false; } void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) { @@ -236,11 +248,12 @@ void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) { if (iter == notifications_.end()) return; - if ((*iter)->shown_as_popup()) + NotificationState* state = &iter->second; + + if (state->shown_as_popup) return; - if (!(*iter)->IsRead()) - (*iter)->set_is_read(true); + state->is_read = true; } NotificationDelegate* NotificationList::GetNotificationDelegate( @@ -248,37 +261,37 @@ NotificationDelegate* NotificationList::GetNotificationDelegate( auto iter = GetNotification(id); if (iter == notifications_.end()) return nullptr; - return (*iter)->delegate(); + return iter->first->delegate(); } void NotificationList::SetQuietMode(bool quiet_mode) { quiet_mode_ = quiet_mode; if (quiet_mode_) { - for (auto& notification : notifications_) - notification->set_shown_as_popup(true); + for (auto& tuple : notifications_) + tuple.second.shown_as_popup = true; } } Notification* NotificationList::GetNotificationById(const std::string& id) { auto iter = GetNotification(id); if (iter != notifications_.end()) - return iter->get(); + return iter->first.get(); return nullptr; } NotificationList::Notifications NotificationList::GetVisibleNotifications( const NotificationBlockers& blockers) const { Notifications result; - for (const auto& notification : notifications_) { + for (const auto& tuple : notifications_) { bool should_show = true; for (size_t i = 0; i < blockers.size(); ++i) { - if (!blockers[i]->ShouldShowNotification(*notification.get())) { + if (!blockers[i]->ShouldShowNotification(*tuple.first)) { should_show = false; break; } } if (should_show) - result.insert(notification.get()); + result.insert(tuple.first.get()); } return result; @@ -293,7 +306,7 @@ NotificationList::OwnedNotifications::iterator NotificationList::GetNotification(const std::string& id) { for (auto iter = notifications_.begin(); iter != notifications_.end(); ++iter) { - if ((*iter)->id() == id) + if (iter->first->id() == id) return iter; } return notifications_.end(); @@ -308,24 +321,19 @@ void NotificationList::PushNotification( // Ensure that notification.id is unique by erasing any existing // notification with the same id (shouldn't normally happen). auto iter = GetNotification(notification->id()); - bool state_inherited = false; + NotificationState state; if (iter != notifications_.end()) { - notification->CopyState(iter->get()); - state_inherited = true; + state = iter->second; EraseNotification(iter); - } - // Add the notification to the the list and mark it unread and unshown. - if (!state_inherited) { + } else { // TODO(mukai): needs to distinguish if a notification is dismissed by // the quiet mode or user operation. - notification->set_is_read(false); - notification->set_shown_as_popup(message_center_->IsMessageCenterVisible() - || quiet_mode_ - || notification->shown_as_popup()); + state.shown_as_popup = + message_center_->IsMessageCenterVisible() || quiet_mode_; } - // Take ownership. The notification can only be removed from the list - // in EraseNotification(), which will delete it. - notifications_.insert(std::move(notification)); + if (notification->priority() == MIN_PRIORITY) + state.is_read = true; + notifications_.emplace(std::move(notification), state); } } // namespace message_center diff --git a/chromium/ui/message_center/notification_list.h b/chromium/ui/message_center/notification_list.h index c5a7935e93f..21a07abfc37 100644 --- a/chromium/ui/message_center/notification_list.h +++ b/chromium/ui/message_center/notification_list.h @@ -8,6 +8,7 @@ #include <stddef.h> #include <list> +#include <map> #include <set> #include <string> @@ -37,18 +38,19 @@ struct NotifierId; // Comparers used to auto-sort the lists of Notifications. struct MESSAGE_CENTER_EXPORT ComparePriorityTimestampSerial { - bool operator()(Notification* n1, Notification* n2); + bool operator()(Notification* n1, Notification* n2) const; }; struct MESSAGE_CENTER_EXPORT CompareTimestampSerial { - bool operator()(Notification* n1, Notification* n2); + bool operator()(Notification* n1, Notification* n2) const; }; // An adapter to allow use of the comparers above with std::unique_ptr. template <typename PlainCompare> struct UniquePtrCompare { template <typename T> - bool operator()(const std::unique_ptr<T>& n1, const std::unique_ptr<T>& n2) { + bool operator()(const std::unique_ptr<T>& n1, + const std::unique_ptr<T>& n2) const { return PlainCompare()(n1.get(), n2.get()); } }; @@ -56,11 +58,19 @@ struct UniquePtrCompare { // A helper class to manage the list of notifications. class MESSAGE_CENTER_EXPORT NotificationList { public: + struct NotificationState { + bool operator!=(const NotificationState& other) const; + + bool shown_as_popup = false; + bool is_read = false; + }; + // Auto-sorted set. Matches the order in which Notifications are shown in // Notification Center. using Notifications = std::set<Notification*, ComparePriorityTimestampSerial>; using OwnedNotifications = - std::set<std::unique_ptr<Notification>, + std::map<std::unique_ptr<Notification>, + NotificationState, UniquePtrCompare<ComparePriorityTimestampSerial>>; // Auto-sorted set used to return the Notifications to be shown as popup @@ -139,6 +149,8 @@ class MESSAGE_CENTER_EXPORT NotificationList { // NULL. Notification instance is owned by this list. Notification* GetNotificationById(const std::string& id); + void PopupBlocked(const std::string& id); + // Returns all visible notifications, in a (priority-timestamp) order. // Suitable for rendering notifications in a MessageCenter. Notifications GetVisibleNotifications( diff --git a/chromium/ui/message_center/notification_list_unittest.cc b/chromium/ui/message_center/notification_list_unittest.cc index 995358660d6..98ee4f841b2 100644 --- a/chromium/ui/message_center/notification_list_unittest.cc +++ b/chromium/ui/message_center/notification_list_unittest.cc @@ -24,6 +24,8 @@ using base::UTF8ToUTF16; namespace message_center { +using NotificationState = NotificationList::NotificationState; + class NotificationListTest : public testing::Test { public: NotificationListTest() {} @@ -90,7 +92,13 @@ class NotificationListTest : public testing::Test { auto iter = notification_list()->GetNotification(id); if (iter == notification_list()->notifications_.end()) return NULL; - return iter->get(); + return iter->first.get(); + } + + NotificationState GetNotificationState(const std::string& id) { + auto iter = notification_list()->GetNotification(id); + EXPECT_FALSE(iter == notification_list()->notifications_.end()); + return iter->second; } NotificationList* notification_list() { return notification_list_.get(); } @@ -191,8 +199,8 @@ TEST_F(NotificationListTest, UpdateNotificationWithPriority) { std::string old_id; auto old_notification = MakeNotification(&old_id); old_notification->set_priority(DEFAULT_PRIORITY); - old_notification->set_shown_as_popup(true); notification_list()->AddNotification(std::move(old_notification)); + notification_list()->MarkSinglePopupAsShown(old_id, true); EXPECT_EQ(1u, notification_list()->NotificationCount(blockers())); std::string new_id; @@ -210,8 +218,14 @@ TEST_F(NotificationListTest, UpdateNotificationWithPriority) { // again. // In quiet mode, |shown_as_popup| should not be reset , as popup should not // be shown even though the priority is promoted. - EXPECT_EQ(static_cast<bool>(quiet_mode), - (*notifications.begin())->shown_as_popup()); + const NotificationList::PopupNotifications popup_notifications = + notification_list()->GetPopupNotifications(blockers(), nullptr); + if (quiet_mode) { + ASSERT_EQ(0U, popup_notifications.size()); + } else { + ASSERT_EQ(1U, popup_notifications.size()); + EXPECT_EQ(new_id, (*popup_notifications.begin())->id()); + } } } @@ -576,15 +590,15 @@ TEST_F(NotificationListTest, UpdateAfterMarkedAsShown) { EXPECT_EQ(2u, GetPopupCounts()); - const Notification* n1 = GetNotification(id1); - EXPECT_FALSE(n1->shown_as_popup()); - EXPECT_TRUE(n1->IsRead()); + NotificationState n1_state = GetNotificationState(id1); + EXPECT_FALSE(n1_state.shown_as_popup); + EXPECT_TRUE(n1_state.is_read); notification_list()->MarkSinglePopupAsShown(id1, true); - n1 = GetNotification(id1); - EXPECT_TRUE(n1->shown_as_popup()); - EXPECT_TRUE(n1->IsRead()); + n1_state = GetNotificationState(id1); + EXPECT_TRUE(n1_state.shown_as_popup); + EXPECT_TRUE(n1_state.is_read); const std::string replaced("test-replaced-id"); std::unique_ptr<Notification> notification(new Notification( @@ -593,11 +607,11 @@ TEST_F(NotificationListTest, UpdateAfterMarkedAsShown) { NotifierId(NotifierId::APPLICATION, kExtensionId), RichNotificationData(), NULL)); notification_list()->UpdateNotificationMessage(id1, std::move(notification)); - n1 = GetNotification(id1); + Notification* n1 = GetNotification(id1); EXPECT_TRUE(n1 == NULL); - const Notification* nr = GetNotification(replaced); - EXPECT_TRUE(nr->shown_as_popup()); - EXPECT_TRUE(nr->IsRead()); + const NotificationState nr_state = GetNotificationState(replaced); + EXPECT_TRUE(nr_state.shown_as_popup); + EXPECT_TRUE(nr_state.is_read); } TEST_F(NotificationListTest, QuietMode) { @@ -616,19 +630,6 @@ TEST_F(NotificationListTest, QuietMode) { // TODO(mukai): Add test of quiet mode with expiration. } -TEST_F(NotificationListTest, TestPushingShownNotification) { - // Create a notification and mark it as shown. - std::string id1; - std::unique_ptr<Notification> notification(MakeNotification(&id1)); - notification->set_shown_as_popup(true); - - // Call PushNotification on this notification. - notification_list()->PushNotification(std::move(notification)); - - // Ensure it is still marked as shown. - EXPECT_TRUE(GetNotification(id1)->shown_as_popup()); -} - TEST_F(NotificationListTest, TestHasNotificationOfType) { std::string id = AddNotification(); diff --git a/chromium/ui/message_center/public/cpp/notification.cc b/chromium/ui/message_center/public/cpp/notification.cc index dd06417bce3..dce03cfdb17 100644 --- a/chromium/ui/message_center/public/cpp/notification.cc +++ b/chromium/ui/message_center/public/cpp/notification.cc @@ -61,7 +61,7 @@ RichNotificationData::RichNotificationData(const RichNotificationData& other) = RichNotificationData::~RichNotificationData() = default; -Notification::Notification() = default; +Notification::Notification() : serial_number_(g_next_serial_number++) {} Notification::Notification(NotificationType type, const std::string& id, @@ -83,8 +83,6 @@ Notification::Notification(NotificationType type, notifier_id_(notifier_id), optional_fields_(optional_fields), serial_number_(g_next_serial_number++), - shown_as_popup_(false), - is_read_(false), delegate_(std::move(delegate)) {} Notification::Notification(const std::string& id, const Notification& other) @@ -121,18 +119,6 @@ std::unique_ptr<Notification> Notification::DeepCopy( return notification_copy; } -bool Notification::IsRead() const { - return is_read_ || optional_fields_.priority == MIN_PRIORITY; -} - -void Notification::CopyState(Notification* base) { - shown_as_popup_ = base->shown_as_popup(); - is_read_ = base->is_read_; - if (!delegate_.get()) - delegate_ = base->delegate(); - optional_fields_.never_timeout = base->never_timeout(); -} - void Notification::SetButtonIcon(size_t index, const gfx::Image& icon) { if (index >= optional_fields_.buttons.size()) return; @@ -185,7 +171,6 @@ std::unique_ptr<Notification> Notification::CreateSystemNotification( RichNotificationData(), new HandleNotificationClickDelegate(click_callback), gfx::kNoneIcon, SystemNotificationWarningLevel::CRITICAL_WARNING); - notification->set_clickable(true); notification->SetSystemPriority(); return notification; } @@ -227,10 +212,12 @@ std::unique_ptr<Notification> Notification::CreateSystemNotification( } // static -void RegisterVectorIcon(const gfx::VectorIcon& vector_icon) { - g_vector_icon_registry.Get().insert( - std::pair<std::string, const gfx::VectorIcon&>(vector_icon.name, - vector_icon)); +void RegisterVectorIcons( + const std::vector<const gfx::VectorIcon*>& vector_icons) { + for (const gfx::VectorIcon* icon : vector_icons) { + g_vector_icon_registry.Get().insert( + std::pair<std::string, const gfx::VectorIcon&>(icon->name, *icon)); + } } // static diff --git a/chromium/ui/message_center/public/cpp/notification.h b/chromium/ui/message_center/public/cpp/notification.h index c91d0dac645..bbe62a47f33 100644 --- a/chromium/ui/message_center/public/cpp/notification.h +++ b/chromium/ui/message_center/public/cpp/notification.h @@ -145,9 +145,6 @@ class MESSAGE_CENTER_PUBLIC_EXPORT RichNotificationData { // depending on visual assistance systems. bool should_make_spoken_feedback_for_popup_updates = true; - // Whether it should be possible for the user to click on the notification. - bool clickable = false; - #if defined(OS_CHROMEOS) // Flag if the notification is pinned. If true, the notification is pinned // and the user can't remove it. @@ -240,10 +237,6 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification { bool include_small_image, bool include_icon_images); - // Copies the internal on-memory state from |base|, i.e. shown_as_popup, - // is_read and never_timeout. - void CopyState(Notification* base); - NotificationType type() const { return type_; } void set_type(NotificationType type) { type_ = type; } @@ -372,15 +365,6 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification { } void SetButtonIcon(size_t index, const gfx::Image& icon); - bool shown_as_popup() const { return shown_as_popup_; } - void set_shown_as_popup(bool shown_as_popup) { - shown_as_popup_ = shown_as_popup; - } - - // Read status in the message center. - bool IsRead() const; - void set_is_read(bool read) { is_read_ = read; } - // Used to keep the order of notifications with the same timestamp. // The notification with lesser serial_number is considered 'older'. unsigned serial_number() { return serial_number_; } @@ -391,9 +375,6 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification { optional_fields_.never_timeout = never_timeout; } - bool clickable() const { return optional_fields_.clickable; } - void set_clickable(bool clickable) { optional_fields_.clickable = clickable; } - bool pinned() const { #if defined(OS_CHROMEOS) return optional_fields_.pinned; @@ -442,11 +423,6 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification { // method explicitly, to avoid setting it accidentally. void SetSystemPriority(); - // Delegate actions. - void Click() const { delegate()->Click(); } - void ButtonClick(int index) const { delegate()->ButtonClick(index); } - void Close(bool by_user) const { delegate()->Close(by_user); } - // Helper method to create a simple system notification. |click_callback| // will be invoked when the notification is clicked. // @@ -512,8 +488,6 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification { // TODO(estade): these book-keeping fields should be moved into // NotificationList. unsigned serial_number_; - bool shown_as_popup_; // True if this has been shown as a popup. - bool is_read_; // True if this has been seen in the message center. // A proxy object that allows access back to the JavaScript object that // represents the notification, for firing events. @@ -525,7 +499,8 @@ class MESSAGE_CENTER_PUBLIC_EXPORT Notification { // of icons it expects to see, this allows for icon lookup without // serialization/deserialization. MESSAGE_CENTER_PUBLIC_EXPORT -void RegisterVectorIcon(const gfx::VectorIcon& vector_icon); +void RegisterVectorIcons( + const std::vector<const gfx::VectorIcon*>& vector_icon); MESSAGE_CENTER_PUBLIC_EXPORT const gfx::VectorIcon* GetRegisteredVectorIcon(const std::string& id); diff --git a/chromium/ui/message_center/public/cpp/notification_delegate.cc b/chromium/ui/message_center/public/cpp/notification_delegate.cc index 42b536b97ac..267b2155a7e 100644 --- a/chromium/ui/message_center/public/cpp/notification_delegate.cc +++ b/chromium/ui/message_center/public/cpp/notification_delegate.cc @@ -20,21 +20,11 @@ void ThunkNotificationDelegate::Close(bool by_user) { impl_->Close(by_user); } -void ThunkNotificationDelegate::Click() { +void ThunkNotificationDelegate::Click( + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) { if (impl_) - impl_->Click(); -} - -void ThunkNotificationDelegate::ButtonClick(int button_index) { - if (impl_) - impl_->ButtonClick(button_index); -} - -void ThunkNotificationDelegate::ButtonClickWithReply( - int button_index, - const base::string16& reply) { - if (impl_) - impl_->ButtonClickWithReply(button_index, reply); + impl_->Click(button_index, reply); } void ThunkNotificationDelegate::SettingsClick() { @@ -72,12 +62,9 @@ HandleNotificationClickDelegate::HandleNotificationClickDelegate( HandleNotificationClickDelegate::~HandleNotificationClickDelegate() {} -void HandleNotificationClickDelegate::Click() { - if (!callback_.is_null()) - callback_.Run(base::nullopt); -} - -void HandleNotificationClickDelegate::ButtonClick(int button_index) { +void HandleNotificationClickDelegate::Click( + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) { if (!callback_.is_null()) callback_.Run(button_index); } diff --git a/chromium/ui/message_center/public/cpp/notification_delegate.h b/chromium/ui/message_center/public/cpp/notification_delegate.h index 8f5a5321969..1c2e62c4668 100644 --- a/chromium/ui/message_center/public/cpp/notification_delegate.h +++ b/chromium/ui/message_center/public/cpp/notification_delegate.h @@ -21,22 +21,19 @@ namespace message_center { // Handles actions performed on a notification. class MESSAGE_CENTER_PUBLIC_EXPORT NotificationObserver { public: - // To be called when the desktop notification is closed. If closed by a - // user explicitly (as opposed to timeout/script), |by_user| should be true. + // Called when the desktop notification is closed. If closed by a user + // explicitly (as opposed to timeout/script), |by_user| should be true. virtual void Close(bool by_user) {} - // To be called when a desktop notification is clicked. - virtual void Click() {} + // Called when a desktop notification is clicked. |button_index| is filled in + // if a button was clicked (as opposed to the body of the notification) while + // |reply| is filled in if there was an input field associated with the + // button. + virtual void Click(const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) {} - // To be called when the user clicks a button in a notification. - virtual void ButtonClick(int button_index) {} - - // To be called when the user types a reply to a notification. - virtual void ButtonClickWithReply(int button_index, - const base::string16& reply) {} - - // To be called when the user clicks the settings button in a notification - // which has a DELEGATE settings button action. + // Called when the user clicks the settings button in a notification which has + // a DELEGATE settings button action. virtual void SettingsClick() {} // Called when the user attempts to disable the notification. @@ -58,8 +55,7 @@ class MESSAGE_CENTER_PUBLIC_EXPORT NotificationDelegate // A pass-through which converts the RefCounted requirement to a WeakPtr // requirement. This class replaces the need for individual delegates that pass // through to an actual controller class, and which only exist because the -// actual controller has a strong ownership model. TODO(estade): replace many -// existing NotificationDelegate overrides with an instance of this class. +// actual controller has a strong ownership model. class MESSAGE_CENTER_PUBLIC_EXPORT ThunkNotificationDelegate : public NotificationDelegate { public: @@ -67,10 +63,8 @@ class MESSAGE_CENTER_PUBLIC_EXPORT ThunkNotificationDelegate // NotificationDelegate: void Close(bool by_user) override; - void Click() override; - void ButtonClick(int button_index) override; - void ButtonClickWithReply(int button_index, - const base::string16& reply) override; + void Click(const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) override; void SettingsClick() override; void DisableNotification() override; @@ -102,8 +96,8 @@ class MESSAGE_CENTER_PUBLIC_EXPORT HandleNotificationClickDelegate const base::RepeatingClosure& closure); // NotificationDelegate overrides: - void Click() override; - void ButtonClick(int button_index) override; + void Click(const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) override; protected: ~HandleNotificationClickDelegate() override; diff --git a/chromium/ui/message_center/public/cpp/notification_delegate_unittest.cc b/chromium/ui/message_center/public/cpp/notification_delegate_unittest.cc index 09874d1ceeb..bd600a7ee64 100644 --- a/chromium/ui/message_center/public/cpp/notification_delegate_unittest.cc +++ b/chromium/ui/message_center/public/cpp/notification_delegate_unittest.cc @@ -37,7 +37,7 @@ TEST_F(NotificationDelegateTest, ClickDelegate) { base::Bind(&NotificationDelegateTest::BodyClickCallback, base::Unretained(this))); - delegate->Click(); + delegate->Click(base::nullopt, base::nullopt); EXPECT_EQ(1, callback_count_); } @@ -45,7 +45,7 @@ TEST_F(NotificationDelegateTest, NullClickDelegate) { auto delegate = base::MakeRefCounted<HandleNotificationClickDelegate>(base::Closure()); - delegate->Click(); + delegate->Click(base::nullopt, base::nullopt); EXPECT_EQ(0, callback_count_); } @@ -54,11 +54,11 @@ TEST_F(NotificationDelegateTest, ButtonClickDelegate) { base::Bind(&NotificationDelegateTest::ButtonClickCallback, base::Unretained(this))); - delegate->Click(); + delegate->Click(base::nullopt, base::nullopt); EXPECT_EQ(1, callback_count_); EXPECT_EQ(base::nullopt, last_button_index_); - delegate->ButtonClick(3); + delegate->Click(3, base::nullopt); EXPECT_EQ(2, callback_count_); EXPECT_EQ(3, *last_button_index_); } diff --git a/chromium/ui/message_center/public/mojo/BUILD.gn b/chromium/ui/message_center/public/mojo/BUILD.gn index d6aaf8314fe..59d1d2bf31f 100644 --- a/chromium/ui/message_center/public/mojo/BUILD.gn +++ b/chromium/ui/message_center/public/mojo/BUILD.gn @@ -12,6 +12,7 @@ mojom("mojo") { public_deps = [ "//mojo/common:common_custom_types", + "//mojo/public/mojom/base", "//ui/gfx/image/mojo:interfaces", "//url/mojom:url_mojom_gurl", ] diff --git a/chromium/ui/message_center/public/mojo/notification.mojom b/chromium/ui/message_center/public/mojo/notification.mojom index 1b1b4ba3cd2..823517c3a43 100644 --- a/chromium/ui/message_center/public/mojo/notification.mojom +++ b/chromium/ui/message_center/public/mojo/notification.mojom @@ -4,7 +4,7 @@ module message_center.mojom; -import "mojo/common/time.mojom"; +import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/string16.mojom"; import "ui/gfx/image/mojo/image.mojom"; import "ui/message_center/public/mojo/notifier_id.mojom"; @@ -51,7 +51,7 @@ struct ButtonInfo { struct RichNotificationData { int32 priority; bool never_time_out; - mojo.common.mojom.Time timestamp; + mojo_base.mojom.Time timestamp; // |context_message| intentionally omitted. See https://crbug.com/797084 gfx.mojom.ImageSkia? image; gfx.mojom.ImageSkia? small_image; @@ -60,7 +60,6 @@ struct RichNotificationData { mojo_base.mojom.String16 progress_status; array<ButtonInfo> buttons; bool should_make_spoken_feedback_for_popup_updates; - bool clickable; bool pinned; // |vibration_pattern| intentionally omitted // |renotify| intentionally omitted diff --git a/chromium/ui/message_center/public/mojo/notification_struct_traits.cc b/chromium/ui/message_center/public/mojo/notification_struct_traits.cc index 35b84fdf5e0..0a51cb6fa9d 100644 --- a/chromium/ui/message_center/public/mojo/notification_struct_traits.cc +++ b/chromium/ui/message_center/public/mojo/notification_struct_traits.cc @@ -4,8 +4,8 @@ #include "ui/message_center/public/mojo/notification_struct_traits.h" -#include "mojo/common/time_struct_traits.h" #include "mojo/public/cpp/base/string16_mojom_traits.h" +#include "mojo/public/cpp/base/time_mojom_traits.h" #include "ui/gfx/image/mojo/image_skia_struct_traits.h" #include "ui/message_center/public/mojo/notifier_id_struct_traits.h" #include "url/mojom/url_gurl_mojom_traits.h" @@ -112,12 +112,6 @@ bool RichNotificationDataStructTraits:: } // static -bool RichNotificationDataStructTraits::clickable( - const RichNotificationData& r) { - return r.clickable; -} - -// static bool RichNotificationDataStructTraits::pinned(const RichNotificationData& r) { return r.pinned; } @@ -171,7 +165,6 @@ bool RichNotificationDataStructTraits::Read(RichNotificationDataDataView data, out->progress = data.progress(); out->should_make_spoken_feedback_for_popup_updates = data.should_make_spoken_feedback_for_popup_updates(); - out->clickable = data.clickable(); out->pinned = data.pinned(); // Look up the vector icon by ID. This will only work if RegisterVectorIcon @@ -179,8 +172,10 @@ bool RichNotificationDataStructTraits::Read(RichNotificationDataDataView data, std::string icon_id; if (data.ReadVectorSmallImageId(&icon_id) && !icon_id.empty()) { out->vector_small_image = message_center::GetRegisteredVectorIcon(icon_id); - if (!out->vector_small_image) + if (!out->vector_small_image) { LOG(ERROR) << "Couldn't find icon: " + icon_id; + return false; + } } out->accent_color = data.accent_color(); diff --git a/chromium/ui/message_center/public/mojo/notification_struct_traits.h b/chromium/ui/message_center/public/mojo/notification_struct_traits.h index e97e4a6c504..4c35d5f716e 100644 --- a/chromium/ui/message_center/public/mojo/notification_struct_traits.h +++ b/chromium/ui/message_center/public/mojo/notification_struct_traits.h @@ -175,7 +175,6 @@ struct StructTraits<message_center::mojom::RichNotificationDataDataView, const message_center::RichNotificationData& r); static bool should_make_spoken_feedback_for_popup_updates( const message_center::RichNotificationData& r); - static bool clickable(const message_center::RichNotificationData& r); static bool pinned(const message_center::RichNotificationData& r); static const base::string16& accessible_name( const message_center::RichNotificationData& r); diff --git a/chromium/ui/message_center/public/mojo/notifier_id_struct_traits.cc b/chromium/ui/message_center/public/mojo/notifier_id_struct_traits.cc index d2b9f6b3e64..016424fedc8 100644 --- a/chromium/ui/message_center/public/mojo/notifier_id_struct_traits.cc +++ b/chromium/ui/message_center/public/mojo/notifier_id_struct_traits.cc @@ -3,8 +3,6 @@ // found in the LICENSE file. #include "ui/message_center/public/mojo/notifier_id_struct_traits.h" - -#include "mojo/common/common_custom_types_struct_traits.h" #include "url/mojom/url_gurl_mojom_traits.h" namespace mojo { diff --git a/chromium/ui/message_center/public/mojo/struct_traits_unittest.cc b/chromium/ui/message_center/public/mojo/struct_traits_unittest.cc index 59f0263524b..39e8dcae9c2 100644 --- a/chromium/ui/message_center/public/mojo/struct_traits_unittest.cc +++ b/chromium/ui/message_center/public/mojo/struct_traits_unittest.cc @@ -49,7 +49,6 @@ class StructTraitsTest : public testing::Test, public mojom::TraitsTestService { .should_make_spoken_feedback_for_popup_updates, output.rich_notification_data() .should_make_spoken_feedback_for_popup_updates); - EXPECT_EQ(input.clickable(), output.clickable()); EXPECT_EQ(input.accessible_name(), output.accessible_name()); EXPECT_EQ(input.accent_color(), output.accent_color()); EXPECT_EQ(input.should_show_settings_button(), @@ -97,7 +96,6 @@ TEST_F(StructTraitsTest, Notification) { input.set_type(NotificationType::NOTIFICATION_TYPE_PROGRESS); input.set_progress(50); input.set_progress_status(base::ASCIIToUTF16("progress text")); - input.set_clickable(!input.clickable()); input.set_image(gfx::test::CreateImage(48, 48)); input.set_small_image(gfx::test::CreateImage(16, 16)); input.set_accent_color(SK_ColorMAGENTA); diff --git a/chromium/ui/message_center/ui_controller.cc b/chromium/ui/message_center/ui_controller.cc index 997b24750f9..1572243177f 100644 --- a/chromium/ui/message_center/ui_controller.cc +++ b/chromium/ui/message_center/ui_controller.cc @@ -130,14 +130,10 @@ void UiController::OnNotificationUpdated(const std::string& notification_id) { OnMessageCenterChanged(); } -void UiController::OnNotificationClicked(const std::string& notification_id) { - if (popups_visible_) - OnMessageCenterChanged(); -} - -void UiController::OnNotificationButtonClicked( +void UiController::OnNotificationClicked( const std::string& notification_id, - int button_index) { + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) { if (popups_visible_) OnMessageCenterChanged(); } diff --git a/chromium/ui/message_center/ui_controller.h b/chromium/ui/message_center/ui_controller.h index 582ad520d4e..5b489e06bae 100644 --- a/chromium/ui/message_center/ui_controller.h +++ b/chromium/ui/message_center/ui_controller.h @@ -60,9 +60,10 @@ class MESSAGE_CENTER_EXPORT UiController : public MessageCenterObserver { void OnNotificationRemoved(const std::string& notification_id, bool by_user) override; void OnNotificationUpdated(const std::string& notification_id) override; - void OnNotificationClicked(const std::string& notification_id) override; - void OnNotificationButtonClicked(const std::string& notification_id, - int button_index) override; + void OnNotificationClicked( + const std::string& notification_id, + const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) override; void OnNotificationSettingsClicked(bool handled) override; void OnNotificationDisplayed(const std::string& notification_id, const DisplaySource source) override; diff --git a/chromium/ui/message_center/vector_icons/notification_close_button.1x.icon b/chromium/ui/message_center/vector_icons/notification_close_button.1x.icon deleted file mode 100644 index 2bea75dea16..00000000000 --- a/chromium/ui/message_center/vector_icons/notification_close_button.1x.icon +++ /dev/null @@ -1,19 +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. - -CANVAS_DIMENSIONS, 12, -MOVE_TO, 10.5f, 2.36f, -LINE_TO, 9.64f, 1.5f, -LINE_TO, 6, 5.14f, -LINE_TO, 2.36f, 1.5f, -LINE_TO, 1.5f, 2.36f, -LINE_TO, 5.14f, 6, -LINE_TO, 1.5f, 9.64f, -LINE_TO, 2.36f, 10.5f, -LINE_TO, 6, 6.86f, -LINE_TO, 9.64f, 10.5f, -LINE_TO, 10.5f, 9.64f, -LINE_TO, 6.86f, 6, -CLOSE, -END diff --git a/chromium/ui/message_center/vector_icons/notification_close_button.icon b/chromium/ui/message_center/vector_icons/notification_close_button.icon index 5ce70e49153..ced7c26b683 100644 --- a/chromium/ui/message_center/vector_icons/notification_close_button.icon +++ b/chromium/ui/message_center/vector_icons/notification_close_button.icon @@ -15,5 +15,19 @@ LINE_TO, 12, 13.73f, LINE_TO, 18.27f, 20, LINE_TO, 20, 18.27f, LINE_TO, 13.73f, 12, -CLOSE, -END +CLOSE + +CANVAS_DIMENSIONS, 12, +MOVE_TO, 10.5f, 2.36f, +LINE_TO, 9.64f, 1.5f, +LINE_TO, 6, 5.14f, +LINE_TO, 2.36f, 1.5f, +LINE_TO, 1.5f, 2.36f, +LINE_TO, 5.14f, 6, +LINE_TO, 1.5f, 9.64f, +LINE_TO, 2.36f, 10.5f, +LINE_TO, 6, 6.86f, +LINE_TO, 9.64f, 10.5f, +LINE_TO, 10.5f, 9.64f, +LINE_TO, 6.86f, 6, +CLOSE diff --git a/chromium/ui/message_center/vector_icons/notification_expand_less.icon b/chromium/ui/message_center/vector_icons/notification_expand_less.icon index b30ee4211b5..d15fce2af57 100644 --- a/chromium/ui/message_center/vector_icons/notification_expand_less.icon +++ b/chromium/ui/message_center/vector_icons/notification_expand_less.icon @@ -9,5 +9,4 @@ LINE_TO, 13.33f, 13, LINE_TO, 15, 11.31f, R_LINE_TO, -7, -6.89f, R_LINE_TO, -7, 6.89f, -CLOSE, -END +CLOSE diff --git a/chromium/ui/message_center/vector_icons/notification_expand_more.icon b/chromium/ui/message_center/vector_icons/notification_expand_more.icon index b779f5f8e5c..155f6b58871 100644 --- a/chromium/ui/message_center/vector_icons/notification_expand_more.icon +++ b/chromium/ui/message_center/vector_icons/notification_expand_more.icon @@ -9,5 +9,4 @@ LINE_TO, 13.33f, 3, LINE_TO, 15, 4.69f, R_LINE_TO, -7, 6.89f, R_LINE_TO, -7, -6.89f, -CLOSE, -END +CLOSE diff --git a/chromium/ui/message_center/vector_icons/notification_inline_reply.icon b/chromium/ui/message_center/vector_icons/notification_inline_reply.icon index e12869934bf..abb71d49ff5 100644 --- a/chromium/ui/message_center/vector_icons/notification_inline_reply.icon +++ b/chromium/ui/message_center/vector_icons/notification_inline_reply.icon @@ -9,5 +9,4 @@ LINE_TO, 10.04f, 14, LINE_TO, 10, 40.44f, LINE_TO, 64.29f, 48, LINE_TO, 10, 55.56f, -CLOSE, -END
\ No newline at end of file +CLOSE diff --git a/chromium/ui/message_center/vector_icons/notification_settings_button.1x.icon b/chromium/ui/message_center/vector_icons/notification_settings_button.1x.icon deleted file mode 100644 index d9996915b9b..00000000000 --- a/chromium/ui/message_center/vector_icons/notification_settings_button.1x.icon +++ /dev/null @@ -1,54 +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. - -CANVAS_DIMENSIONS, 12, -MOVE_TO, 9.82f, 6.49f, -CUBIC_TO, 9.84f, 6.33f, 9.85f, 6.17f, 9.85f, 6, -CUBIC_TO, 9.85f, 5.84f, 9.84f, 5.67f, 9.82f, 5.51f, -LINE_TO, 10.9f, 4.68f, -CUBIC_TO, 11, 4.61f, 11.03f, 4.47f, 10.97f, 4.37f, -LINE_TO, 9.94f, 2.63f, -CUBIC_TO, 9.87f, 2.53f, 9.74f, 2.48f, 9.62f, 2.53f, -LINE_TO, 8.34f, 3.03f, -CUBIC_TO, 8.08f, 2.83f, 7.79f, 2.66f, 7.48f, 2.54f, -LINE_TO, 7.28f, 1.21f, -CUBIC_TO, 7.26f, 1.09f, 7.15f, 1, 7.03f, 1, -LINE_TO, 4.97f, 1, -CUBIC_TO, 4.84f, 1, 4.74f, 1.09f, 4.72f, 1.21f, -LINE_TO, 4.52f, 2.54f, -CUBIC_TO, 4.21f, 2.66f, 3.92f, 2.83f, 3.66f, 3.03f, -LINE_TO, 2.38f, 2.53f, -CUBIC_TO, 2.26f, 2.48f, 2.13f, 2.53f, 2.06f, 2.63f, -LINE_TO, 1.03f, 4.37f, -CUBIC_TO, 0.97f, 4.47f, 1, 4.61f, 1.1f, 4.68f, -LINE_TO, 2.18f, 5.51f, -CUBIC_TO, 2.16f, 5.67f, 2.14f, 5.84f, 2.14f, 6, -CUBIC_TO, 2.14f, 6.17f, 2.16f, 6.33f, 2.18f, 6.49f, -LINE_TO, 1.1f, 7.32f, -CUBIC_TO, 1, 7.39f, 0.97f, 7.53f, 1.03f, 7.64f, -LINE_TO, 2.06f, 9.37f, -CUBIC_TO, 2.13f, 9.48f, 2.26f, 9.52f, 2.38f, 9.48f, -LINE_TO, 3.66f, 8.97f, -CUBIC_TO, 3.92f, 9.17f, 4.21f, 9.34f, 4.52f, 9.47f, -LINE_TO, 4.72f, 10.79f, -CUBIC_TO, 4.74f, 10.91f, 4.84f, 11, 4.97f, 11, -LINE_TO, 7.03f, 11, -CUBIC_TO, 7.15f, 11, 7.26f, 10.91f, 7.28f, 10.79f, -LINE_TO, 7.47f, 9.47f, -CUBIC_TO, 7.79f, 9.34f, 8.08f, 9.17f, 8.34f, 8.97f, -LINE_TO, 9.62f, 9.48f, -CUBIC_TO, 9.74f, 9.52f, 9.87f, 9.48f, 9.94f, 9.37f, -LINE_TO, 10.96f, 7.64f, -CUBIC_TO, 11.03f, 7.53f, 11, 7.39f, 10.9f, 7.32f, -LINE_TO, 9.82f, 6.49f, -LINE_TO, 9.82f, 6.49f, -CLOSE, -MOVE_TO, 6, 7.5f, -CUBIC_TO, 5.17f, 7.5f, 4.5f, 6.83f, 4.5f, 6, -CUBIC_TO, 4.5f, 5.17f, 5.17f, 4.5f, 6, 4.5f, -CUBIC_TO, 6.83f, 4.5f, 7.5f, 5.17f, 7.5f, 6, -CUBIC_TO, 7.5f, 6.83f, 6.83f, 7.5f, 6, 7.5f, -LINE_TO, 6, 7.5f, -CLOSE, -END diff --git a/chromium/ui/message_center/vector_icons/notification_settings_button.icon b/chromium/ui/message_center/vector_icons/notification_settings_button.icon index ae7e26af6fa..3a0a1d9735a 100644 --- a/chromium/ui/message_center/vector_icons/notification_settings_button.icon +++ b/chromium/ui/message_center/vector_icons/notification_settings_button.icon @@ -50,5 +50,54 @@ CUBIC_TO, 9, 10.34f, 10.34f, 9, 12, 9, CUBIC_TO, 13.66f, 9, 15, 10.34f, 15, 12, CUBIC_TO, 15, 13.66f, 13.66f, 15, 12, 15, LINE_TO, 12, 15, +CLOSE + +CANVAS_DIMENSIONS, 12, +MOVE_TO, 9.82f, 6.49f, +CUBIC_TO, 9.84f, 6.33f, 9.85f, 6.17f, 9.85f, 6, +CUBIC_TO, 9.85f, 5.84f, 9.84f, 5.67f, 9.82f, 5.51f, +LINE_TO, 10.9f, 4.68f, +CUBIC_TO, 11, 4.61f, 11.03f, 4.47f, 10.97f, 4.37f, +LINE_TO, 9.94f, 2.63f, +CUBIC_TO, 9.87f, 2.53f, 9.74f, 2.48f, 9.62f, 2.53f, +LINE_TO, 8.34f, 3.03f, +CUBIC_TO, 8.08f, 2.83f, 7.79f, 2.66f, 7.48f, 2.54f, +LINE_TO, 7.28f, 1.21f, +CUBIC_TO, 7.26f, 1.09f, 7.15f, 1, 7.03f, 1, +LINE_TO, 4.97f, 1, +CUBIC_TO, 4.84f, 1, 4.74f, 1.09f, 4.72f, 1.21f, +LINE_TO, 4.52f, 2.54f, +CUBIC_TO, 4.21f, 2.66f, 3.92f, 2.83f, 3.66f, 3.03f, +LINE_TO, 2.38f, 2.53f, +CUBIC_TO, 2.26f, 2.48f, 2.13f, 2.53f, 2.06f, 2.63f, +LINE_TO, 1.03f, 4.37f, +CUBIC_TO, 0.97f, 4.47f, 1, 4.61f, 1.1f, 4.68f, +LINE_TO, 2.18f, 5.51f, +CUBIC_TO, 2.16f, 5.67f, 2.14f, 5.84f, 2.14f, 6, +CUBIC_TO, 2.14f, 6.17f, 2.16f, 6.33f, 2.18f, 6.49f, +LINE_TO, 1.1f, 7.32f, +CUBIC_TO, 1, 7.39f, 0.97f, 7.53f, 1.03f, 7.64f, +LINE_TO, 2.06f, 9.37f, +CUBIC_TO, 2.13f, 9.48f, 2.26f, 9.52f, 2.38f, 9.48f, +LINE_TO, 3.66f, 8.97f, +CUBIC_TO, 3.92f, 9.17f, 4.21f, 9.34f, 4.52f, 9.47f, +LINE_TO, 4.72f, 10.79f, +CUBIC_TO, 4.74f, 10.91f, 4.84f, 11, 4.97f, 11, +LINE_TO, 7.03f, 11, +CUBIC_TO, 7.15f, 11, 7.26f, 10.91f, 7.28f, 10.79f, +LINE_TO, 7.47f, 9.47f, +CUBIC_TO, 7.79f, 9.34f, 8.08f, 9.17f, 8.34f, 8.97f, +LINE_TO, 9.62f, 9.48f, +CUBIC_TO, 9.74f, 9.52f, 9.87f, 9.48f, 9.94f, 9.37f, +LINE_TO, 10.96f, 7.64f, +CUBIC_TO, 11.03f, 7.53f, 11, 7.39f, 10.9f, 7.32f, +LINE_TO, 9.82f, 6.49f, +LINE_TO, 9.82f, 6.49f, CLOSE, -END +MOVE_TO, 6, 7.5f, +CUBIC_TO, 5.17f, 7.5f, 4.5f, 6.83f, 4.5f, 6, +CUBIC_TO, 4.5f, 5.17f, 5.17f, 4.5f, 6, 4.5f, +CUBIC_TO, 6.83f, 4.5f, 7.5f, 5.17f, 7.5f, 6, +CUBIC_TO, 7.5f, 6.83f, 6.83f, 7.5f, 6, 7.5f, +LINE_TO, 6, 7.5f, +CLOSE diff --git a/chromium/ui/message_center/vector_icons/product.icon b/chromium/ui/message_center/vector_icons/product.icon index 97f8448b152..7068cb20b97 100644 --- a/chromium/ui/message_center/vector_icons/product.icon +++ b/chromium/ui/message_center/vector_icons/product.icon @@ -32,5 +32,4 @@ R_CUBIC_TO, -7.73f, 0, -14, -6.27f, -14, -14, R_CUBIC_TO, 0, -7.73f, 6.27f, -14, 14, -14, R_CUBIC_TO, 7.73f, 0, 14, 6.27f, 14, 14, R_CUBIC_TO, 0, 7.73f, -6.27f, 14, -14, 14, -CLOSE, -END +CLOSE diff --git a/chromium/ui/message_center/views/message_popup_collection.cc b/chromium/ui/message_center/views/message_popup_collection.cc index edd1bbdf3ad..e28f4563676 100644 --- a/chromium/ui/message_center/views/message_popup_collection.cc +++ b/chromium/ui/message_center/views/message_popup_collection.cc @@ -172,7 +172,8 @@ void MessagePopupCollection::UpdateWidgets() { // Create top-level notification. MessageView* view = MessageViewFactory::Create(notification, true); observed_views_.Add(view); - view->SetExpanded(true); + if (!view->IsManuallyExpandedOrCollapsed()) + view->SetExpanded(view->IsAutoExpandingAllowed()); #if !defined(OS_CHROMEOS) view->set_context_menu_controller(context_menu_controller_.get()); @@ -190,8 +191,14 @@ void MessagePopupCollection::UpdateWidgets() { ToastContentsView* toast = new ToastContentsView( notification.id(), alignment_delegate_, weak_factory_.GetWeakPtr()); + + const RichNotificationData& optional_fields = + notification.rich_notification_data(); + bool a11y_feedback_for_updates = + optional_fields.should_make_spoken_feedback_for_popup_updates; + // There will be no contents already since this is a new ToastContentsView. - toast->SetContents(view, /*a11y_feedback_for_updates=*/false); + toast->SetContents(view, a11y_feedback_for_updates); toasts_.push_back(toast); gfx::Size preferred_size = toast->GetPreferredSize(); @@ -343,8 +350,7 @@ void MessagePopupCollection::OnNotificationRemoved( RemoveToast(*iter, /*mark_as_shown=*/true); - if (by_user) - RepositionWidgets(); + DoUpdate(); } void MessagePopupCollection::OnNotificationUpdated( diff --git a/chromium/ui/message_center/views/message_view.cc b/chromium/ui/message_center/views/message_view.cc index 41912b341f7..7c8b678de4a 100644 --- a/chromium/ui/message_center/views/message_view.cc +++ b/chromium/ui/message_center/views/message_view.cc @@ -6,6 +6,7 @@ #include "base/feature_list.h" #include "base/strings/utf_string_conversions.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" @@ -17,6 +18,7 @@ #include "ui/message_center/message_center.h" #include "ui/message_center/public/cpp/features.h" #include "ui/message_center/public/cpp/message_center_constants.h" +#include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/background.h" #include "ui/views/border.h" @@ -94,7 +96,11 @@ MessageView::~MessageView() {} void MessageView::UpdateWithNotification(const Notification& notification) { pinned_ = notification.pinned(); - accessible_name_ = CreateAccessibleName(notification); + base::string16 new_accessible_name = CreateAccessibleName(notification); + if (new_accessible_name != accessible_name_) { + accessible_name_ = new_accessible_name; + NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true); + } slide_out_controller_.set_enabled(!GetPinned()); } @@ -124,6 +130,21 @@ void MessageView::SetIsNested() { } } +bool MessageView::IsCloseButtonFocused() const { + auto* control_buttons_view = GetControlButtonsView(); + return control_buttons_view ? control_buttons_view->IsCloseButtonFocused() + : false; +} + +void MessageView::RequestFocusOnCloseButton() { + auto* control_buttons_view = GetControlButtonsView(); + if (!control_buttons_view) + return; + + control_buttons_view->RequestFocusOnCloseButton(); + UpdateControlButtonsVisibility(); +} + void MessageView::SetExpanded(bool expanded) { // Not implemented by default. } @@ -133,6 +154,11 @@ bool MessageView::IsExpanded() const { return false; } +bool MessageView::IsAutoExpandingAllowed() const { + // Allowed by default. + return true; +} + bool MessageView::IsManuallyExpandedOrCollapsed() const { // Not implemented by default. return false; @@ -160,11 +186,18 @@ void MessageView::GetAccessibleNodeData(ui::AXNodeData* node_data) { } bool MessageView::OnMousePressed(const ui::MouseEvent& event) { + return true; +} + +bool MessageView::OnMouseDragged(const ui::MouseEvent& event) { + return true; +} + +void MessageView::OnMouseReleased(const ui::MouseEvent& event) { if (!event.IsOnlyLeftMouseButton()) - return false; + return; MessageCenter::Get()->ClickOnNotification(notification_id_); - return true; } bool MessageView::OnKeyPressed(const ui::KeyEvent& event) { @@ -271,8 +304,17 @@ ui::Layer* MessageView::GetSlideOutLayer() { void MessageView::OnSlideChanged() {} void MessageView::OnSlideOut() { - MessageCenter::Get()->RemoveNotification(notification_id_, - true /* by_user */); + // As a workaround for a MessagePopupCollection bug https://crbug.com/805208, + // pass false to by_user although it is triggered by user. + // TODO(tetsui): Rewrite MessagePopupCollection and remove this hack. + if (pinned_) { + // Also a workaround to not break notification pinning. + MessageCenter::Get()->MarkSinglePopupAsShown( + notification_id_, true /* mark_notification_as_read */); + } else { + MessageCenter::Get()->RemoveNotification(notification_id_, + true /* by_user */); + } } bool MessageView::GetPinned() const { diff --git a/chromium/ui/message_center/views/message_view.h b/chromium/ui/message_center/views/message_view.h index 0cbbaf18602..74df97012b8 100644 --- a/chromium/ui/message_center/views/message_view.h +++ b/chromium/ui/message_center/views/message_view.h @@ -28,14 +28,17 @@ class ScrollView; namespace message_center { +namespace test { +class MessagePopupCollectionTest; +} + class Notification; class NotificationControlButtonsView; // An base class for a notification entry. Contains background and other // elements shared by derived notification views. -class MESSAGE_CENTER_EXPORT MessageView - : public views::InkDropHostView, - public views::SlideOutController::Delegate { +class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView, + public SlideOutController::Delegate { public: static const char kViewClassName[]; @@ -55,13 +58,16 @@ class MESSAGE_CENTER_EXPORT MessageView // Creates a shadow around the notification and changes slide-out behavior. void SetIsNested(); + bool IsCloseButtonFocused() const; + void RequestFocusOnCloseButton(); + virtual NotificationControlButtonsView* GetControlButtonsView() const = 0; - virtual bool IsCloseButtonFocused() const = 0; - virtual void RequestFocusOnCloseButton() = 0; virtual void UpdateControlButtonsVisibility() = 0; + virtual const char* GetMessageViewSubClassName() const = 0; virtual void SetExpanded(bool expanded); virtual bool IsExpanded() const; + virtual bool IsAutoExpandingAllowed() const; virtual bool IsManuallyExpandedOrCollapsed() const; virtual void SetManuallyExpandedOrCollapsed(bool value); @@ -78,16 +84,20 @@ class MESSAGE_CENTER_EXPORT MessageView // views::View void GetAccessibleNodeData(ui::AXNodeData* node_data) override; bool OnMousePressed(const ui::MouseEvent& event) override; + bool OnMouseDragged(const ui::MouseEvent& event) override; + void OnMouseReleased(const ui::MouseEvent& event) override; bool OnKeyPressed(const ui::KeyEvent& event) override; bool OnKeyReleased(const ui::KeyEvent& event) override; void OnPaint(gfx::Canvas* canvas) override; void OnFocus() override; void OnBlur() override; void Layout() override; - const char* GetClassName() const override; void OnGestureEvent(ui::GestureEvent* event) override; + // Subclasses of MessageView shouldn't use this method to distinguish classes. + // Instead, use GetMessageViewSubClassName(). + const char* GetClassName() const final; - // views::SlideOutController::Delegate + // message_center::SlideOutController::Delegate ui::Layer* GetSlideOutLayer() override; void OnSlideChanged() override; void OnSlideOut() override; @@ -113,6 +123,8 @@ class MESSAGE_CENTER_EXPORT MessageView bool is_nested() const { return is_nested_; } private: + friend class test::MessagePopupCollectionTest; + std::string notification_id_; views::View* background_view_ = nullptr; // Owned by views hierarchy. views::ScrollView* scroller_ = nullptr; @@ -124,7 +136,7 @@ class MESSAGE_CENTER_EXPORT MessageView std::unique_ptr<views::Painter> focus_painter_; - views::SlideOutController slide_out_controller_; + message_center::SlideOutController slide_out_controller_; // True if |this| is embedded in another view. Equivalent to |!top_level| in // MessageViewFactory parlance. diff --git a/chromium/ui/message_center/views/notification_header_view.cc b/chromium/ui/message_center/views/notification_header_view.cc index 731494c65f6..b9b83d8e7c3 100644 --- a/chromium/ui/message_center/views/notification_header_view.cc +++ b/chromium/ui/message_center/views/notification_header_view.cc @@ -333,6 +333,17 @@ void NotificationHeaderView::ClearOverflowIndicator() { UpdateSummaryTextVisibility(); } +void NotificationHeaderView::GetAccessibleNodeData(ui::AXNodeData* node_data) { + Button::GetAccessibleNodeData(node_data); + + node_data->SetName(app_name_view_->text()); + node_data->SetDescription(summary_text_view_->text() + + base::ASCIIToUTF16(" ") + timestamp_view_->text()); + + if (is_expanded_) + node_data->AddState(ax::mojom::State::kExpanded); +} + void NotificationHeaderView::SetTimestamp(base::Time past) { timestamp_view_->SetText(FormatToRelativeTime(past)); has_timestamp_ = true; @@ -361,6 +372,7 @@ void NotificationHeaderView::SetExpanded(bool expanded) { expand_button_->SetTooltipText(l10n_util::GetStringUTF16( expanded ? IDS_MESSAGE_CENTER_COLLAPSE_NOTIFICATION : IDS_MESSAGE_CENTER_EXPAND_NOTIFICATION)); + NotifyAccessibilityEvent(ax::mojom::Event::kStateChanged, true); } void NotificationHeaderView::SetAccentColor(SkColor color) { diff --git a/chromium/ui/message_center/views/notification_header_view.h b/chromium/ui/message_center/views/notification_header_view.h index dd40f3f5ebf..b1f14665034 100644 --- a/chromium/ui/message_center/views/notification_header_view.h +++ b/chromium/ui/message_center/views/notification_header_view.h @@ -42,6 +42,9 @@ class NotificationHeaderView : public views::Button { bool IsExpandButtonEnabled(); void SetSubpixelRenderingEnabled(bool enabled); + // views::View: + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + // Button override: std::unique_ptr<views::InkDrop> CreateInkDrop() override; diff --git a/chromium/ui/message_center/views/notification_view.cc b/chromium/ui/message_center/views/notification_view.cc index 7dc5d5d0346..01604e6e8e7 100644 --- a/chromium/ui/message_center/views/notification_view.cc +++ b/chromium/ui/message_center/views/notification_view.cc @@ -43,7 +43,6 @@ #include "ui/views/layout/fill_layout.h" #include "ui/views/native_cursor.h" #include "ui/views/painter.h" -#include "ui/views/view_targeter.h" #include "ui/views/widget/widget.h" #include "url/gurl.h" @@ -146,33 +145,7 @@ void NotificationItemView::SetVisible(bool visible) { // NotificationView //////////////////////////////////////////////////////////// -views::View* NotificationView::TargetForRect(views::View* root, - const gfx::Rect& rect) { - CHECK_EQ(root, this); - - // TODO(tdanderson): Modify this function to support rect-based event - // targeting. Using the center point of |rect| preserves this function's - // expected behavior for the time being. - gfx::Point point = rect.CenterPoint(); - - // Want to return this for underlying views, otherwise GetCursor is not - // called. But buttons are exceptions, they'll have their own event handlings. - std::vector<views::View*> buttons(action_buttons_.begin(), - action_buttons_.end()); - if (control_buttons_view_->settings_button()) - buttons.push_back(control_buttons_view_->settings_button()); - if (control_buttons_view_->close_button()) - buttons.push_back(control_buttons_view_->close_button()); - - for (size_t i = 0; i < buttons.size(); ++i) { - gfx::Point point_in_child = point; - ConvertPointToTarget(this, buttons[i], &point_in_child); - if (buttons[i]->HitTestPoint(point_in_child)) - return buttons[i]->GetEventHandlerForPoint(point_in_child); - } - - return root; -} +const char NotificationView::kMessageViewSubClassName[] = "NotificationView"; void NotificationView::CreateOrUpdateViews(const Notification& notification) { CreateOrUpdateTitleView(notification); @@ -187,7 +160,7 @@ void NotificationView::CreateOrUpdateViews(const Notification& notification) { } NotificationView::NotificationView(const Notification& notification) - : MessageView(notification), clickable_(notification.clickable()) { + : MessageView(notification) { // Create the top_view_, which collects into a vertical box all content // at the top of the notification (to the right of the icon) except for the // close button. @@ -221,8 +194,6 @@ NotificationView::NotificationView(const Notification& notification) AddChildView(small_image_view_.get()); UpdateControlButtonsVisibilityWithNotification(notification); - SetEventTargeter( - std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); set_notify_enter_exit_on_child(true); } @@ -336,12 +307,6 @@ void NotificationView::ScrollRectToVisible(const gfx::Rect& rect) { views::View::ScrollRectToVisible(GetLocalBounds()); } -gfx::NativeCursor NotificationView::GetCursor(const ui::MouseEvent& event) { - return clickable_ ? views::GetNativeHandCursor() - : views::View::GetCursor(event); -} - - void NotificationView::OnMouseEntered(const ui::MouseEvent& event) { MessageView::OnMouseEntered(event); UpdateControlButtonsVisibility(); @@ -380,14 +345,6 @@ void NotificationView::ButtonPressed(views::Button* sender, NOTREACHED(); } -bool NotificationView::IsCloseButtonFocused() const { - return control_buttons_view_->IsCloseButtonFocused(); -} - -void NotificationView::RequestFocusOnCloseButton() { - control_buttons_view_->RequestFocusOnCloseButton(); -} - void NotificationView::CreateOrUpdateTitleView( const Notification& notification) { if (notification.title().empty()) { @@ -672,6 +629,10 @@ NotificationControlButtonsView* NotificationView::GetControlButtonsView() return control_buttons_view_; } +const char* NotificationView::GetMessageViewSubClassName() const { + return kMessageViewSubClassName; +} + int NotificationView::GetMessageLineLimit(int title_lines, int width) const { // Image notifications require that the image must be kept flush against // their icons, but we can allow more text if no image. diff --git a/chromium/ui/message_center/views/notification_view.h b/chromium/ui/message_center/views/notification_view.h index f1f0e2fbdef..0f1f8ced86f 100644 --- a/chromium/ui/message_center/views/notification_view.h +++ b/chromium/ui/message_center/views/notification_view.h @@ -13,7 +13,6 @@ #include "ui/message_center/views/message_view.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/image_button.h" -#include "ui/views/view_targeter_delegate.h" namespace views { class ImageView; @@ -31,11 +30,11 @@ class ProportionalImageView; // list) except the custom notification. Future notification types may be // handled by other classes, in which case instances of those classes would be // returned by the Create() factory method below. -class MESSAGE_CENTER_EXPORT NotificationView - : public MessageView, - public views::ButtonListener, - public views::ViewTargeterDelegate { +class MESSAGE_CENTER_EXPORT NotificationView : public MessageView, + public views::ButtonListener { public: + static const char kMessageViewSubClassName[]; + explicit NotificationView(const Notification& notification); ~NotificationView() override; @@ -45,17 +44,15 @@ class MESSAGE_CENTER_EXPORT NotificationView void Layout() override; void OnFocus() override; void ScrollRectToVisible(const gfx::Rect& rect) override; - gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; // Overridden from MessageView: void UpdateWithNotification(const Notification& notification) override; void ButtonPressed(views::Button* sender, const ui::Event& event) override; - bool IsCloseButtonFocused() const override; - void RequestFocusOnCloseButton() override; void UpdateControlButtonsVisibility() override; NotificationControlButtonsView* GetControlButtonsView() const override; + const char* GetMessageViewSubClassName() const final; private: FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, CreateOrUpdateTest); @@ -71,9 +68,6 @@ class MESSAGE_CENTER_EXPORT NotificationView friend class NotificationViewTest; - // views::ViewTargeterDelegate: - views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override; - void CreateOrUpdateViews(const Notification& notification); void CreateOrUpdateTitleView(const Notification& notification); @@ -101,9 +95,6 @@ class MESSAGE_CENTER_EXPORT NotificationView // Shrink the topmost label not to be covered by the control button. void ShrinkTopmostLabel(); - // Describes whether the view should display a hand pointer or not. - bool clickable_; - // Weak references to NotificationView descendants owned by their parents. views::View* top_view_ = nullptr; BoundedLabel* title_view_ = nullptr; diff --git a/chromium/ui/message_center/views/notification_view_md.cc b/chromium/ui/message_center/views/notification_view_md.cc index eaf1c4852b6..8d26e265a09 100644 --- a/chromium/ui/message_center/views/notification_view_md.cc +++ b/chromium/ui/message_center/views/notification_view_md.cc @@ -12,6 +12,8 @@ #include "components/url_formatter/elide_url.h" #include "ui/base/cursor/cursor.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/gesture_detection/gesture_provider_config_helper.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/size.h" @@ -42,7 +44,6 @@ #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/native_cursor.h" -#include "ui/views/view_targeter.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -169,6 +170,10 @@ class ClickActivator : public ui::EventHandler { } // anonymous namespace +// static +const char NotificationViewMD::kMessageViewSubClassName[] = + "NotificationViewMD"; + // ItemView //////////////////////////////////////////////////////////////////// ItemView::ItemView(const NotificationItem& item) { @@ -533,44 +538,6 @@ class InlineSettingsRadioButton : public views::RadioButton { // NotificationViewMD // //////////////////////////////////////////////////////////// -views::View* NotificationViewMD::TargetForRect(views::View* root, - const gfx::Rect& rect) { - CHECK_EQ(root, this); - - // TODO(tetsui): Modify this function to support rect-based event - // targeting. Using the center point of |rect| preserves this function's - // expected behavior for the time being. - gfx::Point point = rect.CenterPoint(); - - // Want to return this for underlying views, otherwise GetCursor is not - // called. But buttons are exceptions, they'll have their own event handlings. - std::vector<views::View*> buttons; - if (header_row_->expand_button()) - buttons.push_back(header_row_->expand_button()); - buttons.push_back(header_row_); - - if (action_buttons_row_->visible()) { - buttons.insert(buttons.end(), action_buttons_.begin(), - action_buttons_.end()); - } - if (inline_reply_->visible()) - buttons.push_back(inline_reply_); - if (settings_row_) { - buttons.push_back(block_all_button_); - buttons.push_back(dont_block_button_); - buttons.push_back(settings_done_button_); - } - - for (size_t i = 0; i < buttons.size(); ++i) { - gfx::Point point_in_child = point; - ConvertPointToTarget(this, buttons[i], &point_in_child); - if (buttons[i]->HitTestPoint(point_in_child)) - return buttons[i]->GetEventHandlerForPoint(point_in_child); - } - - return root; -} - void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) { CreateOrUpdateContextTitleView(notification); CreateOrUpdateTitleView(notification); @@ -591,8 +558,7 @@ void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) { NotificationViewMD::NotificationViewMD(const Notification& notification) : MessageView(notification), - ink_drop_container_(new views::InkDropContainerView()), - clickable_(notification.clickable()) { + ink_drop_container_(new views::InkDropContainerView()) { SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::kVertical, gfx::Insets(), 0)); @@ -657,8 +623,6 @@ NotificationViewMD::NotificationViewMD(const Notification& notification) CreateOrUpdateViews(notification); UpdateControlButtonsVisibilityWithNotification(notification); - SetEventTargeter( - std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); set_notify_enter_exit_on_child(true); click_activator_ = std::make_unique<ClickActivator>(this); @@ -721,30 +685,28 @@ void NotificationViewMD::ScrollRectToVisible(const gfx::Rect& rect) { views::View::ScrollRectToVisible(GetLocalBounds()); } -gfx::NativeCursor NotificationViewMD::GetCursor(const ui::MouseEvent& event) { - // Do not change the cursor on a notification that isn't clickable. - if (!clickable_) - return views::View::GetCursor(event); - - // Do not change the cursor on the actions row. - if (expanded_) { - DCHECK(actions_row_); - gfx::Point point_in_child = event.location(); - ConvertPointToTarget(this, actions_row_, &point_in_child); - if (actions_row_->HitTestPoint(point_in_child)) - return views::View::GetCursor(event); - } - - // Do not change the cursor when inline settings is shown. - if (settings_row_ && settings_row_->visible()) - return views::View::GetCursor(event); +bool NotificationViewMD::OnMousePressed(const ui::MouseEvent& event) { + last_mouse_pressed_timestamp_ = base::TimeTicks(event.time_stamp()); + return true; +} - return views::GetNativeHandCursor(); +bool NotificationViewMD::OnMouseDragged(const ui::MouseEvent& event) { + return true; } -bool NotificationViewMD::OnMousePressed(const ui::MouseEvent& event) { +void NotificationViewMD::OnMouseReleased(const ui::MouseEvent& event) { if (!event.IsOnlyLeftMouseButton()) - return false; + return; + + // The mouse has been clicked for a long time. + if (ui::EventTimeStampToSeconds(event.time_stamp()) - + ui::EventTimeStampToSeconds(last_mouse_pressed_timestamp_) > + ui::GetGestureProviderConfig( + ui::GestureProviderConfigType::CURRENT_PLATFORM) + .gesture_detector_config.longpress_timeout.InSecondsF()) { + ToggleInlineSettings(event); + return; + } // Ignore click of actions row outside action buttons. if (expanded_) { @@ -752,14 +714,14 @@ bool NotificationViewMD::OnMousePressed(const ui::MouseEvent& event) { gfx::Point point_in_child = event.location(); ConvertPointToTarget(this, actions_row_, &point_in_child); if (actions_row_->HitTestPoint(point_in_child)) - return true; + return; } // Ignore clicks of outside region when inline settings is shown. if (settings_row_ && settings_row_->visible()) - return true; + return; - return MessageView::OnMousePressed(event); + MessageView::OnMouseReleased(event); } void NotificationViewMD::OnMouseEvent(ui::MouseEvent* event) { @@ -776,6 +738,14 @@ void NotificationViewMD::OnMouseEvent(ui::MouseEvent* event) { View::OnMouseEvent(event); } +void NotificationViewMD::OnGestureEvent(ui::GestureEvent* event) { + if (event->type() == ui::ET_GESTURE_LONG_TAP) { + ToggleInlineSettings(*event); + return; + } + MessageView::OnGestureEvent(event); +} + void NotificationViewMD::UpdateWithNotification( const Notification& notification) { MessageView::UpdateWithNotification(notification); @@ -848,14 +818,6 @@ void NotificationViewMD::OnNotificationInputSubmit(size_t index, index, text); } -bool NotificationViewMD::IsCloseButtonFocused() const { - return control_buttons_view_->IsCloseButtonFocused(); -} - -void NotificationViewMD::RequestFocusOnCloseButton() { - control_buttons_view_->RequestFocusOnCloseButton(); -} - void NotificationViewMD::CreateOrUpdateContextTitleView( const Notification& notification) { header_row_->SetAccentColor(notification.accent_color() == SK_ColorTRANSPARENT @@ -1274,7 +1236,8 @@ void NotificationViewMD::UpdateViewForExpandedState(bool expanded) { } void NotificationViewMD::ToggleInlineSettings(const ui::Event& event) { - DCHECK(settings_row_); + if (!settings_row_) + return; bool inline_settings_visible = !settings_row_->visible(); @@ -1303,9 +1266,10 @@ void NotificationViewMD::ToggleInlineSettings(const ui::Event& event) { // among NotificationView and ArcNotificationView. void NotificationViewMD::UpdateControlButtonsVisibility() { const bool target_visibility = - AlwaysShowControlButtons() || IsMouseHovered() || - control_buttons_view_->IsCloseButtonFocused() || - control_buttons_view_->IsSettingsButtonFocused(); + (AlwaysShowControlButtons() || IsMouseHovered() || + control_buttons_view_->IsCloseButtonFocused() || + control_buttons_view_->IsSettingsButtonFocused()) && + !GetPinned(); control_buttons_view_->SetVisible(target_visibility); } @@ -1344,13 +1308,17 @@ void NotificationViewMD::OnSettingsButtonPressed(const ui::Event& event) { MessageView::OnSettingsButtonPressed(event); } +const char* NotificationViewMD::GetMessageViewSubClassName() const { + return kMessageViewSubClassName; +} + void NotificationViewMD::Activate() { GetWidget()->widget_delegate()->set_can_activate(true); GetWidget()->Activate(); } void NotificationViewMD::AddBackgroundAnimation(const ui::Event& event) { - SetInkDropMode(InkDropMode::ON); + SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER); // In case the animation is triggered from keyboard operation. if (!event.IsLocatedEvent()) { AnimateInkDrop(views::InkDropState::ACTION_PENDING, nullptr); diff --git a/chromium/ui/message_center/views/notification_view_md.h b/chromium/ui/message_center/views/notification_view_md.h index d9ed79d902b..d4404224f49 100644 --- a/chromium/ui/message_center/views/notification_view_md.h +++ b/chromium/ui/message_center/views/notification_view_md.h @@ -10,6 +10,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/optional.h" +#include "base/time/time.h" #include "ui/message_center/message_center_export.h" #include "ui/message_center/views/message_view.h" #include "ui/views/animation/ink_drop_observer.h" @@ -18,7 +19,6 @@ #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" -#include "ui/views/view_targeter_delegate.h" namespace views { class ImageButton; @@ -218,9 +218,10 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD : public MessageView, public views::InkDropObserver, public NotificationInputDelegate, - public views::ButtonListener, - public views::ViewTargeterDelegate { + public views::ButtonListener { public: + static const char kMessageViewSubClassName[]; + explicit NotificationViewMD(const Notification& notification); ~NotificationViewMD() override; @@ -233,9 +234,11 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD void Layout() override; void OnFocus() override; void ScrollRectToVisible(const gfx::Rect& rect) override; - gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; bool OnMousePressed(const ui::MouseEvent& event) override; + bool OnMouseDragged(const ui::MouseEvent& event) override; + void OnMouseReleased(const ui::MouseEvent& event) override; void OnMouseEvent(ui::MouseEvent* event) override; + void OnGestureEvent(ui::GestureEvent* event) override; // Overridden from views::InkDropHostView: void AddInkDropLayer(ui::Layer* ink_drop_layer) override; @@ -246,16 +249,14 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD // Overridden from MessageView: void UpdateWithNotification(const Notification& notification) override; void ButtonPressed(views::Button* sender, const ui::Event& event) override; - bool IsCloseButtonFocused() const override; - void RequestFocusOnCloseButton() override; void UpdateControlButtonsVisibility() override; NotificationControlButtonsView* GetControlButtonsView() const override; bool IsExpanded() const override; void SetExpanded(bool expanded) override; bool IsManuallyExpandedOrCollapsed() const override; void SetManuallyExpandedOrCollapsed(bool value) override; - void OnSettingsButtonPressed(const ui::Event& event) override; + const char* GetMessageViewSubClassName() const final; // views::InkDropObserver: void InkDropAnimationStarted() override; @@ -265,9 +266,6 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD void OnNotificationInputSubmit(size_t index, const base::string16& text) override; - // views::ViewTargeterDelegate: - views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override; - private: FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, CreateOrUpdateTest); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestIconSizing); @@ -357,6 +355,8 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD std::unique_ptr<ui::EventHandler> click_activator_; + base::TimeTicks last_mouse_pressed_timestamp_; + DISALLOW_COPY_AND_ASSIGN(NotificationViewMD); }; 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 f8574e2975f..ae5a86196de 100644 --- a/chromium/ui/message_center/views/notification_view_md_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_md_unittest.cc @@ -37,26 +37,23 @@ class NotificationTestDelegate : public NotificationDelegate { public: NotificationTestDelegate() = default; - void ButtonClick(int button_index) override { - if (!expecting_button_click_) - ADD_FAILURE() << "ClickOnNotificationButton should not be invoked."; - clicked_button_index_ = button_index; - } - - void ButtonClickWithReply(int button_index, - const base::string16& reply) override { - if (!expecting_reply_submission_) { - ADD_FAILURE() - << "ClickOnNotificationButtonWithReply should not be invoked."; - } - - clicked_button_index_ = button_index; - submitted_reply_string_ = reply; + void Click(const base::Optional<int>& button_index, + const base::Optional<base::string16>& reply) override { + if (!button_index) + return; + + if (!reply && !expecting_button_click_) + ADD_FAILURE() << "Click should not be invoked with a button index."; + if (reply && !expecting_reply_submission_) + ADD_FAILURE() << "Click should not be invoked with a reply."; + + clicked_button_index_ = *button_index; + submitted_reply_string_ = reply.value_or(base::string16()); } void Reset() { clicked_button_index_ = -1; - submitted_reply_string_ = base::EmptyString16(); + submitted_reply_string_.clear(); } void DisableNotification() override { disable_notification_called_ = true; } @@ -791,7 +788,7 @@ TEST_F(NotificationViewMDTest, InlineSettings) { EXPECT_FALSE(notification_view()->settings_row_->visible()); gfx::Point settings_cursor_location(1, 1); views::View::ConvertPointToScreen( - notification_view()->control_buttons_view_.get()->settings_button(), + notification_view()->control_buttons_view_->settings_button(), &settings_cursor_location); ui::test::EventGenerator generator(widget()->GetNativeWindow()); generator.MoveMouseTo(settings_cursor_location); diff --git a/chromium/ui/message_center/views/slide_out_controller.cc b/chromium/ui/message_center/views/slide_out_controller.cc index efcf8e027d1..6a863de1b87 100644 --- a/chromium/ui/message_center/views/slide_out_controller.cc +++ b/chromium/ui/message_center/views/slide_out_controller.cc @@ -8,7 +8,7 @@ #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/transform.h" -namespace views { +namespace message_center { SlideOutController::SlideOutController(ui::EventTarget* target, Delegate* delegate) @@ -108,4 +108,4 @@ void SlideOutController::OnImplicitAnimationsCompleted() { delegate_->OnSlideOut(); } -} // namespace views +} // namespace message_center diff --git a/chromium/ui/message_center/views/slide_out_controller.h b/chromium/ui/message_center/views/slide_out_controller.h index 5b01df2d98e..c3f134eb270 100644 --- a/chromium/ui/message_center/views/slide_out_controller.h +++ b/chromium/ui/message_center/views/slide_out_controller.h @@ -8,15 +8,16 @@ #include "base/macros.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/events/scoped_target_handler.h" +#include "ui/message_center/message_center_export.h" #include "ui/views/view.h" -#include "ui/views/views_export.h" -namespace views { +namespace message_center { // This class contains logic to control sliding out of a layer in response to // swipes, i.e. gesture scroll events. -class SlideOutController : public ui::EventHandler, - public ui::ImplicitAnimationObserver { +class MESSAGE_CENTER_EXPORT SlideOutController + : public ui::EventHandler, + public ui::ImplicitAnimationObserver { public: class Delegate { public: @@ -59,6 +60,6 @@ class SlideOutController : public ui::EventHandler, DISALLOW_COPY_AND_ASSIGN(SlideOutController); }; -} // namespace views +} // namespace message_center #endif // UI_MESSAGE_CENTER_VIEWS_SLIDE_OUT_CONTROLLER_H_ diff --git a/chromium/ui/message_center/views/toast_contents_view.cc b/chromium/ui/message_center/views/toast_contents_view.cc index 285ca0b30a2..23c84cf6d59 100644 --- a/chromium/ui/message_center/views/toast_contents_view.cc +++ b/chromium/ui/message_center/views/toast_contents_view.cc @@ -11,6 +11,7 @@ #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "build/build_config.h" +#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/aura/window.h" #include "ui/aura/window_targeter.h" @@ -84,17 +85,11 @@ ToastContentsView::~ToastContentsView() { void ToastContentsView::SetContents(MessageView* view, bool a11y_feedback_for_updates) { message_view_ = view; - bool already_has_contents = child_count() > 0; RemoveAllChildViews(true); AddChildView(view); UpdatePreferredSize(); - - // If it has the contents already, this invocation means an update of the - // popup toast, and the new contents should be read through a11y feature. - // The notification type should be ALERT, otherwise the accessibility message - // won't be read for this view which returns ROLE_WINDOW. - if (already_has_contents && a11y_feedback_for_updates) - NotifyAccessibilityEvent(ax::mojom::Event::kAlert, false); + if (a11y_feedback_for_updates) + NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true); } void ToastContentsView::UpdateContents(const Notification& notification, @@ -104,7 +99,7 @@ void ToastContentsView::UpdateContents(const Notification& notification, message_view->UpdateWithNotification(notification); UpdatePreferredSize(); if (a11y_feedback_for_updates) - NotifyAccessibilityEvent(ax::mojom::Event::kAlert, false); + NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true); } void ToastContentsView::RevealWithAnimation(gfx::Point origin) { @@ -316,7 +311,7 @@ void ToastContentsView::UpdatePreferredSize() { void ToastContentsView::GetAccessibleNodeData(ui::AXNodeData* node_data) { if (child_count() > 0) child_at(0)->GetAccessibleNodeData(node_data); - node_data->role = ax::mojom::Role::kWindow; + node_data->role = ax::mojom::Role::kAlertDialog; } const char* ToastContentsView::GetClassName() const { diff --git a/chromium/ui/native_theme/common_theme.cc b/chromium/ui/native_theme/common_theme.cc index 259f4f7cefd..33d7e8a5cec 100644 --- a/chromium/ui/native_theme/common_theme.cc +++ b/chromium/ui/native_theme/common_theme.cc @@ -5,7 +5,6 @@ #include "ui/native_theme/common_theme.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" @@ -97,6 +96,8 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, 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: @@ -200,9 +201,8 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, return kEnabledMenuItemForegroundColor; case NativeTheme::kColorId_DisabledMenuItemForegroundColor: return kDisabledTextColor; - case NativeTheme::kColorId_MenuItemSubtitleColor: - return base_theme->GetSystemColor( - NativeTheme::kColorId_DisabledMenuItemForegroundColor); + case NativeTheme::kColorId_MenuItemMinorTextColor: + return kMenuItemMinorTextColor; // Label case NativeTheme::kColorId_LabelEnabledColor: diff --git a/chromium/ui/native_theme/native_theme.h b/chromium/ui/native_theme/native_theme.h index 01272180372..d926149c781 100644 --- a/chromium/ui/native_theme/native_theme.h +++ b/chromium/ui/native_theme/native_theme.h @@ -315,7 +315,7 @@ class NATIVE_THEME_EXPORT NativeTheme { kColorId_DisabledMenuItemForegroundColor, kColorId_SelectedMenuItemForegroundColor, kColorId_FocusedMenuItemBackgroundColor, - kColorId_MenuItemSubtitleColor, + kColorId_MenuItemMinorTextColor, kColorId_MenuSeparatorColor, kColorId_MenuBackgroundColor, kColorId_MenuBorderColor, diff --git a/chromium/ui/native_theme/native_theme_dark_aura.cc b/chromium/ui/native_theme/native_theme_dark_aura.cc index 079b88cec6c..655b4879669 100644 --- a/chromium/ui/native_theme/native_theme_dark_aura.cc +++ b/chromium/ui/native_theme/native_theme_dark_aura.cc @@ -112,7 +112,7 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { case kColorId_DisabledMenuItemForegroundColor: case kColorId_SelectedMenuItemForegroundColor: case kColorId_FocusedMenuItemBackgroundColor: - case kColorId_MenuItemSubtitleColor: + case kColorId_MenuItemMinorTextColor: case kColorId_MenuSeparatorColor: case kColorId_MenuBackgroundColor: case kColorId_MenuBorderColor: diff --git a/chromium/ui/native_theme/native_theme_mac.mm b/chromium/ui/native_theme/native_theme_mac.mm index 758320fe283..84a2cd18a67 100644 --- a/chromium/ui/native_theme/native_theme_mac.mm +++ b/chromium/ui/native_theme/native_theme_mac.mm @@ -24,20 +24,10 @@ namespace { -// Values calculated by reading pixels and solving simultaneous equations -// derived from "A over B" alpha compositing. Steps: Sample the semi-transparent -// pixel over two backgrounds; P1, P2 over backgrounds B1, B2. Use the color -// value between 0.0 and 1.0 (i.e. divide by 255.0). Then, -// alpha = (P2 - P1 + B1 - B2) / (B1 - B2) -// color = (P1 - B1 + alpha * B1) / alpha. -const SkColor kMenuPopupBackgroundColor = SkColorSetARGB(245, 255, 255, 255); -const SkColor kMenuSeparatorColor = SkColorSetARGB(255, 217, 217, 217); +const SkColor kMenuPopupBackgroundColor = SK_ColorWHITE; +const SkColor kMenuSeparatorColor = SkColorSetA(SK_ColorBLACK, 38); const SkColor kMenuBorderColor = SkColorSetARGB(60, 0, 0, 0); -const SkColor kMenuPopupBackgroundColorMavericks = - SkColorSetARGB(255, 255, 255, 255); -const SkColor kMenuSeparatorColorMavericks = SkColorSetARGB(243, 228, 228, 228); - // Hardcoded color used for some existing dialogs in Chrome's Cocoa UI. const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251); @@ -139,14 +129,22 @@ SkColor NativeThemeMac::GetSystemColor(ColorId color_id) const { case kColorId_DisabledMenuItemForegroundColor: return NSSystemColorToSkColor([NSColor disabledControlTextColor]); case kColorId_SelectedMenuItemForegroundColor: - return NSSystemColorToSkColor([NSColor selectedMenuItemTextColor]); + return SK_ColorBLACK; case kColorId_FocusedMenuItemBackgroundColor: - return NSSystemColorToSkColor([NSColor selectedMenuItemColor]); + // It's necessary to use a different alpha for Aqua mode vs Graphite mode, + // because the black used as the graphite base shows up well even at low + // alphas, but the blue used for Aqua needs a bit more alpha to show up + // properly. At the same alpha as the graphite uses, it's difficult to + // pick out clearly and looks somewhat like faint lilac instead of blue. + return ([NSColor currentControlTint] == NSGraphiteControlTint) + ? SkColorSetA(SK_ColorBLACK, 21) + : SkColorSetA( + NSSystemColorToSkColor([NSColor selectedMenuItemColor]), + 45); case kColorId_MenuBackgroundColor: return kMenuPopupBackgroundColor; case kColorId_MenuSeparatorColor: - return base::mac::IsOS10_9() ? kMenuSeparatorColorMavericks - : kMenuSeparatorColor; + return kMenuSeparatorColor; case kColorId_MenuBorderColor: return kMenuBorderColor; @@ -255,10 +253,7 @@ void NativeThemeMac::PaintMenuPopupBackground( const MenuBackgroundExtraParams& menu_background) const { cc::PaintFlags flags; flags.setAntiAlias(true); - if (base::mac::IsOS10_9()) - flags.setColor(kMenuPopupBackgroundColorMavericks); - else - flags.setColor(kMenuPopupBackgroundColor); + flags.setColor(kMenuPopupBackgroundColor); const SkScalar radius = SkIntToScalar(menu_background.corner_radius); SkRect rect = gfx::RectToSkRect(gfx::Rect(size)); canvas->drawRoundRect(rect, radius, radius, flags); diff --git a/chromium/ui/ozone/common/egl_util.cc b/chromium/ui/ozone/common/egl_util.cc index 9a40d1db001..86636c3f94b 100644 --- a/chromium/ui/ozone/common/egl_util.cc +++ b/chromium/ui/ozone/common/egl_util.cc @@ -14,12 +14,16 @@ namespace ui { namespace { -const char kDefaultEglSoname[] = "libEGL.so.1"; -const char kDefaultGlesSoname[] = "libGLESv2.so.2"; +const base::FilePath::CharType kDefaultEglSoname[] = + FILE_PATH_LITERAL("libEGL.so.1"); +const base::FilePath::CharType kDefaultGlesSoname[] = + FILE_PATH_LITERAL("libGLESv2.so.2"); #if BUILDFLAG(ENABLE_SWIFTSHADER) -const char kGLESv2SwiftShaderLibraryName[] = "libGLESv2.so"; -const char kEGLSwiftShaderLibraryName[] = "libEGL.so"; +const base::FilePath::CharType kGLESv2SwiftShaderLibraryName[] = + FILE_PATH_LITERAL("libGLESv2.so"); +const base::FilePath::CharType kEGLSwiftShaderLibraryName[] = + FILE_PATH_LITERAL("libEGL.so"); #endif bool LoadEGLGLES2Bindings(const base::FilePath& egl_library_path, @@ -69,7 +73,7 @@ bool LoadDefaultEGLGLES2Bindings(gl::GLImplementation implementation) { base::FilePath module_path; if (!PathService::Get(base::DIR_MODULE, &module_path)) return false; - module_path = module_path.Append("swiftshader/"); + module_path = module_path.Append(FILE_PATH_LITERAL("swiftshader/")); glesv2_path = module_path.Append(kGLESv2SwiftShaderLibraryName); egl_path = module_path.Append(kEGLSwiftShaderLibraryName); diff --git a/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h b/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h index 0c5ca179b9a..06065107640 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h @@ -53,7 +53,8 @@ struct DisplaySnapshot_Params { DisplayMode_Params current_mode; bool has_native_mode = false; DisplayMode_Params native_mode; - int64_t product_id = 0; + int64_t product_code = 0; + int32_t year_of_manufacture = display::kInvalidYearOfManufacture; gfx::Size maximum_cursor_size; }; diff --git a/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h b/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h index 6db409c8df7..d472f0830f1 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h @@ -4,6 +4,7 @@ // Multiply-included message file, hence no include guard here, but see below // for a much smaller-than-usual include guard section. +// no-include-guard-because-multiply-included #include <stdint.h> @@ -62,7 +63,8 @@ IPC_STRUCT_TRAITS_BEGIN(ui::DisplaySnapshot_Params) IPC_STRUCT_TRAITS_MEMBER(current_mode) IPC_STRUCT_TRAITS_MEMBER(has_native_mode) IPC_STRUCT_TRAITS_MEMBER(native_mode) - IPC_STRUCT_TRAITS_MEMBER(product_id) + IPC_STRUCT_TRAITS_MEMBER(product_code) + IPC_STRUCT_TRAITS_MEMBER(year_of_manufacture) IPC_STRUCT_TRAITS_MEMBER(maximum_cursor_size) IPC_STRUCT_TRAITS_END() diff --git a/chromium/ui/ozone/demo/BUILD.gn b/chromium/ui/ozone/demo/BUILD.gn index 8a01fb9b10e..71447419f9f 100644 --- a/chromium/ui/ozone/demo/BUILD.gn +++ b/chromium/ui/ozone/demo/BUILD.gn @@ -10,16 +10,22 @@ group("demo") { executable("ozone_demo") { sources = [ + "demo_window.cc", + "demo_window.h", "ozone_demo.cc", "renderer.h", "renderer_base.cc", "renderer_base.h", + "renderer_factory.cc", + "renderer_factory.h", "skia_renderer.cc", "skia_renderer.h", "software_renderer.cc", "software_renderer.h", "surfaceless_skia_renderer.cc", "surfaceless_skia_renderer.h", + "window_manager.cc", + "window_manager.h", ] deps = [ diff --git a/chromium/ui/ozone/demo/demo_window.cc b/chromium/ui/ozone/demo/demo_window.cc new file mode 100644 index 00000000000..f641537e0f3 --- /dev/null +++ b/chromium/ui/ozone/demo/demo_window.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/ozone/demo/demo_window.h" + +#include "base/threading/thread_task_runner_handle.h" +#include "ui/events/event.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/ozone/demo/renderer.h" +#include "ui/ozone/demo/renderer_factory.h" +#include "ui/ozone/demo/window_manager.h" +#include "ui/ozone/public/ozone_platform.h" +#include "ui/platform_window/platform_window.h" + +namespace ui { + +DemoWindow::DemoWindow(WindowManager* window_manager, + RendererFactory* renderer_factory, + const gfx::Rect& bounds) + : window_manager_(window_manager), + renderer_factory_(renderer_factory), + weak_ptr_factory_(this) { + platform_window_ = + OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds); + platform_window_->Show(); +} + +DemoWindow::~DemoWindow() {} + +gfx::AcceleratedWidget DemoWindow::GetAcceleratedWidget() { + // TODO(spang): We should start rendering asynchronously. + DCHECK_NE(widget_, gfx::kNullAcceleratedWidget) + << "Widget not available synchronously"; + return widget_; +} + +gfx::Size DemoWindow::GetSize() { + return platform_window_->GetBounds().size(); +} + +void DemoWindow::Start() { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&DemoWindow::StartOnGpu, weak_ptr_factory_.GetWeakPtr())); +} + +void DemoWindow::Quit() { + window_manager_->Quit(); +} + +void DemoWindow::OnBoundsChanged(const gfx::Rect& new_bounds) {} + +void DemoWindow::OnDamageRect(const gfx::Rect& damaged_region) {} + +void DemoWindow::DispatchEvent(Event* event) { + if (event->IsKeyEvent() && event->AsKeyEvent()->code() == DomCode::US_Q) + Quit(); +} + +void DemoWindow::OnCloseRequest() { + Quit(); +} + +void DemoWindow::OnClosed() {} + +void DemoWindow::OnWindowStateChanged(PlatformWindowState new_state) {} + +void DemoWindow::OnLostCapture() {} + +void DemoWindow::OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget, + float device_pixel_ratio) { + DCHECK_NE(widget, gfx::kNullAcceleratedWidget); + widget_ = widget; +} + +void DemoWindow::OnAcceleratedWidgetDestroyed() { + NOTREACHED(); +} + +void DemoWindow::OnActivationChanged(bool active) {} + +void DemoWindow::StartOnGpu() { + renderer_ = + renderer_factory_->CreateRenderer(GetAcceleratedWidget(), GetSize()); + if (!renderer_->Initialize()) + LOG(ERROR) << "Failed to initialize renderer."; +} + +} // namespace ui diff --git a/chromium/ui/ozone/demo/demo_window.h b/chromium/ui/ozone/demo/demo_window.h new file mode 100644 index 00000000000..6a41df1252b --- /dev/null +++ b/chromium/ui/ozone/demo/demo_window.h @@ -0,0 +1,69 @@ +// 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_DEMO_DEMO_WINDOW_H_ +#define UI_OZONE_DEMO_DEMO_WINDOW_H_ + +#include "base/memory/weak_ptr.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" +#include "ui/platform_window/platform_window_delegate.h" + +namespace ui { + +class Event; +class PlatformWindow; +class Renderer; +class RendererFactory; +class WindowManager; + +class DemoWindow : public PlatformWindowDelegate { + public: + DemoWindow(WindowManager* window_manager, + RendererFactory* renderer_factory, + const gfx::Rect& bounds); + ~DemoWindow() override; + + gfx::AcceleratedWidget GetAcceleratedWidget(); + + gfx::Size GetSize(); + + void Start(); + void Quit(); + + // PlatformWindowDelegate: + void OnBoundsChanged(const gfx::Rect& new_bounds) override; + void OnDamageRect(const gfx::Rect& damaged_region) override; + void DispatchEvent(Event* event) override; + void OnCloseRequest() override; + void OnClosed() override; + void OnWindowStateChanged(PlatformWindowState new_state) override; + void OnLostCapture() override; + void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget, + float device_pixel_ratio) override; + void OnAcceleratedWidgetDestroyed() override; + void OnActivationChanged(bool active) override; + + private: + // Since we pretend to have a GPU process, we should also pretend to + // initialize the GPU resources via a posted task. + void StartOnGpu(); + + WindowManager* window_manager_; // Not owned. + RendererFactory* renderer_factory_; // Not owned. + + std::unique_ptr<Renderer> renderer_; + + // Window-related state. + std::unique_ptr<PlatformWindow> platform_window_; + gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget; + + base::WeakPtrFactory<DemoWindow> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DemoWindow); +}; + +} // namespace ui + +#endif // UI_OZONE_DEMO_DEMO_WINDOW_H_ diff --git a/chromium/ui/ozone/demo/ozone_demo.cc b/chromium/ui/ozone/demo/ozone_demo.cc index fc989906f2e..6806dbe84fe 100644 --- a/chromium/ui/ozone/demo/ozone_demo.cc +++ b/chromium/ui/ozone/demo/ozone_demo.cc @@ -2,334 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <utility> +#include <iostream> #include "base/at_exit.h" #include "base/command_line.h" #include "base/debug/stack_trace.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/task_scheduler/task_scheduler.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "components/tracing/common/trace_to_console.h" #include "components/tracing/common/tracing_switches.h" -#include "ui/display/types/display_snapshot.h" -#include "ui/display/types/native_display_delegate.h" -#include "ui/display/types/native_display_observer.h" -#include "ui/events/event.h" -#include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/ozone/layout/keyboard_layout_engine.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gl/gl_surface.h" -#include "ui/gl/init/gl_factory.h" -#include "ui/ozone/demo/skia_renderer.h" -#include "ui/ozone/demo/software_renderer.h" -#include "ui/ozone/demo/surfaceless_skia_renderer.h" -#include "ui/ozone/public/ozone_gpu_test_helper.h" +#include "ui/ozone/demo/window_manager.h" #include "ui/ozone/public/ozone_platform.h" -#include "ui/ozone/public/ozone_switches.h" -#include "ui/platform_window/platform_window.h" -#include "ui/platform_window/platform_window_delegate.h" -const int kTestWindowWidth = 800; -const int kTestWindowHeight = 600; - -const char kDisableGpu[] = "disable-gpu"; -const char kDisableSurfaceless[] = "disable-surfaceless"; - -const char kWindowSize[] = "window-size"; - -class DemoWindow; - -scoped_refptr<gl::GLSurface> CreateGLSurface(gfx::AcceleratedWidget widget) { - scoped_refptr<gl::GLSurface> surface; - if (!base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableSurfaceless)) - surface = gl::init::CreateSurfacelessViewGLSurface(widget); - if (!surface) - surface = gl::init::CreateViewGLSurface(widget); - return surface; -} - -class RendererFactory { - public: - enum RendererType { - SKIA, - SOFTWARE, - }; - - RendererFactory(); - ~RendererFactory(); - - bool Initialize(); - std::unique_ptr<ui::Renderer> CreateRenderer(gfx::AcceleratedWidget widget, - const gfx::Size& size); - - private: - RendererType type_ = SOFTWARE; - - // Helper for applications that do GL on main thread. - ui::OzoneGpuTestHelper gpu_helper_; - - DISALLOW_COPY_AND_ASSIGN(RendererFactory); -}; - -class WindowManager : public display::NativeDisplayObserver { - public: - explicit WindowManager(const base::Closure& quit_closure); - ~WindowManager() override; - - void Quit(); - - void AddWindow(DemoWindow* window); - void RemoveWindow(DemoWindow* window); - - private: - void OnDisplaysAquired( - const std::vector<display::DisplaySnapshot*>& displays); - void OnDisplayConfigured(const gfx::Rect& bounds, bool success); - - // display::NativeDisplayDelegate: - void OnConfigurationChanged() override; - void OnDisplaySnapshotsInvalidated() override; - - std::unique_ptr<display::NativeDisplayDelegate> delegate_; - base::Closure quit_closure_; - RendererFactory renderer_factory_; - std::vector<std::unique_ptr<DemoWindow>> windows_; - - // Flags used to keep track of the current state of display configuration. - // - // True if configuring the displays. In this case a new display configuration - // isn't started. - bool is_configuring_ = false; - - // If |is_configuring_| is true and another display configuration event - // happens, the event is deferred. This is set to true and a display - // configuration will be scheduled after the current one finishes. - bool should_configure_ = false; - - DISALLOW_COPY_AND_ASSIGN(WindowManager); -}; - -class DemoWindow : public ui::PlatformWindowDelegate { - public: - DemoWindow(WindowManager* window_manager, - RendererFactory* renderer_factory, - const gfx::Rect& bounds) - : window_manager_(window_manager), - renderer_factory_(renderer_factory), - weak_ptr_factory_(this) { - platform_window_ = - ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds); - platform_window_->Show(); - } - ~DemoWindow() override {} - - gfx::AcceleratedWidget GetAcceleratedWidget() { - // TODO(spang): We should start rendering asynchronously. - DCHECK_NE(widget_, gfx::kNullAcceleratedWidget) - << "Widget not available synchronously"; - return widget_; - } - - gfx::Size GetSize() { return platform_window_->GetBounds().size(); } - - void Start() { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&DemoWindow::StartOnGpu, weak_ptr_factory_.GetWeakPtr())); - } - - void Quit() { - window_manager_->Quit(); - } - - // PlatformWindowDelegate: - void OnBoundsChanged(const gfx::Rect& new_bounds) override {} - void OnDamageRect(const gfx::Rect& damaged_region) override {} - void DispatchEvent(ui::Event* event) override { - if (event->IsKeyEvent() && event->AsKeyEvent()->code() == ui::DomCode::US_Q) - Quit(); - } - void OnCloseRequest() override { Quit(); } - void OnClosed() override {} - void OnWindowStateChanged(ui::PlatformWindowState new_state) override {} - void OnLostCapture() override {} - void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget, - float device_pixel_ratio) override { - DCHECK_NE(widget, gfx::kNullAcceleratedWidget); - widget_ = widget; - } - void OnAcceleratedWidgetDestroyed() override { - NOTREACHED(); - } - void OnActivationChanged(bool active) override {} - - private: - // Since we pretend to have a GPU process, we should also pretend to - // initialize the GPU resources via a posted task. - void StartOnGpu() { - renderer_ = - renderer_factory_->CreateRenderer(GetAcceleratedWidget(), GetSize()); - if (!renderer_->Initialize()) - LOG(ERROR) << "Failed to initialize renderer."; - } - - WindowManager* window_manager_; // Not owned. - RendererFactory* renderer_factory_; // Not owned. - - std::unique_ptr<ui::Renderer> renderer_; - - // Window-related state. - std::unique_ptr<ui::PlatformWindow> platform_window_; - gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget; - - base::WeakPtrFactory<DemoWindow> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(DemoWindow); -}; - -/////////////////////////////////////////////////////////////////////////////// -// RendererFactory implementation: - -RendererFactory::RendererFactory() { -} - -RendererFactory::~RendererFactory() { -} - -bool RendererFactory::Initialize() { - ui::OzonePlatform::InitParams params; - params.single_process = true; - ui::OzonePlatform::InitializeForGPU(params); - ui::OzonePlatform::GetInstance()->AfterSandboxEntry(); - - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (!command_line->HasSwitch(kDisableGpu) && gl::init::InitializeGLOneOff() && - gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get())) { - type_ = SKIA; - } else { - type_ = SOFTWARE; - } - - return true; -} - -std::unique_ptr<ui::Renderer> RendererFactory::CreateRenderer( - gfx::AcceleratedWidget widget, - const gfx::Size& size) { - switch (type_) { - case SKIA: { - scoped_refptr<gl::GLSurface> surface = CreateGLSurface(widget); - if (!surface) - LOG(FATAL) << "Failed to create GL surface"; - if (surface->IsSurfaceless()) { - return std::make_unique<ui::SurfacelessSkiaRenderer>(widget, surface, - size); - } - return std::make_unique<ui::SkiaRenderer>(widget, surface, size); - } - case SOFTWARE: - return std::make_unique<ui::SoftwareRenderer>(widget, size); - } - - return nullptr; -} - -/////////////////////////////////////////////////////////////////////////////// -// WindowManager implementation: - -WindowManager::WindowManager(const base::Closure& quit_closure) - : delegate_( - ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate()), - quit_closure_(quit_closure) { - if (!renderer_factory_.Initialize()) - LOG(FATAL) << "Failed to initialize renderer factory"; - - if (delegate_) { - delegate_->AddObserver(this); - delegate_->Initialize(); - OnConfigurationChanged(); - } else { - LOG(WARNING) << "No display delegate; falling back to test window"; - int width = kTestWindowWidth; - int height = kTestWindowHeight; - sscanf(base::CommandLine::ForCurrentProcess() - ->GetSwitchValueASCII(kWindowSize) - .c_str(), - "%dx%d", &width, &height); - - DemoWindow* window = new DemoWindow(this, &renderer_factory_, - gfx::Rect(gfx::Size(width, height))); - window->Start(); - } -} - -WindowManager::~WindowManager() { - if (delegate_) - delegate_->RemoveObserver(this); -} - -void WindowManager::Quit() { - quit_closure_.Run(); -} - -void WindowManager::OnConfigurationChanged() { - if (is_configuring_) { - should_configure_ = true; - return; - } - - is_configuring_ = true; - delegate_->GetDisplays( - base::Bind(&WindowManager::OnDisplaysAquired, base::Unretained(this))); -} - -void WindowManager::OnDisplaySnapshotsInvalidated() {} - -void WindowManager::OnDisplaysAquired( - const std::vector<display::DisplaySnapshot*>& displays) { - windows_.clear(); - - gfx::Point origin; - for (auto* display : displays) { - if (!display->native_mode()) { - LOG(ERROR) << "Display " << display->display_id() - << " doesn't have a native mode"; - continue; - } - - delegate_->Configure( - *display, display->native_mode(), origin, - base::Bind(&WindowManager::OnDisplayConfigured, base::Unretained(this), - gfx::Rect(origin, display->native_mode()->size()))); - origin.Offset(display->native_mode()->size().width(), 0); - } - is_configuring_ = false; - - if (should_configure_) { - should_configure_ = false; - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&WindowManager::OnConfigurationChanged, - base::Unretained(this))); - } -} - -void WindowManager::OnDisplayConfigured(const gfx::Rect& bounds, bool success) { - if (success) { - std::unique_ptr<DemoWindow> window( - new DemoWindow(this, &renderer_factory_, bounds)); - window->Start(); - windows_.push_back(std::move(window)); - } else { - LOG(ERROR) << "Failed to configure display at " << bounds.ToString(); - } -} +const char kHelp[] = "help"; int main(int argc, char** argv) { base::CommandLine::Init(argc, argv); @@ -341,6 +30,25 @@ int main(int argc, char** argv) { logging::LoggingSettings settings; logging::InitLogging(settings); + if (base::CommandLine::ForCurrentProcess()->HasSwitch(kHelp)) { + std::cout << + "Usage:\n\n" + " --enable-drm-atomic Use the atomic KMS API\n" + " --disable-gpu Force software rendering\n" + " --disable-surfaceless Don't use surfaceless EGL\n" + " --window-size=WIDTHxHEIGHT Specify window size\n" + " --use-ddl Use SkDeferredDisplayList\n" + " --partial-primary-plane " + "Use smaller than fullscreen primary plane\n" + " --enable-overlay Use an overlay plane\n" + " --disable-primary-plane Don't use the primary plane\n"; + + // TODO(hoegsberg): We should add a little more help text about how these + // options interact and depend on each other. + + exit(EXIT_SUCCESS); + } + // Initialize tracing. if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kTraceToConsole)) { @@ -363,7 +71,7 @@ int main(int argc, char** argv) { base::RunLoop run_loop; - WindowManager window_manager(run_loop.QuitClosure()); + ui::WindowManager window_manager(run_loop.QuitClosure()); run_loop.Run(); diff --git a/chromium/ui/ozone/demo/renderer_factory.cc b/chromium/ui/ozone/demo/renderer_factory.cc new file mode 100644 index 00000000000..10a01103786 --- /dev/null +++ b/chromium/ui/ozone/demo/renderer_factory.cc @@ -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. + +#include "ui/ozone/demo/renderer_factory.h" + +#include <memory> + +#include "base/command_line.h" +#include "ui/gl/gl_surface.h" +#include "ui/gl/init/gl_factory.h" +#include "ui/ozone/demo/skia_renderer.h" +#include "ui/ozone/demo/software_renderer.h" +#include "ui/ozone/demo/surfaceless_skia_renderer.h" +#include "ui/ozone/public/ozone_platform.h" + +namespace ui { +namespace { + +const char kDisableSurfaceless[] = "disable-surfaceless"; +const char kDisableGpu[] = "disable-gpu"; + +scoped_refptr<gl::GLSurface> CreateGLSurface(gfx::AcceleratedWidget widget) { + scoped_refptr<gl::GLSurface> surface; + if (!base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableSurfaceless)) + surface = gl::init::CreateSurfacelessViewGLSurface(widget); + if (!surface) + surface = gl::init::CreateViewGLSurface(widget); + return surface; +} + +} // namespace + +RendererFactory::RendererFactory() {} + +RendererFactory::~RendererFactory() {} + +bool RendererFactory::Initialize() { + OzonePlatform::InitParams params; + params.single_process = true; + OzonePlatform::InitializeForGPU(params); + OzonePlatform::GetInstance()->AfterSandboxEntry(); + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(kDisableGpu) && gl::init::InitializeGLOneOff() && + gpu_helper_.Initialize(base::ThreadTaskRunnerHandle::Get())) { + type_ = SKIA; + } else { + type_ = SOFTWARE; + } + + return true; +} + +std::unique_ptr<Renderer> RendererFactory::CreateRenderer( + gfx::AcceleratedWidget widget, + const gfx::Size& size) { + switch (type_) { + case SKIA: { + scoped_refptr<gl::GLSurface> surface = CreateGLSurface(widget); + if (!surface) + LOG(FATAL) << "Failed to create GL surface"; + if (surface->IsSurfaceless()) { + return std::make_unique<SurfacelessSkiaRenderer>(widget, surface, size); + } + return std::make_unique<SkiaRenderer>(widget, surface, size); + } + case SOFTWARE: + return std::make_unique<SoftwareRenderer>(widget, size); + } + + return nullptr; +} + +} // namespace ui diff --git a/chromium/ui/ozone/demo/renderer_factory.h b/chromium/ui/ozone/demo/renderer_factory.h new file mode 100644 index 00000000000..f87aeaea6d3 --- /dev/null +++ b/chromium/ui/ozone/demo/renderer_factory.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_OZONE_DEMO_RENDERER_FACTORY_H_ +#define UI_OZONE_DEMO_RENDERER_FACTORY_H_ + +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/ozone/public/ozone_gpu_test_helper.h" + +namespace ui { + +class Renderer; + +class RendererFactory { + public: + enum RendererType { + SKIA, + SOFTWARE, + }; + + RendererFactory(); + ~RendererFactory(); + + bool Initialize(); + std::unique_ptr<Renderer> CreateRenderer(gfx::AcceleratedWidget widget, + const gfx::Size& size); + + private: + RendererType type_ = SOFTWARE; + + // Helper for applications that do GL on main thread. + OzoneGpuTestHelper gpu_helper_; + + DISALLOW_COPY_AND_ASSIGN(RendererFactory); +}; + +} // namespace ui + +#endif // UI_OZONE_DEMO_RENDERER_FACTORY_H_ diff --git a/chromium/ui/ozone/demo/skia_renderer.cc b/chromium/ui/ozone/demo/skia_renderer.cc index a8d7947ed8f..71154c08925 100644 --- a/chromium/ui/ozone/demo/skia_renderer.cc +++ b/chromium/ui/ozone/demo/skia_renderer.cc @@ -85,13 +85,13 @@ void SkiaRenderer::RenderFrame() { if (!sk_surface_) { GrGLFramebufferInfo framebuffer_info; framebuffer_info.fFBOID = 0; + framebuffer_info.fFormat = GL_RGBA8; GrBackendRenderTarget render_target(size_.width(), size_.height(), 0, 8, - kRGBA_8888_GrPixelConfig, framebuffer_info); sk_surface_ = SkSurface::MakeFromBackendRenderTarget( - gr_context_.get(), render_target, kBottomLeft_GrSurfaceOrigin, nullptr, - &surface_props); + gr_context_.get(), render_target, kBottomLeft_GrSurfaceOrigin, + kRGBA_8888_SkColorType, nullptr, &surface_props); } if (use_ddl_) { diff --git a/chromium/ui/ozone/demo/surfaceless_skia_renderer.cc b/chromium/ui/ozone/demo/surfaceless_skia_renderer.cc index fef66dff8af..c3bce8be2d5 100644 --- a/chromium/ui/ozone/demo/surfaceless_skia_renderer.cc +++ b/chromium/ui/ozone/demo/surfaceless_skia_renderer.cc @@ -114,7 +114,7 @@ bool SurfacelessSkiaRenderer::BufferWrapper::Initialize( ->GetSurfaceFactoryOzone() ->CreateNativePixmap(widget, size, format, gfx::BufferUsage::SCANOUT); scoped_refptr<gl::GLImageNativePixmap> image( - new gl::GLImageNativePixmap(size, GL_RGB)); + new gl::GLImageNativePixmap(size, GL_BGRA_EXT)); if (!image->Initialize(pixmap.get(), format)) { LOG(ERROR) << "Failed to create GLImage"; return false; @@ -130,12 +130,12 @@ bool SurfacelessSkiaRenderer::BufferWrapper::Initialize( GrGLTextureInfo texture_info; texture_info.fTarget = GL_TEXTURE_2D; texture_info.fID = gl_tex_; - texture_info.fFormat = GL_RGBA; + texture_info.fFormat = GL_BGRA8_EXT; GrBackendTexture backend_texture(size_.width(), size_.height(), - kRGBA_8888_GrPixelConfig, texture_info); - sk_surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget( - gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, 0, nullptr, - nullptr); + GrMipMapped::kNo, texture_info); + sk_surface_ = SkSurface::MakeFromBackendTexture( + gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, 0, + kBGRA_8888_SkColorType, nullptr, nullptr); if (!sk_surface_) { LOG(ERROR) << "Failed to create skia surface"; return false; @@ -184,7 +184,7 @@ bool SurfacelessSkiaRenderer::Initialize() { overlay_buffer_[i]->Initialize(gr_context_.get(), gfx::kNullAcceleratedWidget, overlay_size); SkCanvas* sk_canvas = overlay_buffer_[i]->sk_surface()->getCanvas(); - sk_canvas->clear(SkColorSetARGB(255, 255 * i, 255, 0)); + sk_canvas->clear(SkColorSetARGB(96, 255 * i, 255, 0)); } } @@ -246,13 +246,13 @@ void SurfacelessSkiaRenderer::RenderFrame() { CHECK(overlay_list.front().overlay_handled); gl_surface_->ScheduleOverlayPlane( 0, gfx::OVERLAY_TRANSFORM_NONE, buffers_[back_buffer_]->image(), - primary_plane_rect_, gfx::RectF(0, 0, 1, 1)); + primary_plane_rect_, gfx::RectF(0, 0, 1, 1), /* enable_blend */ true); } if (overlay_buffer_[0] && overlay_list.back().overlay_handled) { - gl_surface_->ScheduleOverlayPlane(1, gfx::OVERLAY_TRANSFORM_NONE, - overlay_buffer_[back_buffer_]->image(), - overlay_rect, gfx::RectF(0, 0, 1, 1)); + gl_surface_->ScheduleOverlayPlane( + 1, gfx::OVERLAY_TRANSFORM_NONE, overlay_buffer_[back_buffer_]->image(), + overlay_rect, gfx::RectF(0, 0, 1, 1), /* enable_blend */ true); } back_buffer_ ^= 1; diff --git a/chromium/ui/ozone/demo/window_manager.cc b/chromium/ui/ozone/demo/window_manager.cc new file mode 100644 index 00000000000..9fb74d7e1c5 --- /dev/null +++ b/chromium/ui/ozone/demo/window_manager.cc @@ -0,0 +1,112 @@ +// 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/demo/window_manager.h" + +#include "base/command_line.h" +#include "base/threading/thread_task_runner_handle.h" +#include "ui/display/types/display_snapshot.h" +#include "ui/display/types/native_display_delegate.h" +#include "ui/ozone/demo/demo_window.h" +#include "ui/ozone/public/ozone_platform.h" + +namespace ui { +namespace { + +const int kTestWindowWidth = 800; +const int kTestWindowHeight = 600; + +const char kWindowSize[] = "window-size"; + +} // namespace + +WindowManager::WindowManager(base::OnceClosure quit_closure) + : delegate_( + ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate()), + quit_closure_(std::move(quit_closure)) { + if (!renderer_factory_.Initialize()) + LOG(FATAL) << "Failed to initialize renderer factory"; + + if (delegate_) { + delegate_->AddObserver(this); + delegate_->Initialize(); + OnConfigurationChanged(); + } else { + LOG(WARNING) << "No display delegate; falling back to test window"; + int width = kTestWindowWidth; + int height = kTestWindowHeight; + sscanf(base::CommandLine::ForCurrentProcess() + ->GetSwitchValueASCII(kWindowSize) + .c_str(), + "%dx%d", &width, &height); + + DemoWindow* window = new DemoWindow(this, &renderer_factory_, + gfx::Rect(gfx::Size(width, height))); + window->Start(); + } +} + +WindowManager::~WindowManager() { + if (delegate_) + delegate_->RemoveObserver(this); +} + +void WindowManager::Quit() { + std::move(quit_closure_).Run(); +} + +void WindowManager::OnConfigurationChanged() { + if (is_configuring_) { + should_configure_ = true; + return; + } + + is_configuring_ = true; + delegate_->GetDisplays(base::BindRepeating(&WindowManager::OnDisplaysAquired, + base::Unretained(this))); +} + +void WindowManager::OnDisplaySnapshotsInvalidated() {} + +void WindowManager::OnDisplaysAquired( + const std::vector<display::DisplaySnapshot*>& displays) { + windows_.clear(); + + gfx::Point origin; + for (auto* display : displays) { + if (!display->native_mode()) { + LOG(ERROR) << "Display " << display->display_id() + << " doesn't have a native mode"; + continue; + } + + delegate_->Configure( + *display, display->native_mode(), origin, + base::BindRepeating(&WindowManager::OnDisplayConfigured, + base::Unretained(this), + gfx::Rect(origin, display->native_mode()->size()))); + origin.Offset(display->native_mode()->size().width(), 0); + } + is_configuring_ = false; + + if (should_configure_) { + should_configure_ = false; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindRepeating(&WindowManager::OnConfigurationChanged, + base::Unretained(this))); + } +} + +void WindowManager::OnDisplayConfigured(const gfx::Rect& bounds, bool success) { + if (success) { + std::unique_ptr<DemoWindow> window( + new DemoWindow(this, &renderer_factory_, bounds)); + window->Start(); + windows_.push_back(std::move(window)); + } else { + LOG(ERROR) << "Failed to configure display at " << bounds.ToString(); + } +} + +} // namespace ui diff --git a/chromium/ui/ozone/demo/window_manager.h b/chromium/ui/ozone/demo/window_manager.h new file mode 100644 index 00000000000..d15590843fc --- /dev/null +++ b/chromium/ui/ozone/demo/window_manager.h @@ -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. + +#ifndef UI_OZONE_DEMO_WINDOW_MANAGER_H_ +#define UI_OZONE_DEMO_WINDOW_MANAGER_H_ + +#include <vector> + +#include "base/callback.h" +#include "ui/display/types/native_display_observer.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/ozone/demo/renderer_factory.h" + +namespace display { + +class DisplaySnapshot; +class NativeDisplayDelegate; + +} // namespace display + +namespace ui { + +class DemoWindow; + +class WindowManager : public display::NativeDisplayObserver { + public: + explicit WindowManager(base::OnceClosure quit_closure); + ~WindowManager() override; + + void Quit(); + + void AddWindow(DemoWindow* window); + void RemoveWindow(DemoWindow* window); + + private: + void OnDisplaysAquired( + const std::vector<display::DisplaySnapshot*>& displays); + void OnDisplayConfigured(const gfx::Rect& bounds, bool success); + + // display::NativeDisplayDelegate: + void OnConfigurationChanged() override; + void OnDisplaySnapshotsInvalidated() override; + + std::unique_ptr<display::NativeDisplayDelegate> delegate_; + base::OnceClosure quit_closure_; + RendererFactory renderer_factory_; + std::vector<std::unique_ptr<DemoWindow>> windows_; + + // Flags used to keep track of the current state of display configuration. + // + // True if configuring the displays. In this case a new display configuration + // isn't started. + bool is_configuring_ = false; + + // If |is_configuring_| is true and another display configuration event + // happens, the event is deferred. This is set to true and a display + // configuration will be scheduled after the current one finishes. + bool should_configure_ = false; + + DISALLOW_COPY_AND_ASSIGN(WindowManager); +}; + +} // namespace ui + +#endif // UI_OZONE_DEMO_WINDOW_MANAGER_H_ diff --git a/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc b/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc index 52a42f83617..c2948020455 100644 --- a/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc +++ b/chromium/ui/ozone/gl/gl_image_ozone_native_pixmap_unittest.cc @@ -9,7 +9,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/buffer_types.h" #include "ui/gfx/client_native_pixmap.h" -#include "ui/gfx/client_native_pixmap_factory.h" #include "ui/gl/gl_image_native_pixmap.h" #include "ui/gl/test/gl_image_test_template.h" #include "ui/ozone/public/client_native_pixmap_factory_ozone.h" @@ -26,7 +25,7 @@ template <gfx::BufferUsage usage, gfx::BufferFormat format> class GLImageNativePixmapTestDelegate : public GLImageTestDelegateBase { public: GLImageNativePixmapTestDelegate() { - ui::CreateClientNativePixmapFactoryOzone(); + client_native_pixmap_factory_ = ui::CreateClientNativePixmapFactoryOzone(); } ~GLImageNativePixmapTestDelegate() override = default; @@ -41,9 +40,8 @@ class GLImageNativePixmapTestDelegate : public GLImageTestDelegateBase { DCHECK(pixmap); if (usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE || usage == gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE) { - auto client_pixmap = - gfx::ClientNativePixmapFactory::GetInstance()->ImportFromHandle( - pixmap->ExportHandle(), size, usage); + auto client_pixmap = client_native_pixmap_factory_->ImportFromHandle( + pixmap->ExportHandle(), size, usage); bool mapped = client_pixmap->Map(); EXPECT_TRUE(mapped); @@ -77,6 +75,8 @@ class GLImageNativePixmapTestDelegate : public GLImageTestDelegateBase { } private: + std::unique_ptr<gfx::ClientNativePixmapFactory> client_native_pixmap_factory_; + DISALLOW_COPY_AND_ASSIGN(GLImageNativePixmapTestDelegate); }; diff --git a/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc b/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc index 607bf221064..4aa44c770ba 100644 --- a/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc +++ b/chromium/ui/ozone/platform/cast/client_native_pixmap_factory_cast.cc @@ -5,7 +5,6 @@ #include "ui/ozone/platform/cast/client_native_pixmap_factory_cast.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "ui/gfx/buffer_types.h" #include "ui/gfx/client_native_pixmap.h" #include "ui/gfx/client_native_pixmap_factory.h" @@ -39,7 +38,7 @@ class ClientNativePixmapFactoryCast : public gfx::ClientNativePixmapFactory { // ClientNativePixmapFactoryCast implementation: bool IsConfigurationSupported(gfx::BufferFormat format, gfx::BufferUsage usage) const override { - return format == gfx::BufferFormat::RGBA_8888 && + return format == gfx::BufferFormat::BGRA_8888 && usage == gfx::BufferUsage::SCANOUT; } diff --git a/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc b/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc index 4066ed416c9..33d0651f58b 100644 --- a/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc +++ b/chromium/ui/ozone/platform/cast/gl_ozone_egl_cast.cc @@ -11,7 +11,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "chromecast/base/chromecast_switches.h" #include "chromecast/public/cast_egl_platform.h" diff --git a/chromium/ui/ozone/platform/cast/gl_surface_cast.cc b/chromium/ui/ozone/platform/cast/gl_surface_cast.cc index dd50b11cfec..7097dfbde90 100644 --- a/chromium/ui/ozone/platform/cast/gl_surface_cast.cc +++ b/chromium/ui/ozone/platform/cast/gl_surface_cast.cc @@ -5,7 +5,6 @@ #include "ui/ozone/platform/cast/gl_surface_cast.h" #include "base/feature_list.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "chromecast/base/cast_features.h" #include "chromecast/base/chromecast_switches.h" @@ -100,9 +99,10 @@ bool GLSurfaceCast::ScheduleOverlayPlane(int z_order, gfx::OverlayTransform transform, gl::GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { return image->ScheduleOverlayPlane(widget_, z_order, transform, bounds_rect, - crop_rect); + crop_rect, enable_blend); } EGLConfig GLSurfaceCast::GetConfig() { diff --git a/chromium/ui/ozone/platform/cast/gl_surface_cast.h b/chromium/ui/ozone/platform/cast/gl_surface_cast.h index 3d3a5412021..2bd8fa16ccf 100644 --- a/chromium/ui/ozone/platform/cast/gl_surface_cast.h +++ b/chromium/ui/ozone/platform/cast/gl_surface_cast.h @@ -35,7 +35,8 @@ class GLSurfaceCast : public gl::NativeViewGLSurfaceEGL { gfx::OverlayTransform transform, gl::GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; EGLConfig GetConfig() override; protected: diff --git a/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc b/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc index 9ce4f0a3e39..6a34f691350 100644 --- a/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc +++ b/chromium/ui/ozone/platform/cast/overlay_manager_cast.cc @@ -4,72 +4,18 @@ #include "ui/ozone/platform/cast/overlay_manager_cast.h" -#include "base/lazy_instance.h" -#include "base/memory/ptr_util.h" -#include "ui/gfx/geometry/rect_conversions.h" #include "ui/ozone/public/overlay_candidates_ozone.h" namespace ui { namespace { -base::LazyInstance<OverlayManagerCast::OverlayCompositedCallback>:: - DestructorAtExit g_overlay_composited_callback = LAZY_INSTANCE_INITIALIZER; - -// Translates a gfx::OverlayTransform into a VideoPlane::Transform. -// Could be just a lookup table once we have unit tests for this code -// to ensure it stays in sync with OverlayTransform. -chromecast::media::VideoPlane::Transform ConvertTransform( - gfx::OverlayTransform transform) { - switch (transform) { - case gfx::OVERLAY_TRANSFORM_NONE: - return chromecast::media::VideoPlane::TRANSFORM_NONE; - case gfx::OVERLAY_TRANSFORM_FLIP_HORIZONTAL: - return chromecast::media::VideoPlane::FLIP_HORIZONTAL; - case gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL: - return chromecast::media::VideoPlane::FLIP_VERTICAL; - case gfx::OVERLAY_TRANSFORM_ROTATE_90: - return chromecast::media::VideoPlane::ROTATE_90; - case gfx::OVERLAY_TRANSFORM_ROTATE_180: - return chromecast::media::VideoPlane::ROTATE_180; - case gfx::OVERLAY_TRANSFORM_ROTATE_270: - return chromecast::media::VideoPlane::ROTATE_270; - default: - NOTREACHED(); - return chromecast::media::VideoPlane::TRANSFORM_NONE; - } -} - class OverlayCandidatesCast : public OverlayCandidatesOzone { public: OverlayCandidatesCast() {} - void CheckOverlaySupport(OverlaySurfaceCandidateList* surfaces) override; + void CheckOverlaySupport(OverlaySurfaceCandidateList* surfaces) override {} }; -void OverlayCandidatesCast::CheckOverlaySupport( - OverlaySurfaceCandidateList* surfaces) { - for (auto& candidate : *surfaces) { - if (candidate.plane_z_order != -1) - continue; - - candidate.overlay_handled = true; - - // Compositor requires all overlay rectangles to have integer coords. - candidate.display_rect = - gfx::RectF(gfx::ToEnclosedRect(candidate.display_rect)); - - chromecast::RectF display_rect( - candidate.display_rect.x(), candidate.display_rect.y(), - candidate.display_rect.width(), candidate.display_rect.height()); - - // Update video plane geometry + transform to match compositor quad. - if (!g_overlay_composited_callback.Get().is_null()) - g_overlay_composited_callback.Get().Run( - display_rect, ConvertTransform(candidate.transform)); - return; - } -} - } // namespace OverlayManagerCast::OverlayManagerCast() { @@ -83,10 +29,4 @@ OverlayManagerCast::CreateOverlayCandidates(gfx::AcceleratedWidget w) { return std::make_unique<OverlayCandidatesCast>(); } -// static -void OverlayManagerCast::SetOverlayCompositedCallback( - const OverlayCompositedCallback& cb) { - g_overlay_composited_callback.Get() = cb; -} - } // namespace ui diff --git a/chromium/ui/ozone/platform/cast/overlay_manager_cast.h b/chromium/ui/ozone/platform/cast/overlay_manager_cast.h index 0ddf98d67f5..6d9963b4f4a 100644 --- a/chromium/ui/ozone/platform/cast/overlay_manager_cast.h +++ b/chromium/ui/ozone/platform/cast/overlay_manager_cast.h @@ -7,16 +7,12 @@ #include <memory> -#include "base/callback.h" #include "base/macros.h" -#include "chromecast/public/graphics_types.h" -#include "chromecast/public/video_plane.h" -#include "ui/ozone/ozone_export.h" #include "ui/ozone/public/overlay_manager_ozone.h" namespace ui { -class OZONE_EXPORT OverlayManagerCast : public OverlayManagerOzone { +class OverlayManagerCast : public OverlayManagerOzone { public: OverlayManagerCast(); ~OverlayManagerCast() override; @@ -25,16 +21,7 @@ class OZONE_EXPORT OverlayManagerCast : public OverlayManagerOzone { std::unique_ptr<OverlayCandidatesOzone> CreateOverlayCandidates( gfx::AcceleratedWidget w) override; - // Callback that's made whenever an overlay quad is processed - // in the compositor. Used to allow hardware video plane to - // be positioned to match compositor hole. - using OverlayCompositedCallback = - base::Callback<void(const chromecast::RectF&, - chromecast::media::VideoPlane::Transform)>; - static void SetOverlayCompositedCallback(const OverlayCompositedCallback& cb); - private: - DISALLOW_COPY_AND_ASSIGN(OverlayManagerCast); }; diff --git a/chromium/ui/ozone/platform/cast/platform_window_cast.cc b/chromium/ui/ozone/platform/cast/platform_window_cast.cc index d5a33378cd5..a24276ca87a 100644 --- a/chromium/ui/ozone/platform/cast/platform_window_cast.cc +++ b/chromium/ui/ozone/platform/cast/platform_window_cast.cc @@ -18,15 +18,13 @@ PlatformWindowCast::PlatformWindowCast(PlatformWindowDelegate* delegate, widget_ = (bounds.width() << 16) + bounds.height(); delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f); - if (ui::PlatformEventSource::GetInstance()) { - ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); - } + if (PlatformEventSource::GetInstance()) + PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); } PlatformWindowCast::~PlatformWindowCast() { - if (ui::PlatformEventSource::GetInstance()) { - ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); - } + if (PlatformEventSource::GetInstance()) + PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); } gfx::Rect PlatformWindowCast::GetBounds() { @@ -41,16 +39,19 @@ void PlatformWindowCast::SetBounds(const gfx::Rect& bounds) { void PlatformWindowCast::SetTitle(const base::string16& title) { } +bool PlatformWindowCast::HasCapture() const { + return false; +} + PlatformImeController* PlatformWindowCast::GetPlatformImeController() { return nullptr; } -bool PlatformWindowCast::CanDispatchEvent(const ui::PlatformEvent& ne) { +bool PlatformWindowCast::CanDispatchEvent(const PlatformEvent& ne) { return true; } -uint32_t PlatformWindowCast::DispatchEvent( - const ui::PlatformEvent& native_event) { +uint32_t PlatformWindowCast::DispatchEvent(const PlatformEvent& native_event) { DispatchEventFromNativeUiEvent( native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent, base::Unretained(delegate_))); diff --git a/chromium/ui/ozone/platform/cast/platform_window_cast.h b/chromium/ui/ozone/platform/cast/platform_window_cast.h index 4a494f2c830..a7930ee26fa 100644 --- a/chromium/ui/ozone/platform/cast/platform_window_cast.h +++ b/chromium/ui/ozone/platform/cast/platform_window_cast.h @@ -31,6 +31,7 @@ class PlatformWindowCast : public PlatformWindow, void PrepareForShutdown() override {} void SetCapture() override {} void ReleaseCapture() override {} + bool HasCapture() const override; void ToggleFullscreen() override {} void Maximize() override {} void Minimize() override {} diff --git a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc index 234c3f90d71..4718585dcd0 100644 --- a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc +++ b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc @@ -69,7 +69,8 @@ class CastPixmap : public gfx::NativePixmap { int plane_z_order, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, - const gfx::RectF& crop_rect) override { + const gfx::RectF& crop_rect, + bool enable_blend) override { parent_->OnOverlayScheduled(display_bounds); return true; } diff --git a/chromium/ui/ozone/platform/drm/BUILD.gn b/chromium/ui/ozone/platform/drm/BUILD.gn index 99fc0d66bf2..7ff7018bbb6 100644 --- a/chromium/ui/ozone/platform/drm/BUILD.gn +++ b/chromium/ui/ozone/platform/drm/BUILD.gn @@ -114,7 +114,7 @@ source_set("gbm") { deps = [ "//base", "//ipc", - "//mojo/common:common_base", + "//mojo/public/cpp/system", "//services/service_manager/public/cpp", "//services/ui/public/interfaces:constants", "//skia", diff --git a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc index f8dd781ebf4..8e363e69280 100644 --- a/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc +++ b/chromium/ui/ozone/platform/drm/client_native_pixmap_factory_gbm.cc @@ -7,7 +7,6 @@ #include <utility> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "ui/gfx/linux/client_native_pixmap_factory_dmabuf.h" #include "ui/gfx/native_pixmap_handle.h" diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.cc b/chromium/ui/ozone/platform/drm/common/drm_util.cc index d4c3dd51037..77054a23dbf 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util.cc @@ -14,7 +14,7 @@ #include <utility> #include "base/containers/flat_map.h" -#include "base/memory/ptr_util.h" +#include "ui/display/types/display_constants.h" #include "ui/display/types/display_mode.h" #include "ui/display/util/edid_parser.h" @@ -381,7 +381,7 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot( const base::FilePath& sys_path, size_t device_index, const gfx::Point& origin) { - int64_t display_id = ConnectorIndex(device_index, info->index()); + const uint8_t display_index = ConnectorIndex(device_index, info->index()); const gfx::Size physical_size = gfx::Size(info->connector()->mmWidth, info->connector()->mmHeight); const display::DisplayConnectionType type = GetDisplayType(info->connector()); @@ -391,29 +391,31 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot( HasColorCorrectionMatrix(fd, info->crtc()); const gfx::Size maximum_cursor_size = GetMaximumCursorSize(fd); - std::vector<uint8_t> edid; std::string display_name; - int64_t product_id = display::DisplaySnapshot::kInvalidProductID; + int64_t display_id = display_index; + int64_t product_code = display::DisplaySnapshot::kInvalidProductCode; + int32_t year_of_manufacture = display::kInvalidYearOfManufacture; bool has_overscan = false; gfx::ColorSpace display_color_space; - - // This is the size of the active pixels from the first detailed timing - // descriptor in the EDID. + // Active pixels size from the first detailed timing descriptor in the EDID. gfx::Size active_pixel_size; ScopedDrmPropertyBlobPtr edid_blob( GetDrmPropertyBlob(fd, info->connector(), "EDID")); + std::vector<uint8_t> edid; if (edid_blob) { edid.assign(static_cast<uint8_t*>(edid_blob->data), static_cast<uint8_t*>(edid_blob->data) + edid_blob->length); - display::GetDisplayIdFromEDID(edid, display_id, &display_id, &product_id); - - display::ParseOutputDeviceData(edid, nullptr, nullptr, &display_name, - &active_pixel_size, nullptr); - display::ParseOutputOverscanFlag(edid, &has_overscan); - - display_color_space = GetColorSpaceFromEdid(edid); + display::EdidParser edid_parser(edid); + display_name = edid_parser.display_name(); + active_pixel_size = edid_parser.active_pixel_size(); + product_code = edid_parser.GetProductCode(); + display_id = edid_parser.GetDisplayId(display_index); + year_of_manufacture = edid_parser.year_of_manufacture(); + has_overscan = + edid_parser.has_overscan_flag() && edid_parser.overscan_flag(); + display_color_space = GetColorSpaceFromEdid(edid_parser); } else { VLOG(1) << "Failed to get EDID blob for connector " << info->connector()->connector_id; @@ -428,7 +430,7 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot( display_id, origin, physical_size, type, is_aspect_preserving_scaling, has_overscan, has_color_correction_matrix, display_color_space, display_name, sys_path, std::move(modes), edid, current_mode, native_mode, - product_id, maximum_cursor_size); + product_code, year_of_manufacture, maximum_cursor_size); } // TODO(rjkroege): Remove in a subsequent CL once Mojo IPC is used everywhere. @@ -464,7 +466,8 @@ std::vector<DisplaySnapshot_Params> CreateDisplaySnapshotParams( if (d->native_mode()) p.native_mode = GetDisplayModeParams(*d->native_mode()); - p.product_id = d->product_id(); + p.product_code = d->product_code(); + p.year_of_manufacture = d->year_of_manufacture(); p.maximum_cursor_size = d->maximum_cursor_size(); params.push_back(p); @@ -490,7 +493,8 @@ std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshot( params.is_aspect_preserving_scaling, params.has_overscan, params.has_color_correction_matrix, params.color_space, params.display_name, params.sys_path, std::move(modes), params.edid, - current_mode, native_mode, params.product_id, params.maximum_cursor_size); + current_mode, native_mode, params.product_code, + params.year_of_manufacture, params.maximum_cursor_size); } int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format) { @@ -631,10 +635,8 @@ std::vector<OverlayCheckReturn_Params> CreateParamsFromOverlayStatusList( return params; } -gfx::ColorSpace GetColorSpaceFromEdid(const std::vector<uint8_t>& edid) { - SkColorSpacePrimaries primaries = {0}; - if (!display::ParseChromaticityCoordinates(edid, &primaries)) - return gfx::ColorSpace(); +gfx::ColorSpace GetColorSpaceFromEdid(const display::EdidParser& edid_parser) { + const SkColorSpacePrimaries primaries = edid_parser.primaries(); // Sanity check: primaries should verify By <= Ry <= Gy, Bx <= Rx and Gx <= // Rx, to guarantee that the R, G and B colors are each in the correct region. @@ -653,12 +655,25 @@ gfx::ColorSpace GetColorSpaceFromEdid(const std::vector<uint8_t>& edid) { if (primaries_area_twice < kBT709PrimariesArea) return gfx::ColorSpace(); + // Sanity check: https://crbug.com/809909, the blue primary coordinates should + // not be too far left/upwards of the expected location (namely [0.15, 0.06] + // for sRGB/ BT.709/ Adobe RGB/ DCI-P3, and [0.131, 0.046] for BT.2020). + constexpr float kExpectedBluePrimaryX = 0.15f; + constexpr float kBluePrimaryXDelta = 0.02f; + constexpr float kExpectedBluePrimaryY = 0.06f; + constexpr float kBluePrimaryYDelta = 0.031f; + const bool is_blue_primary_broken = + (std::abs(primaries.fBX - kExpectedBluePrimaryX) > kBluePrimaryXDelta) || + (std::abs(primaries.fBY - kExpectedBluePrimaryY) > kBluePrimaryYDelta); + if (is_blue_primary_broken) + return gfx::ColorSpace(); + SkMatrix44 color_space_as_matrix; if (!primaries.toXYZD50(&color_space_as_matrix)) return gfx::ColorSpace(); - double gamma = 0.0; - if (!display::ParseGammaValue(edid, &gamma)) + const double gamma = edid_parser.gamma(); + if (gamma < 1.0) return gfx::ColorSpace(); SkColorSpaceTransferFn transfer = {gamma, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.h b/chromium/ui/ozone/platform/drm/common/drm_util.h index 9a058a78180..6677fe19cfe 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.h +++ b/chromium/ui/ozone/platform/drm/common/drm_util.h @@ -21,6 +21,7 @@ typedef struct _drmModeModeInfo drmModeModeInfo; namespace display { class DisplayMode; +class EdidParser; } // namespace display namespace gfx { @@ -119,9 +120,9 @@ OverlayStatusList CreateOverlayStatusListFrom( std::vector<OverlayCheckReturn_Params> CreateParamsFromOverlayStatusList( const OverlayStatusList& returns); -// Parses |edid| to extract a gfx::ColorSpace which will be IsValid() if both -// gamma and the color primaries were correctly found. -gfx::ColorSpace GetColorSpaceFromEdid(const std::vector<uint8_t>& edid); +// Uses |edid_parser| to extract a gfx::ColorSpace which will be IsValid() if +// both gamma and the color primaries were correctly found. +gfx::ColorSpace GetColorSpaceFromEdid(const display::EdidParser& edid_parser); } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc b/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc index 03dae19b0fc..ec4227e1e31 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc @@ -11,6 +11,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/display/types/display_snapshot.h" +#include "ui/display/util/edid_parser.h" #include "ui/gfx/geometry/size.h" #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" @@ -93,6 +94,17 @@ const unsigned char kSST210Corrected[] = "\x30\x31\x20\x54\x69\x44\x69\x67\x61\x74\x0a\x6c\x00\x00\xff\x00" "\x48\x00\x4b\x34\x41\x54\x30\x30\x32\x38\x0a\x38\x20\x20\xf8\x00"; +// This EDID produces blue primary coordinates too far off the expected point, +// which would paint blue colors as purple. See https://crbug.com/809909. +const unsigned char kBrokenBluePrimaries[] = + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x83\x4d\x83\x00\x00\x00\x00" + "\x00\x19\x01\x04\x95\x1d\x10\x78\x0a\xee\x25\xa3\x54\x4c\x99\x29" + "\x26\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\xd2\x37\x80\xa2\x70\x38\x40\x40\x30\x20" + "\x25\x00\x25\xa5\x10\x00\x00\x1a\xa6\x2c\x80\xa2\x70\x38\x40\x40" + "\x30\x20\x25\x00\x25\xa5\x10\x00\x00\x1a\x00\x00\x00\xfe\x00\x56" + "\x59\x54\x39\x36\x80\x31\x33\x33\x48\x4c\x0a\x20\x00\x00\x00\x00"; + } // anonymous namespace bool operator==(const ui::DisplayMode_Params& a, @@ -114,7 +126,8 @@ bool operator==(const ui::DisplaySnapshot_Params& a, a.has_current_mode == b.has_current_mode && a.current_mode == b.current_mode && a.has_native_mode == b.has_native_mode && - a.native_mode == b.native_mode && a.product_id == b.product_id && + a.native_mode == b.native_mode && a.product_code == b.product_code && + a.year_of_manufacture == b.year_of_manufacture && a.maximum_cursor_size == b.maximum_cursor_size; } @@ -146,7 +159,8 @@ void DetailedCompare(const ui::DisplaySnapshot_Params& a, EXPECT_EQ(a.current_mode, b.current_mode); EXPECT_EQ(a.has_native_mode, b.has_native_mode); EXPECT_EQ(a.native_mode, b.native_mode); - EXPECT_EQ(a.product_id, b.product_id); + EXPECT_EQ(a.product_code, b.product_code); + EXPECT_EQ(a.year_of_manufacture, b.year_of_manufacture); EXPECT_EQ(a.maximum_cursor_size, b.maximum_cursor_size); } @@ -187,7 +201,8 @@ TEST_F(DrmUtilTest, RoundTripDisplaySnapshot) { fp.current_mode = MakeDisplay(1.2); fp.has_native_mode = true; fp.native_mode = MakeDisplay(1.1); - fp.product_id = 7; + fp.product_code = 7; + fp.year_of_manufacture = 1776; fp.maximum_cursor_size = gfx::Size(103, 44); sp.display_id = 1002; @@ -206,7 +221,8 @@ TEST_F(DrmUtilTest, RoundTripDisplaySnapshot) { sp.has_current_mode = false; sp.has_native_mode = true; sp.native_mode = MakeDisplay(500.2); - sp.product_id = 8; + sp.product_code = 8; + sp.year_of_manufacture = 2018; sp.maximum_cursor_size = gfx::Size(500, 44); ep.display_id = 2002; @@ -225,7 +241,8 @@ TEST_F(DrmUtilTest, RoundTripDisplaySnapshot) { ep.has_current_mode = true; ep.current_mode = MakeDisplay(1000.2); ep.has_native_mode = false; - ep.product_id = 9; + ep.product_code = 9; + ep.year_of_manufacture = 2000; ep.maximum_cursor_size = gfx::Size(1000, 44); orig_params.push_back(fp); @@ -298,8 +315,8 @@ TEST_F(DrmUtilTest, GetColorSpaceFromEdid) { hpz32x_toXYZ50_matrix.setRowMajord(hpz32x_toXYZ50_coeffs); const gfx::ColorSpace hpz32x_color_space = gfx::ColorSpace::CreateCustom( hpz32x_toXYZ50_matrix, SkColorSpaceTransferFn({2.2, 1, 0, 0, 0, 0, 0})); - EXPECT_STREQ(hpz32x_color_space.ToString().c_str(), - GetColorSpaceFromEdid(hpz32x_edid).ToString().c_str()); + EXPECT_EQ(hpz32x_color_space.ToString(), + GetColorSpaceFromEdid(display::EdidParser(hpz32x_edid)).ToString()); const std::vector<uint8_t> samus_edid(kSamus, kSamus + arraysize(kSamus) - 1); const double samus_toXYZ50_coeffs[] = {0.41211, 0.39743, 0.15468, 0., @@ -309,8 +326,8 @@ TEST_F(DrmUtilTest, GetColorSpaceFromEdid) { samus_toXYZ50_matrix.setRowMajord(samus_toXYZ50_coeffs); const gfx::ColorSpace samus_color_space = gfx::ColorSpace::CreateCustom( samus_toXYZ50_matrix, SkColorSpaceTransferFn({2.5, 1, 0, 0, 0, 0, 0})); - EXPECT_STREQ(samus_color_space.ToString().c_str(), - GetColorSpaceFromEdid(samus_edid).ToString().c_str()); + EXPECT_EQ(samus_color_space.ToString(), + GetColorSpaceFromEdid(display::EdidParser(samus_edid)).ToString()); const std::vector<uint8_t> eve_edid(kEve, kEve + arraysize(kEve) - 1); const double eve_toXYZ50_coeffs[] = {0.444601, 0.377972, 0.141646, 0., @@ -320,37 +337,47 @@ TEST_F(DrmUtilTest, GetColorSpaceFromEdid) { eve_toXYZ50_matrix.setRowMajord(eve_toXYZ50_coeffs); const gfx::ColorSpace eve_color_space = gfx::ColorSpace::CreateCustom( eve_toXYZ50_matrix, SkColorSpaceTransferFn({2.2, 1, 0, 0, 0, 0, 0})); - EXPECT_STREQ(eve_color_space.ToString().c_str(), - GetColorSpaceFromEdid(eve_edid).ToString().c_str()); + EXPECT_EQ(eve_color_space.ToString(), + GetColorSpaceFromEdid(display::EdidParser(eve_edid)).ToString()); const std::vector<uint8_t> no_gamma_edid( kEdidWithNoGamma, kEdidWithNoGamma + arraysize(kEdidWithNoGamma) - 1); const gfx::ColorSpace no_gamma_color_space = - GetColorSpaceFromEdid(no_gamma_edid); + GetColorSpaceFromEdid(display::EdidParser(no_gamma_edid)); EXPECT_FALSE(no_gamma_color_space.IsValid()); } TEST_F(DrmUtilTest, GetInvalidColorSpaceFromEdid) { const std::vector<uint8_t> empty_edid; - EXPECT_EQ(gfx::ColorSpace(), GetColorSpaceFromEdid(empty_edid)); + EXPECT_EQ(gfx::ColorSpace(), + GetColorSpaceFromEdid(display::EdidParser(empty_edid))); const std::vector<uint8_t> invalid_edid( kInvalidEdid, kInvalidEdid + arraysize(kInvalidEdid) - 1); const gfx::ColorSpace invalid_color_space = - GetColorSpaceFromEdid(invalid_edid); + GetColorSpaceFromEdid(display::EdidParser(invalid_edid)); EXPECT_FALSE(invalid_color_space.IsValid()); const std::vector<uint8_t> sst210_edid(kSST210, kSST210 + arraysize(kSST210) - 1); - const gfx::ColorSpace sst210_color_space = GetColorSpaceFromEdid(sst210_edid); + const gfx::ColorSpace sst210_color_space = + GetColorSpaceFromEdid(display::EdidParser(sst210_edid)); EXPECT_FALSE(sst210_color_space.IsValid()) << sst210_color_space.ToString(); const std::vector<uint8_t> sst210_edid_2( kSST210Corrected, kSST210Corrected + arraysize(kSST210Corrected) - 1); const gfx::ColorSpace sst210_color_space_2 = - GetColorSpaceFromEdid(sst210_edid_2); + GetColorSpaceFromEdid(display::EdidParser(sst210_edid_2)); EXPECT_FALSE(sst210_color_space_2.IsValid()) << sst210_color_space_2.ToString(); + + const std::vector<uint8_t> broken_blue_edid( + kBrokenBluePrimaries, + kBrokenBluePrimaries + arraysize(kBrokenBluePrimaries) - 1); + const gfx::ColorSpace broken_blue_color_space = + GetColorSpaceFromEdid(display::EdidParser(broken_blue_edid)); + EXPECT_FALSE(broken_blue_color_space.IsValid()) + << broken_blue_color_space.ToString(); } TEST_F(DrmUtilTest, TestDisplayModesExtraction) { diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc index 88456854b97..fdf87224eda 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc @@ -44,11 +44,11 @@ CrtcController::~CrtcController() { } bool CrtcController::Modeset(const OverlayPlane& plane, drmModeModeInfo mode) { - if (!drm_->SetCrtc(crtc_, plane.buffer->GetFramebufferId(), + if (!drm_->SetCrtc(crtc_, plane.buffer->GetOpaqueFramebufferId(), std::vector<uint32_t>(1, connector_), &mode)) { PLOG(ERROR) << "Failed to modeset: crtc=" << crtc_ << " connector=" << connector_ - << " framebuffer_id=" << plane.buffer->GetFramebufferId() + << " framebuffer_id=" << plane.buffer->GetOpaqueFramebufferId() << " mode=" << mode.hdisplay << "x" << mode.vdisplay << "@" << mode.vrefresh; return false; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc index 9fd0bd15bc8..b3ffc96902d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc @@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/memory/free_deleter.h" #include "base/message_loop/message_loop.h" +#include "base/message_loop/message_pump_for_io.h" #include "base/posix/safe_strerror.h" #include "base/task_runner.h" #include "base/threading/thread_task_runner_handle.h" @@ -289,7 +290,7 @@ class DrmDevice::PageFlipManager { DISALLOW_COPY_AND_ASSIGN(PageFlipManager); }; -class DrmDevice::IOWatcher : public base::MessagePumpLibevent::Watcher { +class DrmDevice::IOWatcher : public base::MessagePumpLibevent::FdWatcher { public: IOWatcher(int fd, DrmDevice::PageFlipManager* page_flip_manager) : page_flip_manager_(page_flip_manager), controller_(FROM_HERE), fd_(fd) { @@ -302,7 +303,7 @@ class DrmDevice::IOWatcher : public base::MessagePumpLibevent::Watcher { void Register() { DCHECK(base::MessageLoopForIO::IsCurrent()); base::MessageLoopForIO::current()->WatchFileDescriptor( - fd_, true, base::MessageLoopForIO::WATCH_READ, &controller_, this); + fd_, true, base::MessagePumpForIO::WATCH_READ, &controller_, this); } void Unregister() { @@ -310,7 +311,7 @@ class DrmDevice::IOWatcher : public base::MessagePumpLibevent::Watcher { controller_.StopWatchingFileDescriptor(); } - // base::MessagePumpLibevent::Watcher overrides: + // base::MessagePumpLibevent::FdWatcher overrides: void OnFileCanReadWithoutBlocking(int fd) override { DCHECK(base::MessageLoopForIO::IsCurrent()); TRACE_EVENT1("drm", "OnDrmEvent", "socket", fd); @@ -324,7 +325,7 @@ class DrmDevice::IOWatcher : public base::MessagePumpLibevent::Watcher { DrmDevice::PageFlipManager* page_flip_manager_; - base::MessagePumpLibevent::FileDescriptorWatcher controller_; + base::MessagePumpLibevent::FdWatchController controller_; int fd_; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc index 07d013b97c3..ce85361b01e 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.cc @@ -7,7 +7,6 @@ #include <xf86drmMode.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" #include "ui/ozone/platform/drm/common/drm_util.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc index d07f5760ed1..a1cd5650a7c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc @@ -6,7 +6,6 @@ #include <stddef.h> -#include "base/memory/ptr_util.h" #include "ui/display/types/display_mode.h" #include "ui/display/types/display_snapshot.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc index 15d5bae1977..d0a82568e4a 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.cc @@ -82,7 +82,7 @@ std::vector<OverlayCheckReturn_Params> DrmOverlayValidator::TestPageFlip( OverlayPlane plane(buffer, params[i].plane_z_order, params[i].transform, params[i].display_rect, params[i].crop_rect, - base::kInvalidPlatformFile); + /* enable_blend */ true, base::kInvalidPlatformFile); test_list.push_back(plane); if (buffer && controller->TestPageFlip(test_list)) { 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 0328b0cc82c..3b3642b8d88 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 @@ -138,7 +138,7 @@ void DrmOverlayValidatorTest::AddPlane(const ui::OverlayCheck_Params& params) { params.buffer_size); ui::OverlayPlane plane(std::move(scanout_buffer), params.plane_z_order, params.transform, params.display_rect, - params.crop_rect, base::kInvalidPlatformFile); + params.crop_rect, true, base::kInvalidPlatformFile); plane_list_.push_back(plane); } diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc index 566de8b0b67..6e187761ea9 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc @@ -9,7 +9,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" @@ -204,7 +203,7 @@ void DrmThread::SchedulePageFlip(gfx::AcceleratedWidget widget, drm_device->plane_manager()->RequestPlanesReadyCallback( planes, base::BindOnce(&DrmThread::OnPlanesReadyForPageFlip, weak_ptr_factory_.GetWeakPtr(), widget, planes, - base::Passed(&callback))); + std::move(callback))); } void DrmThread::OnPlanesReadyForPageFlip( diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc index 6e1d4a749e3..842ed61ff2c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.cc @@ -65,23 +65,23 @@ bool DrmThreadMessageProxy::OnMessageReceived(const IPC::Message& message) { void DrmThreadMessageProxy::OnCreateWindow(gfx::AcceleratedWidget widget) { DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::CreateWindow, - base::Unretained(drm_thread_), widget)); + FROM_HERE, base::BindOnce(&DrmThread::CreateWindow, + base::Unretained(drm_thread_), widget)); } void DrmThreadMessageProxy::OnDestroyWindow(gfx::AcceleratedWidget widget) { DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::DestroyWindow, - base::Unretained(drm_thread_), widget)); + FROM_HERE, base::BindOnce(&DrmThread::DestroyWindow, + base::Unretained(drm_thread_), widget)); } void DrmThreadMessageProxy::OnWindowBoundsChanged(gfx::AcceleratedWidget widget, const gfx::Rect& bounds) { DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::SetWindowBounds, - base::Unretained(drm_thread_), widget, bounds)); + FROM_HERE, base::BindOnce(&DrmThread::SetWindowBounds, + base::Unretained(drm_thread_), widget, bounds)); } void DrmThreadMessageProxy::OnCursorSet(gfx::AcceleratedWidget widget, @@ -91,16 +91,17 @@ void DrmThreadMessageProxy::OnCursorSet(gfx::AcceleratedWidget widget, DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( FROM_HERE, - base::Bind(&DrmThread::SetCursor, base::Unretained(drm_thread_), widget, - bitmaps, location, frame_delay_ms)); + base::BindOnce(&DrmThread::SetCursor, base::Unretained(drm_thread_), + widget, bitmaps, location, frame_delay_ms)); } void DrmThreadMessageProxy::OnCursorMove(gfx::AcceleratedWidget widget, const gfx::Point& location) { DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::MoveCursor, - base::Unretained(drm_thread_), widget, location)); + FROM_HERE, + base::BindOnce(&DrmThread::MoveCursor, base::Unretained(drm_thread_), + widget, location)); } void DrmThreadMessageProxy::OnCheckOverlayCapabilities( @@ -191,15 +192,15 @@ void DrmThreadMessageProxy::OnAddGraphicsDevice( base::File file(fd.fd); drm_thread_->task_runner()->PostTask( FROM_HERE, - base::Bind(&DrmThread::AddGraphicsDevice, base::Unretained(drm_thread_), - path, Passed(&file))); + base::BindOnce(&DrmThread::AddGraphicsDevice, + base::Unretained(drm_thread_), path, std::move(file))); } void DrmThreadMessageProxy::OnRemoveGraphicsDevice(const base::FilePath& path) { DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::RemoveGraphicsDevice, - base::Unretained(drm_thread_), path)); + FROM_HERE, base::BindOnce(&DrmThread::RemoveGraphicsDevice, + base::Unretained(drm_thread_), path)); } void DrmThreadMessageProxy::OnGetHDCPState(int64_t display_id) { @@ -232,9 +233,9 @@ void DrmThreadMessageProxy::OnSetColorCorrection( const std::vector<float>& correction_matrix) { DCHECK(drm_thread_->IsRunning()); drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::Bind(&DrmThread::SetColorCorrection, base::Unretained(drm_thread_), - id, degamma_lut, gamma_lut, correction_matrix)); + FROM_HERE, base::BindOnce(&DrmThread::SetColorCorrection, + base::Unretained(drm_thread_), id, degamma_lut, + gamma_lut, correction_matrix)); } void DrmThreadMessageProxy::OnCheckOverlayCapabilitiesCallback( diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc index 2bad52ddd44..fe6c4b3affa 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc @@ -5,7 +5,6 @@ #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h" #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h" #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" #include "ui/ozone/platform/drm/gpu/gbm_buffer.h" @@ -76,8 +75,8 @@ void DrmThreadProxy::AddBindingCursorDevice( ozone::mojom::DeviceCursorRequest request) { drm_thread_.task_runner()->PostTask( FROM_HERE, - base::Bind(&DrmThread::AddBindingCursorDevice, - base::Unretained(&drm_thread_), base::Passed(&request))); + base::BindOnce(&DrmThread::AddBindingCursorDevice, + base::Unretained(&drm_thread_), std::move(request))); } void DrmThreadProxy::AddBindingDrmDevice( @@ -87,8 +86,8 @@ void DrmThreadProxy::AddBindingDrmDevice( drm_thread_.task_runner()->PostTask( FROM_HERE, - base::Bind(&DrmThread::AddBindingDrmDevice, - base::Unretained(&drm_thread_), base::Passed(&request))); + base::BindOnce(&DrmThread::AddBindingDrmDevice, + base::Unretained(&drm_thread_), std::move(request))); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc index 51d18556c01..6deff4440b2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc @@ -8,7 +8,6 @@ #include <stdint.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" #include "third_party/skia/include/core/SkBitmap.h" 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 cd41e064885..f5f882a7958 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc @@ -30,9 +30,9 @@ void DrmWindowProxy::SchedulePageFlip(const std::vector<OverlayPlane>& planes, void DrmWindowProxy::GetVSyncParameters( const gfx::VSyncProvider::UpdateVSyncCallback& callback) { drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::Bind(&DrmThread::GetVSyncParameters, base::Unretained(drm_thread_), - widget_, CreateSafeCallback(callback))); + FROM_HERE, base::BindOnce(&DrmThread::GetVSyncParameters, + base::Unretained(drm_thread_), widget_, + CreateSafeCallback(callback))); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc index 4faa63288cc..40a207ec1c3 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_unittest.cc @@ -183,7 +183,7 @@ TEST_F(DrmWindowTest, CheckCallbackOnFailedSwap) { // Window was re-sized, so the expectation is to re-create the buffers first. window->SchedulePageFlip( std::vector<ui::OverlayPlane>(1, ui::OverlayPlane(plane)), - base::Bind(&DrmWindowTest::OnSwapBuffers, base::Unretained(this))); + base::BindOnce(&DrmWindowTest::OnSwapBuffers, base::Unretained(this))); EXPECT_EQ(1, on_swap_buffers_count_); EXPECT_EQ(gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS, last_swap_buffers_result_); @@ -191,7 +191,7 @@ TEST_F(DrmWindowTest, CheckCallbackOnFailedSwap) { window->SchedulePageFlip( std::vector<ui::OverlayPlane>(1, ui::OverlayPlane(plane)), - base::Bind(&DrmWindowTest::OnSwapBuffers, base::Unretained(this))); + base::BindOnce(&DrmWindowTest::OnSwapBuffers, base::Unretained(this))); EXPECT_EQ(2, on_swap_buffers_count_); EXPECT_EQ(gfx::SwapResult::SWAP_FAILED, last_swap_buffers_result_); EXPECT_EQ(gfx::PresentationFeedback(), last_presentation_feedback_); diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc index 17948261354..19a26ada002 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc @@ -385,11 +385,12 @@ bool GbmPixmap::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int plane_z_order, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, - const gfx::RectF& crop_rect) { + const gfx::RectF& crop_rect, + bool enable_blend) { DCHECK(buffer_->GetFlags() & GBM_BO_USE_SCANOUT); surface_manager_->GetSurface(widget)->QueueOverlayPlane( OverlayPlane(buffer_, plane_z_order, plane_transform, display_bounds, - crop_rect, base::kInvalidPlatformFile)); + crop_rect, enable_blend, base::kInvalidPlatformFile)); return true; } diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h index 984e7df6afd..8a5573db3e4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.h @@ -121,7 +121,8 @@ class GbmPixmap : public gfx::NativePixmap { int plane_z_order, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; gfx::NativePixmapHandle ExportHandle() override; scoped_refptr<GbmBuffer> buffer() { return buffer_; } diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc index ac50b648d41..56c05a954a5 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc @@ -65,7 +65,7 @@ void GbmSurface::SwapBuffersAsync( const PresentationCallback& presentation_callback) { if (!images_[current_surface_]->ScheduleOverlayPlane( widget(), 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, - gfx::Rect(GetSize()), gfx::RectF(1, 1))) { + gfx::Rect(GetSize()), gfx::RectF(1, 1), /* enable_blend */ false)) { completion_callback.Run(gfx::SwapResult::SWAP_FAILED); // Notify the caller, the buffer is never presented on a screen. presentation_callback.Run(gfx::PresentationFeedback()); @@ -136,7 +136,7 @@ bool GbmSurface::CreatePixmaps() { if (!pixmap) return false; scoped_refptr<gl::GLImageNativePixmap> image = - new gl::GLImageNativePixmap(GetSize(), GL_RGB); + new gl::GLImageNativePixmap(GetSize(), GL_BGRA_EXT); if (!image->Initialize(pixmap.get(), display::DisplaySnapshot::PrimaryFormat())) return false; 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 ca8a5206071..4fdef17f041 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc @@ -9,7 +9,6 @@ #include <utility> #include "base/files/file_path.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "third_party/khronos/EGL/egl.h" #include "ui/gfx/buffer_format_util.h" diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index fab047e68ca..f067067795a 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc @@ -8,7 +8,6 @@ #include "base/bind.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/task_scheduler/post_task.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/presentation_feedback.h" @@ -68,9 +67,10 @@ bool GbmSurfaceless::ScheduleOverlayPlane(int z_order, gfx::OverlayTransform transform, gl::GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) { - unsubmitted_frames_.back()->overlays.push_back( - gl::GLSurfaceOverlay(z_order, transform, image, bounds_rect, crop_rect)); + const gfx::RectF& crop_rect, + bool enable_blend) { + unsubmitted_frames_.back()->overlays.push_back(gl::GLSurfaceOverlay( + z_order, transform, image, bounds_rect, crop_rect, enable_blend)); 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 32cf0e8e863..71c3022e384 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h @@ -41,7 +41,8 @@ class GbmSurfaceless : public gl::SurfacelessEGL { gfx::OverlayTransform transform, gl::GLImage* image, const gfx::Rect& bounds_rect, - const gfx::RectF& crop_rect) override; + const gfx::RectF& crop_rect, + bool enable_blend) override; bool IsOffscreen() override; gfx::VSyncProvider* GetVSyncProvider() override; bool SupportsPresentationCallback() override; 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 de661e983a7..aa68355c3a2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc @@ -10,7 +10,6 @@ #include <utility> #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/gfx/geometry/point.h" @@ -117,11 +116,6 @@ bool HardwareDisplayController::ActualSchedulePageFlip( [](const OverlayPlane& l, const OverlayPlane& r) { return l.z_order < r.z_order; }); - if (pending_planes.front().z_order < 0) { - std::move(callback).Run(gfx::SwapResult::SWAP_FAILED, - gfx::PresentationFeedback()); - return false; - } scoped_refptr<PageFlipRequest> page_flip_request = new PageFlipRequest(crtc_controllers_.size(), std::move(callback)); 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 afc1acf0d57..d31581b51a8 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 @@ -107,8 +107,8 @@ TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane2); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_TRUE(plane1.buffer->HasOneRef()); EXPECT_FALSE(plane2.buffer->HasOneRef()); @@ -144,8 +144,8 @@ TEST_F(HardwareDisplayControllerTest, CheckStateIfPageFlipFails) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane2); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); planes.clear(); @@ -162,7 +162,7 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) { ui::OverlayPlane plane2( scoped_refptr<ui::ScanoutBuffer>(new ui::MockScanoutBuffer(kOverlaySize)), 1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize), - gfx::RectF(kDefaultModeSizeF), base::kInvalidPlatformFile); + gfx::RectF(kDefaultModeSizeF), true, base::kInvalidPlatformFile); EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); @@ -171,8 +171,8 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) { planes.push_back(plane2); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); @@ -187,7 +187,7 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { ui::OverlayPlane plane2( scoped_refptr<ui::ScanoutBuffer>(new ui::MockScanoutBuffer(kOverlaySize)), 1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize), - gfx::RectF(kDefaultModeSizeF), base::kInvalidPlatformFile); + gfx::RectF(kDefaultModeSizeF), true, base::kInvalidPlatformFile); EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); @@ -196,8 +196,8 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { planes.push_back(plane2); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); // A test call shouldn't cause new flips, but should succeed. EXPECT_TRUE(controller_->TestPageFlip(planes)); @@ -209,8 +209,8 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { // Regular flips should continue on normally. controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(2, page_flips_); @@ -218,7 +218,7 @@ TEST_F(HardwareDisplayControllerTest, CheckOverlayTestMode) { EXPECT_EQ(2, drm_->get_overlay_flip_call_count()); } -TEST_F(HardwareDisplayControllerTest, RejectUnderlays) { +TEST_F(HardwareDisplayControllerTest, AcceptUnderlays) { ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>( new ui::MockScanoutBuffer(kDefaultModeSize)), base::kInvalidPlatformFile); @@ -226,7 +226,7 @@ TEST_F(HardwareDisplayControllerTest, RejectUnderlays) { scoped_refptr<ui::ScanoutBuffer>( new ui::MockScanoutBuffer(kDefaultModeSize)), -1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kDefaultModeSize), - gfx::RectF(kDefaultModeSizeF), base::kInvalidPlatformFile); + gfx::RectF(kDefaultModeSizeF), true, base::kInvalidPlatformFile); EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); @@ -235,9 +235,10 @@ TEST_F(HardwareDisplayControllerTest, RejectUnderlays) { planes.push_back(plane2); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); - EXPECT_EQ(gfx::SwapResult::SWAP_FAILED, last_swap_result_); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); + drm_->RunCallbacks(); + EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); } @@ -257,8 +258,8 @@ TEST_F(HardwareDisplayControllerTest, PageflipMirroredControllers) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane2); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); @@ -274,8 +275,8 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); @@ -297,8 +298,8 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterRemoveCrtc) { // Check that controller doesn't affect the state of removed plane in // subsequent page flip. controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); @@ -314,8 +315,8 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterDestroyingCrtc) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); @@ -346,8 +347,8 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); @@ -364,8 +365,8 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { hdc_controller.reset(new ui::HardwareDisplayController( controller_->RemoveCrtc(drm_, kPrimaryCrtc), controller_->origin())); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(2, page_flips_); @@ -378,8 +379,8 @@ TEST_F(HardwareDisplayControllerTest, PlaneStateAfterAddCrtc) { primary_crtc_plane->set_in_use(false); primary_crtc_plane->set_owning_crtc(0); hdc_controller->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(3, page_flips_); @@ -395,8 +396,8 @@ TEST_F(HardwareDisplayControllerTest, ModesetWhilePageFlipping) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); drm_->RunCallbacks(); @@ -414,8 +415,8 @@ TEST_F(HardwareDisplayControllerTest, FailPageFlipping) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_FAILED, last_swap_result_); @@ -427,13 +428,13 @@ TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlane) { new ui::MockScanoutBuffer(kDefaultModeSize)), 1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kDefaultModeSize), gfx::RectF(0, 0, 1, 1), - base::kInvalidPlatformFile); + true, base::kInvalidPlatformFile); EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); @@ -448,8 +449,8 @@ TEST_F(HardwareDisplayControllerTest, AddCrtcMidPageFlip) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); controller_->AddCrtc(std::unique_ptr<ui::CrtcController>( new ui::CrtcController(drm_.get(), kSecondaryCrtc, kSecondaryConnector))); @@ -467,8 +468,8 @@ TEST_F(HardwareDisplayControllerTest, RemoveCrtcMidPageFlip) { std::vector<ui::OverlayPlane> planes = std::vector<ui::OverlayPlane>(1, plane1); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); controller_->RemoveCrtc(drm_, kPrimaryCrtc); @@ -486,15 +487,15 @@ TEST_F(HardwareDisplayControllerTest, Disable) { ui::OverlayPlane plane2(new ui::MockScanoutBuffer(kOverlaySize), 1, gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize), - gfx::RectF(kDefaultModeSizeF), + gfx::RectF(kDefaultModeSizeF), true, base::kInvalidPlatformFile); std::vector<ui::OverlayPlane> planes; planes.push_back(plane1); planes.push_back(plane2); controller_->SchedulePageFlip( - planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, - base::Unretained(this))); + planes, base::BindOnce(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); drm_->RunCallbacks(); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h index bf7ddde34f3..952c6f93e0d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h @@ -25,13 +25,13 @@ class HardwareDisplayPlaneAtomic : public HardwareDisplayPlane { HardwareDisplayPlaneAtomic(uint32_t plane_id, uint32_t possible_crtcs); ~HardwareDisplayPlaneAtomic() override; - bool SetPlaneData(drmModeAtomicReq* property_set, - uint32_t crtc_id, - uint32_t framebuffer, - const gfx::Rect& crtc_rect, - const gfx::Rect& src_rect, - const gfx::OverlayTransform transform, - int in_fence_fd); + virtual bool SetPlaneData(drmModeAtomicReq* property_set, + uint32_t crtc_id, + uint32_t framebuffer, + const gfx::Rect& crtc_rect, + const gfx::Rect& src_rect, + const gfx::OverlayTransform transform, + int in_fence_fd); void set_crtc(CrtcController* crtc) { crtc_ = crtc; } CrtcController* crtc() const { return crtc_; } 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 b74520781ae..42446c9316d 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 @@ -214,7 +214,10 @@ bool HardwareDisplayPlaneManager::IsCompatible(HardwareDisplayPlane* plane, if (!plane->CanUseForCrtc(crtc_index)) return false; - if (!plane->IsSupportedFormat(overlay.buffer->GetFramebufferPixelFormat())) + const uint32_t format = overlay.enable_blend ? + overlay.buffer->GetFramebufferPixelFormat() : + overlay.buffer->GetOpaqueFramebufferPixelFormat(); + if (!plane->IsSupportedFormat(format)) return false; // TODO(kalyank): We should check for z-order and any needed transformation 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 f49d7cbd611..134c40ee9d9 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 @@ -137,9 +137,9 @@ class HardwareDisplayPlaneManager { // Returns true if |plane| can support |overlay| and compatible with // |crtc_index|. - bool IsCompatible(HardwareDisplayPlane* plane, - const OverlayPlane& overlay, - uint32_t crtc_index) const; + virtual bool IsCompatible(HardwareDisplayPlane* plane, + const OverlayPlane& overlay, + uint32_t crtc_index) const; void ResetCurrentPlaneList(HardwareDisplayPlaneList* plane_list) const; 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 febc9945b23..a2b7170f630 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 @@ -142,7 +142,7 @@ bool HardwareDisplayPlaneManagerAtomic::SetPlaneData( CrtcController* crtc) { HardwareDisplayPlaneAtomic* atomic_plane = static_cast<HardwareDisplayPlaneAtomic*>(hw_plane); - uint32_t framebuffer_id = overlay.z_order + uint32_t framebuffer_id = overlay.enable_blend ? overlay.buffer->GetFramebufferId() : overlay.buffer->GetOpaqueFramebufferId(); if (!atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(), 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 7dd8b19f94b..46619c52f9a 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 @@ -27,8 +27,6 @@ class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { void RequestPlanesReadyCallback(const OverlayPlaneList& planes, base::OnceClosure callback) override; - - private: bool SetPlaneData(HardwareDisplayPlaneList* plane_list, HardwareDisplayPlane* hw_plane, const OverlayPlane& overlay, @@ -36,6 +34,7 @@ class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { const gfx::Rect& src_rect, CrtcController* crtc) override; + private: std::unique_ptr<HardwareDisplayPlane> CreatePlane( uint32_t plane_id, uint32_t possible_crtcs) 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 8aaf4e280d2..a87020d2c4d 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 @@ -167,10 +167,23 @@ bool HardwareDisplayPlaneManagerLegacy::SetPlaneData( } else { plane_list->legacy_page_flips.back().planes.push_back( HardwareDisplayPlaneList::PageFlipInfo::Plane( - hw_plane->plane_id(), overlay.buffer->GetFramebufferId(), + hw_plane->plane_id(), overlay.buffer->GetOpaqueFramebufferId(), overlay.display_bounds, src_rect)); } return true; } +bool HardwareDisplayPlaneManagerLegacy::IsCompatible( + HardwareDisplayPlane* plane, + const OverlayPlane& overlay, + uint32_t crtc_index) const { + if (!plane->CanUseForCrtc(crtc_index)) + return false; + + // When using legacy kms we always scanout only one plane (the primary), + // and we always use the opaque fb. Refer to SetPlaneData above. + const uint32_t format = overlay.buffer->GetOpaqueFramebufferPixelFormat(); + return plane->IsSupportedFormat(format); +} + } // namespace ui 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 3f492ca1d37..db1d78a4b07 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 @@ -35,6 +35,9 @@ class HardwareDisplayPlaneManagerLegacy : public HardwareDisplayPlaneManager { uint32_t crtc_id, const gfx::Rect& src_rect, CrtcController* crtc) override; + bool IsCompatible(HardwareDisplayPlane* plane, + const OverlayPlane& overlay, + uint32_t crtc_index) const override; private: DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneManagerLegacy); 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 3c5f7146d96..46f2d563fde 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 @@ -15,6 +15,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/ozone/platform/drm/gpu/crtc_controller.h" #include "ui/ozone/platform/drm/gpu/fake_plane_info.h" +#include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h" #include "ui/ozone/platform/drm/gpu/mock_drm_device.h" @@ -362,4 +363,44 @@ TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, EXPECT_TRUE(callback_called); } +class HardwareDisplayPlaneAtomicMock : public ui::HardwareDisplayPlaneAtomic { + public: + HardwareDisplayPlaneAtomicMock() : ui::HardwareDisplayPlaneAtomic(0, ~0) {} + ~HardwareDisplayPlaneAtomicMock() override {} + + bool SetPlaneData(drmModeAtomicReq* property_set, + uint32_t crtc_id, + uint32_t framebuffer, + const gfx::Rect& crtc_rect, + const gfx::Rect& src_rect, + const gfx::OverlayTransform transform, + int in_fence_fd) override { + framebuffer_ = framebuffer; + return true; + } + uint32_t framebuffer() const { return framebuffer_; } + + private: + uint32_t framebuffer_ = 0; +}; + +TEST(HardwareDisplayPlaneManagerAtomic, EnableBlend) { + auto plane_manager = + std::make_unique<ui::HardwareDisplayPlaneManagerAtomic>(); + ui::HardwareDisplayPlaneList plane_list; + HardwareDisplayPlaneAtomicMock hw_plane; + scoped_refptr<ui::ScanoutBuffer> buffer = + new ui::MockScanoutBuffer(kDefaultBufferSize); + ui::OverlayPlane overlay(buffer, base::kInvalidPlatformFile); + overlay.enable_blend = true; + plane_manager->SetPlaneData(&plane_list, &hw_plane, overlay, 1, gfx::Rect(), + nullptr); + EXPECT_EQ(hw_plane.framebuffer(), buffer->GetFramebufferId()); + + overlay.enable_blend = false; + plane_manager->SetPlaneData(&plane_list, &hw_plane, overlay, 1, gfx::Rect(), + nullptr); + EXPECT_EQ(hw_plane.framebuffer(), buffer->GetOpaqueFramebufferId()); +} + } // namespace diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.cc index fd15615ecd6..1230e86011f 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.cc @@ -21,6 +21,7 @@ MockScanoutBuffer::MockScanoutBuffer(const gfx::Size& size, format_(format), modifier_(modifier), id_(g_current_framebuffer_id++), + opaque_id_(g_current_framebuffer_id++), drm_(drm) {} MockScanoutBuffer::~MockScanoutBuffer() {} @@ -30,7 +31,7 @@ uint32_t MockScanoutBuffer::GetFramebufferId() const { } uint32_t MockScanoutBuffer::GetOpaqueFramebufferId() const { - return 2; + return opaque_id_; } uint32_t MockScanoutBuffer::GetHandle() const { diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.h b/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.h index 33f5977d776..cbeb256d5b4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/mock_scanout_buffer.h @@ -38,6 +38,7 @@ class MockScanoutBuffer : public ScanoutBuffer { uint32_t format_; uint64_t modifier_; uint32_t id_; + uint32_t opaque_id_; scoped_refptr<DrmDevice> drm_; DISALLOW_COPY_AND_ASSIGN(MockScanoutBuffer); diff --git a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc index 8135647550e..8cf1be09bf9 100644 --- a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc +++ b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.cc @@ -16,6 +16,7 @@ OverlayPlane::OverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer, plane_transform(gfx::OVERLAY_TRANSFORM_NONE), display_bounds(gfx::Point(), buffer->GetSize()), crop_rect(0, 0, 1, 1), + enable_blend(false), fence_fd(fence_fd) {} OverlayPlane::OverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer, @@ -23,12 +24,14 @@ OverlayPlane::OverlayPlane(const scoped_refptr<ScanoutBuffer>& buffer, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, const gfx::RectF& crop_rect, + bool enable_blend, int fence_fd) : buffer(buffer), z_order(z_order), plane_transform(plane_transform), display_bounds(display_bounds), crop_rect(crop_rect), + enable_blend(enable_blend), fence_fd(fence_fd) {} OverlayPlane::OverlayPlane(const OverlayPlane& other) = default; diff --git a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h index 4bfb9b4738c..31fa65d49e6 100644 --- a/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h +++ b/chromium/ui/ozone/platform/drm/gpu/overlay_plane.h @@ -30,6 +30,7 @@ struct OverlayPlane { gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, const gfx::RectF& crop_rect, + bool enable_blend, int fence_fd); OverlayPlane(const OverlayPlane& other); @@ -45,6 +46,7 @@ struct OverlayPlane { gfx::OverlayTransform plane_transform; gfx::Rect display_bounds; gfx::RectF crop_rect; + bool enable_blend; int fence_fd; }; diff --git a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc index 60bc23f75ea..863e96531c4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc +++ b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.cc @@ -24,7 +24,7 @@ void PostSyncTask( base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); bool success = task_runner->PostTask( - FROM_HERE, base::Bind(OnRunPostedTaskAndSignal, callback, &wait)); + FROM_HERE, base::BindOnce(OnRunPostedTaskAndSignal, callback, &wait)); if (success) wait.Wait(); } diff --git a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h index 086709c9a01..a1393ef3dfa 100644 --- a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h +++ b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers.h @@ -19,7 +19,7 @@ void PostAsyncTask( const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const base::Callback<void(Args...)>& callback, Args... args) { - task_runner->PostTask(FROM_HERE, base::Bind(callback, args...)); + task_runner->PostTask(FROM_HERE, base::BindOnce(callback, args...)); } template <typename... Args> diff --git a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers_unittest.cc index 46e5223c7d6..5dbc2853518 100644 --- a/chromium/ui/ozone/platform/drm/gpu/proxy_helpers_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/proxy_helpers_unittest.cc @@ -29,8 +29,8 @@ class ProxyHelpersTest : public testing::Test { EXPECT_TRUE(drm_checker_.CalledOnValidThread()); message_loop_.task_runner()->PostTask( - FROM_HERE, base::Bind(&ProxyHelpersTest::QuitFunctionCallback, - base::Unretained(this), 8)); + FROM_HERE, base::BindOnce(&ProxyHelpersTest::QuitFunctionCallback, + base::Unretained(this), 8)); } // QuitFunctionCallback runs on the main thread. @@ -106,7 +106,7 @@ TEST_F(ProxyHelpersTest, PostTask) { // Binds the thread checker on the drm thread. drm_thread_->task_runner()->PostTask( FROM_HERE, - base::Bind(&ProxyHelpersTest::SetDrmChecker, base::Unretained(this))); + base::BindOnce(&ProxyHelpersTest::SetDrmChecker, base::Unretained(this))); // Test passing a type by value. auto value_callback = base::BindOnce(&ProxyHelpersTest::ValueTypeCallback, @@ -147,8 +147,8 @@ TEST_F(ProxyHelpersTest, PostTask) { // Shutdown the RunLoop. drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::Bind(&ProxyHelpersTest::QuitFunction, base::Unretained(this), 42)); + FROM_HERE, base::BindOnce(&ProxyHelpersTest::QuitFunction, + base::Unretained(this), 42)); run_loop_.Run(); diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc index 87b2abc6f37..e8e477733e4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc @@ -9,7 +9,6 @@ #include <utility> #include "base/files/platform_file.h" -#include "base/memory/ptr_util.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/display/types/display_snapshot.h" #include "ui/gfx/geometry/point.h" @@ -32,9 +31,9 @@ namespace { void FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, HardwareDisplayController* controller, ScanoutBuffer* buffer) { - DrmConsoleBuffer modeset_buffer(drm, buffer->GetFramebufferId()); + DrmConsoleBuffer modeset_buffer(drm, buffer->GetOpaqueFramebufferId()); if (!modeset_buffer.Initialize()) { - VLOG(2) << "Failed to grab framebuffer " << buffer->GetFramebufferId(); + VLOG(2) << "Failed to grab framebuffer " << buffer->GetOpaqueFramebufferId(); return; } @@ -378,7 +377,8 @@ OverlayPlane ScreenManager::GetModesetBuffer( if (!buffer) { LOG(ERROR) << "Failed to create scanout buffer"; return OverlayPlane(nullptr, 0, gfx::OVERLAY_TRANSFORM_INVALID, gfx::Rect(), - gfx::RectF(), base::kInvalidPlatformFile); + gfx::RectF(), /* enable_blend */ true, + base::kInvalidPlatformFile); } FillModesetBuffer(drm, controller, buffer.get()); 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 2fe14daad7d..a50a0ac275a 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc @@ -511,7 +511,7 @@ TEST_F(ScreenManagerTest, EnableControllerWhenWindowHasBuffer) { drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), kDefaultMode); - EXPECT_EQ(buffer->GetFramebufferId(), drm_->current_framebuffer()); + EXPECT_EQ(buffer->GetOpaqueFramebufferId(), drm_->current_framebuffer()); window = screen_manager_->RemoveWindow(1); window->Shutdown(); diff --git a/chromium/ui/ozone/platform/drm/host/drm_cursor.cc b/chromium/ui/ozone/platform/drm/host/drm_cursor.cc index 0042294556f..8ffc06b4c05 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_cursor.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_cursor.cc @@ -226,6 +226,9 @@ void DrmCursor::SetCursorLocationLocked(const gfx::PointF& location) { gfx::PointF(confined_bounds_.right() - 1, confined_bounds_.bottom() - 1)); location_ = clamped_location; +#if defined(OS_CHROMEOS) + ui::CursorController::GetInstance()->SetCursorLocation(location_); +#endif } void DrmCursor::SendCursorShowLocked() { diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc index 9868a4e3719..d486445a005 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/location.h" -#include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/display/types/display_mode.h" #include "ui/display/types/display_snapshot.h" @@ -56,7 +55,7 @@ void DrmDisplayHost::Configure(const display::DisplayMode* mode, void DrmDisplayHost::OnDisplayConfigured(bool status) { if (!configure_callback_.is_null()) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(configure_callback_, status)); + FROM_HERE, base::BindOnce(configure_callback_, status)); } else { LOG(ERROR) << "Got unexpected event for display " << snapshot_->display_id(); @@ -76,7 +75,7 @@ void DrmDisplayHost::OnHDCPStateReceived(bool status, display::HDCPState state) { if (!get_hdcp_callback_.is_null()) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(get_hdcp_callback_, status, state)); + FROM_HERE, base::BindOnce(get_hdcp_callback_, status, state)); } else { LOG(ERROR) << "Got unexpected event for display " << snapshot_->display_id(); @@ -96,7 +95,7 @@ void DrmDisplayHost::SetHDCPState( void DrmDisplayHost::OnHDCPStateUpdated(bool status) { if (!set_hdcp_callback_.is_null()) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(set_hdcp_callback_, status)); + FROM_HERE, base::BindOnce(set_hdcp_callback_, status)); } else { LOG(ERROR) << "Got unexpected event for display " << snapshot_->display_id(); diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc index c3f28c7b473..31967c98427 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.cc @@ -12,7 +12,6 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "base/task_scheduler/post_task.h" #include "base/threading/thread_restrictions.h" @@ -60,9 +59,9 @@ void OpenDeviceAsync(const base::FilePath& device_path, std::unique_ptr<DrmDeviceHandle> handle(new DrmDeviceHandle()); handle->Initialize(device_path, sys_path); - reply_runner->PostTask(FROM_HERE, - base::Bind(callback, device_path, sys_path, - base::Passed(std::move(handle)))); + reply_runner->PostTask( + FROM_HERE, + base::BindOnce(callback, device_path, sys_path, std::move(handle))); } base::FilePath GetPrimaryDisplayCardPath() { @@ -253,18 +252,19 @@ void DrmDisplayHostManager::ProcessEvent() { FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, - base::Bind(&OpenDeviceAsync, event.path, - base::ThreadTaskRunnerHandle::Get(), - base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice, - weak_ptr_factory_.GetWeakPtr()))); + base::BindOnce( + &OpenDeviceAsync, event.path, + base::ThreadTaskRunnerHandle::Get(), + base::Bind(&DrmDisplayHostManager::OnAddGraphicsDevice, + weak_ptr_factory_.GetWeakPtr()))); task_pending_ = true; } break; case DeviceEvent::CHANGE: task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&DrmDisplayHostManager::OnUpdateGraphicsDevice, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&DrmDisplayHostManager::OnUpdateGraphicsDevice, + weak_ptr_factory_.GetWeakPtr())); break; case DeviceEvent::REMOVE: DCHECK(event.path != primary_graphics_card_path_) @@ -273,8 +273,8 @@ void DrmDisplayHostManager::ProcessEvent() { if (it != drm_devices_.end()) { task_pending_ = base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&DrmDisplayHostManager::OnRemoveGraphicsDevice, - weak_ptr_factory_.GetWeakPtr(), it->second)); + base::BindOnce(&DrmDisplayHostManager::OnRemoveGraphicsDevice, + weak_ptr_factory_.GetWeakPtr(), it->second)); drm_devices_.erase(it); } break; @@ -321,7 +321,7 @@ void DrmDisplayHostManager::OnGpuProcessLaunched() { MapDevPathToSysPath(primary_graphics_card_path_); if (!handle) { - handle.reset(new DrmDeviceHandle()); + handle = std::make_unique<DrmDeviceHandle>(); if (!handle->Initialize(primary_graphics_card_path_, drm_devices_[primary_graphics_card_path_])) LOG(FATAL) << "Failed to open primary graphics card"; @@ -330,8 +330,10 @@ void DrmDisplayHostManager::OnGpuProcessLaunched() { // Send the primary device first since this is used to initialize graphics // state. - proxy_->GpuAddGraphicsDevice(drm_devices_[primary_graphics_card_path_], - handle->PassFD()); + if (!proxy_->GpuAddGraphicsDevice(drm_devices_[primary_graphics_card_path_], + handle->PassFD())) { + LOG(ERROR) << "Failed to add primary graphics device."; + } } void DrmDisplayHostManager::OnGpuThreadReady() { @@ -343,8 +345,8 @@ void DrmDisplayHostManager::OnGpuThreadReady() { if (!get_displays_callback_.is_null()) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback, - weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); + base::BindOnce(&DrmDisplayHostManager::RunUpdateDisplaysCallback, + weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); get_displays_callback_.Reset(); } @@ -384,8 +386,8 @@ void DrmDisplayHostManager::GpuHasUpdatedNativeDisplays( if (!get_displays_callback_.is_null()) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&DrmDisplayHostManager::RunUpdateDisplaysCallback, - weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); + base::BindOnce(&DrmDisplayHostManager::RunUpdateDisplaysCallback, + weak_ptr_factory_.GetWeakPtr(), get_displays_callback_)); get_displays_callback_.Reset(); } } @@ -435,7 +437,7 @@ void DrmDisplayHostManager::GpuTookDisplayControl(bool status) { } base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(take_display_control_callback_, status)); + FROM_HERE, base::BindOnce(take_display_control_callback_, status)); take_display_control_callback_.Reset(); display_control_change_pending_ = false; } @@ -455,7 +457,7 @@ void DrmDisplayHostManager::GpuRelinquishedDisplayControl(bool status) { } base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(relinquish_display_control_callback_, status)); + FROM_HERE, base::BindOnce(relinquish_display_control_callback_, status)); relinquish_display_control_callback_.Reset(); display_control_change_pending_ = false; } diff --git a/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc b/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc index 6c7f4f4b510..09632b10933 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.cc @@ -74,8 +74,8 @@ void CursorIPC::Move(gfx::AcceleratedWidget window, const gfx::Point& point) { void CursorIPC::InitializeOnEvdevIfNecessary() {} void CursorIPC::Send(IPC::Message* message) { - if (IsConnected() && - send_runner_->PostTask(FROM_HERE, base::Bind(send_callback_, message))) + if (IsConnected() && send_runner_->PostTask( + FROM_HERE, base::BindOnce(send_callback_, message))) return; // Drop disconnected updates. DrmWindowHost will call @@ -115,7 +115,8 @@ void DrmGpuPlatformSupportHost::RemoveGpuThreadObserver( } bool DrmGpuPlatformSupportHost::IsConnected() { - return host_id_ >= 0 && channel_established_; + base::AutoLock auto_lock(host_id_lock_); + return host_id_ >= 0; } void DrmGpuPlatformSupportHost::OnGpuServiceLaunched( @@ -139,27 +140,23 @@ void DrmGpuPlatformSupportHost::OnGpuProcessLaunched( DCHECK(!ui_runner_->BelongsToCurrentThread()); TRACE_EVENT1("drm", "DrmGpuPlatformSupportHost::OnGpuProcessLaunched", "host_id", host_id); - host_id_ = host_id; - send_runner_ = std::move(send_runner); - send_callback_ = send_callback; - - for (GpuThreadObserver& observer : gpu_thread_observers_) - observer.OnGpuProcessLaunched(); ui_runner_->PostTask( FROM_HERE, base::BindOnce(&DrmGpuPlatformSupportHost::OnChannelEstablished, - weak_ptr_)); + weak_ptr_, host_id, std::move(send_runner), + std::move(send_callback))); } void DrmGpuPlatformSupportHost::OnChannelDestroyed(int host_id) { TRACE_EVENT1("drm", "DrmGpuPlatformSupportHost::OnChannelDestroyed", "host_id", host_id); - if (host_id_ == host_id) { + { + base::AutoLock auto_lock(host_id_lock_); + host_id_ = -1; + } cursor_->ResetDrmCursorProxy(); - host_id_ = -1; - channel_established_ = false; send_runner_ = nullptr; send_callback_.Reset(); for (GpuThreadObserver& observer : gpu_thread_observers_) @@ -182,8 +179,8 @@ void DrmGpuPlatformSupportHost::OnMessageReceived(const IPC::Message& message) { } bool DrmGpuPlatformSupportHost::Send(IPC::Message* message) { - if (IsConnected() && - send_runner_->PostTask(FROM_HERE, base::Bind(send_callback_, message))) + if (IsConnected() && send_runner_->PostTask( + FROM_HERE, base::BindOnce(send_callback_, message))) return true; delete message; @@ -200,9 +197,22 @@ void DrmGpuPlatformSupportHost::UnRegisterHandlerForDrmDisplayHostManager() { display_manager_ = nullptr; } -void DrmGpuPlatformSupportHost::OnChannelEstablished() { +void DrmGpuPlatformSupportHost::OnChannelEstablished( + int host_id, + scoped_refptr<base::SingleThreadTaskRunner> send_runner, + const base::Callback<void(IPC::Message*)>& send_callback) { + DCHECK(ui_runner_->BelongsToCurrentThread()); TRACE_EVENT0("drm", "DrmGpuPlatformSupportHost::OnChannelEstablished"); - channel_established_ = true; + + send_runner_ = std::move(send_runner); + send_callback_ = send_callback; + { + base::AutoLock auto_lock(host_id_lock_); + host_id_ = host_id; + } + + for (GpuThreadObserver& observer : gpu_thread_observers_) + observer.OnGpuProcessLaunched(); for (GpuThreadObserver& observer : gpu_thread_observers_) observer.OnGpuThreadReady(); @@ -278,22 +288,8 @@ bool DrmGpuPlatformSupportHost::GpuRelinquishDisplayControl() { bool DrmGpuPlatformSupportHost::GpuAddGraphicsDevice(const base::FilePath& path, base::ScopedFD fd) { - IPC::Message* message = new OzoneGpuMsg_AddGraphicsDevice( - path, base::FileDescriptor(std::move(fd))); - - // This function may be called from two places: - // - DrmDisplayHostManager::OnGpuProcessLaunched() invoked synchronously - // by GpuProcessHost::Init() on IO thread, which is the same thread as - // |send_runner_|. In this case we can synchronously send the IPC; - // - DrmDisplayHostManager::OnAddGraphicsDevice() on UI thread. In this - // case we need to post the send task to IO thread. - if (send_runner_ && send_runner_->BelongsToCurrentThread()) { - DCHECK(!send_callback_.is_null()); - send_callback_.Run(message); - return true; - } - - return Send(message); + return Send(new OzoneGpuMsg_AddGraphicsDevice( + path, base::FileDescriptor(std::move(fd)))); } bool DrmGpuPlatformSupportHost::GpuRemoveGraphicsDevice( 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 ebcb9d5ea0d..4f0f918fdff 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 @@ -10,6 +10,7 @@ #include "base/callback.h" #include "base/observer_list.h" #include "base/single_thread_task_runner.h" +#include "base/synchronization/lock.h" #include "ui/display/types/display_constants.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" @@ -99,7 +100,10 @@ class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost, const gfx::Rect& bounds) override; private: - void OnChannelEstablished(); + void OnChannelEstablished( + int host_id, + scoped_refptr<base::SingleThreadTaskRunner> send_runner, + const base::Callback<void(IPC::Message*)>& send_callback); bool OnMessageReceivedForDrmDisplayHostManager(const IPC::Message& message); void OnUpdateNativeDisplays( const std::vector<DisplaySnapshot_Params>& displays); @@ -117,7 +121,7 @@ class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost, const std::vector<OverlayCheckReturn_Params>& returns); int host_id_ = -1; - bool channel_established_ = false; + base::Lock host_id_lock_; scoped_refptr<base::SingleThreadTaskRunner> ui_runner_; scoped_refptr<base::SingleThreadTaskRunner> send_runner_; diff --git a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc index dc0f75a1f9e..4e8aae39f35 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc @@ -9,7 +9,6 @@ #include <algorithm> #include "base/command_line.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/ozone/platform/drm/common/drm_util.h" @@ -61,11 +60,6 @@ void DrmOverlayManager::CheckOverlaySupport( continue; } - // Compositor doesn't have information about the total size of primary - // candidate. We get this information from display rect. - if (candidate.plane_z_order == 0) - candidate.buffer_size = gfx::ToNearestRect(candidate.display_rect).size(); - result_candidates.push_back(OverlaySurfaceCandidate(candidate)); // Start out hoping that we can have an overlay. result_candidates.back().overlay_handled = true; diff --git a/chromium/ui/ozone/platform/drm/host/drm_window_host.cc b/chromium/ui/ozone/platform/drm/host/drm_window_host.cc index 3df6467ad0c..c3470c283c2 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_window_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_window_host.cc @@ -97,6 +97,10 @@ void DrmWindowHost::ReleaseCapture() { window_manager_->UngrabEvents(widget_); } +bool DrmWindowHost::HasCapture() const { + return widget_ == window_manager_->event_grabber(); +} + void DrmWindowHost::ToggleFullscreen() { } @@ -129,9 +133,8 @@ PlatformImeController* DrmWindowHost::GetPlatformImeController() { return nullptr; } -bool DrmWindowHost::CanDispatchEvent(const PlatformEvent& ne) { - DCHECK(ne); - Event* event = static_cast<Event*>(ne); +bool DrmWindowHost::CanDispatchEvent(const PlatformEvent& event) { + DCHECK(event); // If there is a grab, capture events here. gfx::AcceleratedWidget grabber = window_manager_->event_grabber(); @@ -170,10 +173,9 @@ bool DrmWindowHost::CanDispatchEvent(const PlatformEvent& ne) { return true; } -uint32_t DrmWindowHost::DispatchEvent(const PlatformEvent& native_event) { - DCHECK(native_event); +uint32_t DrmWindowHost::DispatchEvent(const PlatformEvent& event) { + DCHECK(event); - Event* event = static_cast<Event*>(native_event); if (event->IsLocatedEvent()) { // Make the event location relative to this window's origin. LocatedEvent* located_event = event->AsLocatedEvent(); @@ -183,8 +185,8 @@ uint32_t DrmWindowHost::DispatchEvent(const PlatformEvent& native_event) { located_event->set_root_location_f(location); } DispatchEventFromNativeUiEvent( - native_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent, - base::Unretained(delegate_))); + event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent, + base::Unretained(delegate_))); return POST_DISPATCH_STOP_PROPAGATION; } 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 01f0ccefce2..1b17b20c8c5 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_window_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_window_host.h @@ -67,6 +67,7 @@ class DrmWindowHost : public PlatformWindow, void SetTitle(const base::string16& title) override; void SetCapture() override; void ReleaseCapture() override; + bool HasCapture() const override; void ToggleFullscreen() override; void Maximize() override; void Minimize() override; diff --git a/chromium/ui/ozone/platform/drm/host/gpu_thread_observer.h b/chromium/ui/ozone/platform/drm/host/gpu_thread_observer.h index ed6da90beac..24b5b19831e 100644 --- a/chromium/ui/ozone/platform/drm/host/gpu_thread_observer.h +++ b/chromium/ui/ozone/platform/drm/host/gpu_thread_observer.h @@ -7,20 +7,19 @@ namespace ui { -// Observes the channel state. +// Observes the channel state. All calls should happen on the same thread that +// OzonePlatform::InitializeForUI() is called on. This can be the browser UI +// thread or the WS thread for mus/mash. class GpuThreadObserver { public: virtual ~GpuThreadObserver() {} // Called when the GPU process is launched. - // This is called from browser IO thread. virtual void OnGpuProcessLaunched() = 0; // Called when a GPU thread implementation has become available. - // This is called from browser UI thread. virtual void OnGpuThreadReady() = 0; // Called when the GPU thread implementation has ceased to be // available. - // This is called from browser UI thread. virtual void OnGpuThreadRetired() = 0; }; diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc index 963f77e8804..a6e1286c2a7 100644 --- a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc +++ b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc @@ -14,7 +14,6 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_task_runner_handle.h" diff --git a/chromium/ui/ozone/platform/headless/BUILD.gn b/chromium/ui/ozone/platform/headless/BUILD.gn index 6de57300b13..0637f8e6f01 100644 --- a/chromium/ui/ozone/platform/headless/BUILD.gn +++ b/chromium/ui/ozone/platform/headless/BUILD.gn @@ -20,6 +20,8 @@ source_set("headless") { "ozone_platform_headless.h", ] + defines = [ "OZONE_IMPLEMENTATION" ] + deps = [ "//base", "//skia", diff --git a/chromium/ui/ozone/platform/headless/gl_surface_osmesa_png.cc b/chromium/ui/ozone/platform/headless/gl_surface_osmesa_png.cc index 8700136a9f7..2bbb7a86b7e 100644 --- a/chromium/ui/ozone/platform/headless/gl_surface_osmesa_png.cc +++ b/chromium/ui/ozone/platform/headless/gl_surface_osmesa_png.cc @@ -67,7 +67,7 @@ void GLSurfaceOSMesaPng::WriteBufferToPng() { base::PostTaskWithTraits( FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, - base::Bind(&WritePngToFile, output_path_, base::Passed(&png_data))); + base::BindOnce(&WritePngToFile, output_path_, std::move(png_data))); } } diff --git a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc index 6e64f51c583..7bfe64670d3 100644 --- a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc +++ b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc @@ -8,7 +8,6 @@ #include "base/files/file_util.h" #include "base/location.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/task_scheduler/post_task.h" #include "build/build_config.h" @@ -28,6 +27,8 @@ namespace ui { namespace { +const base::FilePath::CharType kDevNull[] = FILE_PATH_LITERAL("/dev/null"); + void WriteDataToFile(const base::FilePath& location, const SkBitmap& bitmap) { DCHECK(!location.empty()); std::vector<unsigned char> png_data; @@ -60,7 +61,7 @@ class FileSurface : public SurfaceOzoneCanvas { base::PostTaskWithTraits( FROM_HERE, {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, - base::Bind(&WriteDataToFile, base_path_, bitmap)); + base::BindOnce(&WriteDataToFile, base_path_, bitmap)); } } std::unique_ptr<gfx::VSyncProvider> CreateVSyncProvider() override { @@ -90,7 +91,8 @@ class TestPixmap : public gfx::NativePixmap { int plane_z_order, gfx::OverlayTransform plane_transform, const gfx::Rect& display_bounds, - const gfx::RectF& crop_rect) override { + const gfx::RectF& crop_rect, + bool enable_blend) override { return true; } gfx::NativePixmapHandle ExportHandle() override { @@ -137,7 +139,7 @@ HeadlessSurfaceFactory::~HeadlessSurfaceFactory() = default; base::FilePath HeadlessSurfaceFactory::GetPathForWidget( gfx::AcceleratedWidget widget) { - if (base_path_.empty() || base_path_ == base::FilePath("/dev/null")) + if (base_path_.empty() || base_path_ == base::FilePath(kDevNull)) return base_path_; // Disambiguate multiple window output files with the window id. @@ -177,7 +179,7 @@ void HeadlessSurfaceFactory::CheckBasePath() const { return; if (!DirectoryExists(base_path_) && !base::CreateDirectory(base_path_) && - base_path_ != base::FilePath("/dev/null")) + base_path_ != base::FilePath(kDevNull)) PLOG(FATAL) << "Unable to create output directory"; if (!base::PathIsWritable(base_path_)) diff --git a/chromium/ui/ozone/platform/headless/headless_window.cc b/chromium/ui/ozone/platform/headless/headless_window.cc index 3608ffa4255..5bf6e7c9653 100644 --- a/chromium/ui/ozone/platform/headless/headless_window.cc +++ b/chromium/ui/ozone/platform/headless/headless_window.cc @@ -6,6 +6,7 @@ #include <string> +#include "build/build_config.h" #include "ui/events/platform/platform_event_source.h" #include "ui/ozone/platform/headless/headless_window_manager.h" #include "ui/platform_window/platform_window_delegate.h" @@ -16,12 +17,20 @@ HeadlessWindow::HeadlessWindow(PlatformWindowDelegate* delegate, HeadlessWindowManager* manager, const gfx::Rect& bounds) : delegate_(delegate), manager_(manager), bounds_(bounds) { +#if defined(OS_WIN) + widget_ = reinterpret_cast<gfx::AcceleratedWidget>(manager_->AddWindow(this)); +#else widget_ = manager_->AddWindow(this); +#endif delegate_->OnAcceleratedWidgetAvailable(widget_, 1.f); } HeadlessWindow::~HeadlessWindow() { +#if defined(OS_WIN) + manager_->RemoveWindow(reinterpret_cast<uint64_t>(widget_), this); +#else manager_->RemoveWindow(widget_, this); +#endif } gfx::Rect HeadlessWindow::GetBounds() { @@ -47,6 +56,10 @@ void HeadlessWindow::SetCapture() {} void HeadlessWindow::ReleaseCapture() {} +bool HeadlessWindow::HasCapture() const { + return false; +} + void HeadlessWindow::ToggleFullscreen() {} void HeadlessWindow::Maximize() {} diff --git a/chromium/ui/ozone/platform/headless/headless_window.h b/chromium/ui/ozone/platform/headless/headless_window.h index 4e909cf6e4e..2051d143cb9 100644 --- a/chromium/ui/ozone/platform/headless/headless_window.h +++ b/chromium/ui/ozone/platform/headless/headless_window.h @@ -32,6 +32,7 @@ class HeadlessWindow : public PlatformWindow { void PrepareForShutdown() override; void SetCapture() override; void ReleaseCapture() override; + bool HasCapture() const override; void ToggleFullscreen() override; void Maximize() override; void Minimize() override; diff --git a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc index 8c569f49131..e6935b79eaf 100644 --- a/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc +++ b/chromium/ui/ozone/platform/headless/ozone_platform_headless.cc @@ -7,7 +7,6 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/macros.h" -#include "base/memory/ptr_util.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" @@ -31,7 +30,7 @@ namespace { // A headless implementation of PlatformEventSource that we can instantiate to // make // sure that the PlatformEventSource has an instance while in unit tests. -class HeadlessPlatformEventSource : public ui::PlatformEventSource { +class HeadlessPlatformEventSource : public PlatformEventSource { public: HeadlessPlatformEventSource() = default; ~HeadlessPlatformEventSource() override = default; diff --git a/chromium/ui/ozone/platform/wayland/fake_server.cc b/chromium/ui/ozone/platform/wayland/fake_server.cc index 7adfe81029e..84c6594cb95 100644 --- a/chromium/ui/ozone/platform/wayland/fake_server.cc +++ b/chromium/ui/ozone/platform/wayland/fake_server.cc @@ -23,6 +23,11 @@ const uint32_t kOutputVersion = 2; const uint32_t kSeatVersion = 4; const uint32_t kXdgShellVersion = 1; +template <class T> +T* GetUserDataAs(wl_resource* resource) { + return static_cast<T*>(wl_resource_get_user_data(resource)); +} + void DestroyResource(wl_client* client, wl_resource* resource) { wl_resource_destroy(resource); } @@ -30,8 +35,7 @@ void DestroyResource(wl_client* client, wl_resource* resource) { // wl_compositor void CreateSurface(wl_client* client, wl_resource* resource, uint32_t id) { - auto* compositor = - static_cast<MockCompositor*>(wl_resource_get_user_data(resource)); + auto* compositor = GetUserDataAs<MockCompositor>(resource); wl_resource* surface_resource = wl_resource_create( client, &wl_surface_interface, wl_resource_get_version(resource), id); if (!surface_resource) { @@ -53,8 +57,7 @@ void Attach(wl_client* client, wl_resource* buffer_resource, int32_t x, int32_t y) { - static_cast<MockSurface*>(wl_resource_get_user_data(resource)) - ->Attach(buffer_resource, x, y); + GetUserDataAs<MockSurface>(resource)->Attach(buffer_resource, x, y); } void Damage(wl_client* client, @@ -63,12 +66,11 @@ void Damage(wl_client* client, int32_t y, int32_t width, int32_t height) { - static_cast<MockSurface*>(wl_resource_get_user_data(resource)) - ->Damage(x, y, width, height); + GetUserDataAs<MockSurface>(resource)->Damage(x, y, width, height); } void Commit(wl_client* client, wl_resource* resource) { - static_cast<MockSurface*>(wl_resource_get_user_data(resource))->Commit(); + GetUserDataAs<MockSurface>(resource)->Commit(); } const struct wl_surface_interface surface_impl = { @@ -89,14 +91,13 @@ const struct wl_surface_interface surface_impl = { void UseUnstableVersion(wl_client* client, wl_resource* resource, int32_t version) { - static_cast<MockXdgShell*>(wl_resource_get_user_data(resource)) - ->UseUnstableVersion(version); + GetUserDataAs<MockXdgShell>(resource)->UseUnstableVersion(version); } // xdg_shell and zxdg_shell_v6 void Pong(wl_client* client, wl_resource* resource, uint32_t serial) { - static_cast<MockXdgShell*>(wl_resource_get_user_data(resource))->Pong(serial); + GetUserDataAs<MockXdgShell>(resource)->Pong(serial); } // xdg_shell @@ -131,36 +132,36 @@ const struct zxdg_shell_v6_interface zxdg_shell_v6_impl = { // wl_seat void GetPointer(wl_client* client, wl_resource* resource, uint32_t id) { - auto* seat = static_cast<MockSeat*>(wl_resource_get_user_data(resource)); wl_resource* pointer_resource = wl_resource_create( client, &wl_pointer_interface, wl_resource_get_version(resource), id); if (!pointer_resource) { wl_client_post_no_memory(client); return; } - seat->pointer.reset(new MockPointer(pointer_resource)); + auto* seat = GetUserDataAs<MockSeat>(resource); + seat->set_pointer(std::make_unique<MockPointer>(pointer_resource)); } void GetKeyboard(wl_client* client, wl_resource* resource, uint32_t id) { - auto* seat = static_cast<MockSeat*>(wl_resource_get_user_data(resource)); wl_resource* keyboard_resource = wl_resource_create( client, &wl_keyboard_interface, wl_resource_get_version(resource), id); if (!keyboard_resource) { wl_client_post_no_memory(client); return; } - seat->keyboard.reset(new MockKeyboard(keyboard_resource)); + auto* seat = GetUserDataAs<MockSeat>(resource); + seat->set_keyboard(std::make_unique<MockKeyboard>(keyboard_resource)); } void GetTouch(wl_client* client, wl_resource* resource, uint32_t id) { - auto* seat = static_cast<MockSeat*>(wl_resource_get_user_data(resource)); wl_resource* touch_resource = wl_resource_create( client, &wl_touch_interface, wl_resource_get_version(resource), id); if (!touch_resource) { wl_client_post_no_memory(client); return; } - seat->touch.reset(new MockTouch(touch_resource)); + auto* seat = GetUserDataAs<MockSeat>(resource); + seat->set_touch(std::make_unique<MockTouch>(touch_resource)); } const struct wl_seat_interface seat_impl = { @@ -192,18 +193,15 @@ const struct wl_touch_interface touch_impl = { // xdg_surface, zxdg_surface_v6 and zxdg_toplevel shared methods. void SetTitle(wl_client* client, wl_resource* resource, const char* title) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->SetTitle(title); + GetUserDataAs<MockXdgSurface>(resource)->SetTitle(title); } void SetAppId(wl_client* client, wl_resource* resource, const char* app_id) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->SetAppId(app_id); + GetUserDataAs<MockXdgSurface>(resource)->SetAppId(app_id); } void AckConfigure(wl_client* client, wl_resource* resource, uint32_t serial) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->AckConfigure(serial); + GetUserDataAs<MockXdgSurface>(resource)->AckConfigure(serial); } void SetWindowGeometry(wl_client* client, @@ -212,35 +210,30 @@ void SetWindowGeometry(wl_client* client, int32_t y, int32_t width, int32_t height) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->SetWindowGeometry(x, y, width, height); + GetUserDataAs<MockXdgSurface>(resource)->SetWindowGeometry(x, y, width, + height); } void SetMaximized(wl_client* client, wl_resource* resource) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->SetMaximized(); + GetUserDataAs<MockXdgSurface>(resource)->SetMaximized(); } void UnsetMaximized(wl_client* client, wl_resource* resource) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->UnsetMaximized(); + GetUserDataAs<MockXdgSurface>(resource)->UnsetMaximized(); } void SetFullscreen(wl_client* client, wl_resource* resource, wl_resource* output) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->SetFullscreen(); + GetUserDataAs<MockXdgSurface>(resource)->SetFullscreen(); } void UnsetFullscreen(wl_client* client, wl_resource* resource) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->UnsetFullscreen(); + GetUserDataAs<MockXdgSurface>(resource)->UnsetFullscreen(); } void SetMinimized(wl_client* client, wl_resource* resource) { - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)) - ->SetMinimized(); + GetUserDataAs<MockXdgSurface>(resource)->SetMinimized(); } const struct xdg_surface_interface xdg_surface_impl = { @@ -263,9 +256,8 @@ const struct xdg_surface_interface xdg_surface_impl = { // zxdg_surface specific interface void GetTopLevel(wl_client* client, wl_resource* resource, uint32_t id) { - auto* surface = - static_cast<MockXdgSurface*>(wl_resource_get_user_data(resource)); - if (surface->xdg_toplevel) { + auto* surface = GetUserDataAs<MockXdgSurface>(resource); + if (surface->xdg_toplevel()) { wl_resource_post_error(resource, ZXDG_SURFACE_V6_ERROR_ALREADY_CONSTRUCTED, "surface has already been constructed"); return; @@ -276,7 +268,8 @@ void GetTopLevel(wl_client* client, wl_resource* resource, uint32_t id) { wl_client_post_no_memory(client); return; } - surface->xdg_toplevel.reset(new MockXdgTopLevel(xdg_toplevel_resource)); + surface->set_xdg_toplevel( + std::make_unique<MockXdgTopLevel>(xdg_toplevel_resource)); } const struct zxdg_surface_v6_interface zxdg_surface_v6_impl = { @@ -310,9 +303,8 @@ void GetXdgSurfaceImpl(wl_client* client, wl_resource* surface_resource, const struct wl_interface* interface, const void* implementation) { - auto* surface = - static_cast<MockSurface*>(wl_resource_get_user_data(surface_resource)); - if (surface->xdg_surface) { + auto* surface = GetUserDataAs<MockSurface>(surface_resource); + if (surface->xdg_surface()) { uint32_t xdg_error = implementation == &xdg_surface_impl ? XDG_SHELL_ERROR_ROLE : ZXDG_SHELL_V6_ERROR_ROLE; @@ -326,8 +318,8 @@ void GetXdgSurfaceImpl(wl_client* client, wl_client_post_no_memory(client); return; } - surface->xdg_surface.reset( - new MockXdgSurface(xdg_surface_resource, implementation)); + surface->set_xdg_surface( + std::make_unique<MockXdgSurface>(xdg_surface_resource, implementation)); } // xdg_shell @@ -361,60 +353,62 @@ ServerObject::~ServerObject() { // static void ServerObject::OnResourceDestroyed(wl_resource* resource) { - auto* obj = static_cast<ServerObject*>(wl_resource_get_user_data(resource)); + auto* obj = GetUserDataAs<ServerObject>(resource); obj->resource_ = nullptr; } +template <class T> +void SetImplementation(wl_resource* resource, + const void* implementation, + T* user_data) { + wl_resource_set_implementation(resource, implementation, user_data, + &ServerObject::OnResourceDestroyed); +} + MockXdgSurface::MockXdgSurface(wl_resource* resource, const void* implementation) : ServerObject(resource) { - wl_resource_set_implementation(resource, implementation, this, - &ServerObject::OnResourceDestroyed); + SetImplementation(resource, implementation, this); } MockXdgSurface::~MockXdgSurface() {} MockXdgTopLevel::MockXdgTopLevel(wl_resource* resource) : MockXdgSurface(resource, &zxdg_surface_v6_impl) { - wl_resource_set_implementation(resource, &zxdg_toplevel_v6_impl, this, - &ServerObject::OnResourceDestroyed); + SetImplementation(resource, &zxdg_toplevel_v6_impl, this); } MockXdgTopLevel::~MockXdgTopLevel() {} MockSurface::MockSurface(wl_resource* resource) : ServerObject(resource) { - wl_resource_set_implementation(resource, &surface_impl, this, - &ServerObject::OnResourceDestroyed); + SetImplementation(resource, &surface_impl, this); } MockSurface::~MockSurface() { - if (xdg_surface && xdg_surface->resource()) - wl_resource_destroy(xdg_surface->resource()); + if (xdg_surface_ && xdg_surface_->resource()) + wl_resource_destroy(xdg_surface_->resource()); } MockSurface* MockSurface::FromResource(wl_resource* resource) { if (!wl_resource_instance_of(resource, &wl_surface_interface, &surface_impl)) return nullptr; - return static_cast<MockSurface*>(wl_resource_get_user_data(resource)); + return GetUserDataAs<MockSurface>(resource); } MockPointer::MockPointer(wl_resource* resource) : ServerObject(resource) { - wl_resource_set_implementation(resource, &pointer_impl, this, - &ServerObject::OnResourceDestroyed); + SetImplementation(resource, &pointer_impl, this); } MockPointer::~MockPointer() {} MockKeyboard::MockKeyboard(wl_resource* resource) : ServerObject(resource) { - wl_resource_set_implementation(resource, &keyboard_impl, this, - &ServerObject::OnResourceDestroyed); + SetImplementation(resource, &keyboard_impl, this); } MockKeyboard::~MockKeyboard() {} MockTouch::MockTouch(wl_resource* resource) : ServerObject(resource) { - wl_resource_set_implementation(resource, &touch_impl, this, - &ServerObject::OnResourceDestroyed); + SetImplementation(resource, &touch_impl, this); } MockTouch::~MockTouch() {} @@ -451,14 +445,13 @@ void Global::Bind(wl_client* client, } if (!global->resource_) global->resource_ = resource; - wl_resource_set_implementation(resource, global->implementation_, global, - &Global::OnResourceDestroyed); + SetImplementation(resource, global->implementation_, global); global->OnBind(); } // static void Global::OnResourceDestroyed(wl_resource* resource) { - auto* global = static_cast<Global*>(wl_resource_get_user_data(resource)); + auto* global = GetUserDataAs<Global>(resource); if (global->resource_ == resource) global->resource_ = nullptr; } @@ -552,8 +545,8 @@ bool FakeServer::Start(uint32_t shell_version) { (void)server_fd.release(); base::Thread::Options options; - options.message_pump_factory = - base::Bind(&FakeServer::CreateMessagePump, base::Unretained(this)); + options.message_pump_factory = base::BindRepeating( + &FakeServer::CreateMessagePump, base::Unretained(this)); if (!base::Thread::StartWithOptions(options)) return false; @@ -564,7 +557,7 @@ bool FakeServer::Start(uint32_t shell_version) { void FakeServer::Pause() { task_runner()->PostTask( - FROM_HERE, base::Bind(&FakeServer::DoPause, base::Unretained(this))); + FROM_HERE, base::BindOnce(&FakeServer::DoPause, base::Unretained(this))); pause_event_.Wait(); } diff --git a/chromium/ui/ozone/platform/wayland/fake_server.h b/chromium/ui/ozone/platform/wayland/fake_server.h index 816f5815e6f..24e3863053d 100644 --- a/chromium/ui/ozone/platform/wayland/fake_server.h +++ b/chromium/ui/ozone/platform/wayland/fake_server.h @@ -25,7 +25,7 @@ namespace wl { // Base class for managing the life cycle of server objects. class ServerObject { public: - ServerObject(wl_resource* resource); + explicit ServerObject(wl_resource* resource); virtual ~ServerObject(); wl_resource* resource() { return resource_; } @@ -54,24 +54,29 @@ class MockXdgSurface : public ServerObject { MOCK_METHOD1(SetAppId, void(const char* app_id)); MOCK_METHOD1(AckConfigure, void(uint32_t serial)); MOCK_METHOD4(SetWindowGeometry, - void(int32_t x, int32_t y, int32_t widht, int32_t height)); + void(int32_t x, int32_t y, int32_t width, int32_t height)); MOCK_METHOD0(SetMaximized, void()); MOCK_METHOD0(UnsetMaximized, void()); MOCK_METHOD0(SetFullscreen, void()); MOCK_METHOD0(UnsetFullscreen, void()); MOCK_METHOD0(SetMinimized, void()); - // Used when xdg v6 is used. - std::unique_ptr<MockXdgTopLevel> xdg_toplevel; + void set_xdg_toplevel(std::unique_ptr<MockXdgTopLevel> xdg_toplevel) { + xdg_toplevel_ = std::move(xdg_toplevel); + } + MockXdgTopLevel* xdg_toplevel() { return xdg_toplevel_.get(); } private: + // Used when xdg v6 is used. + std::unique_ptr<MockXdgTopLevel> xdg_toplevel_; + DISALLOW_COPY_AND_ASSIGN(MockXdgSurface); }; // Manage zxdg_toplevel for providing desktop UI. class MockXdgTopLevel : public MockXdgSurface { public: - MockXdgTopLevel(wl_resource* resource); + explicit MockXdgTopLevel(wl_resource* resource); ~MockXdgTopLevel() override; // TODO(msisov): mock other zxdg_toplevel specific methods once implementation @@ -85,7 +90,7 @@ class MockXdgTopLevel : public MockXdgSurface { // Manage client surface class MockSurface : public ServerObject { public: - MockSurface(wl_resource* resource); + explicit MockSurface(wl_resource* resource); ~MockSurface() override; static MockSurface* FromResource(wl_resource* resource); @@ -95,15 +100,20 @@ class MockSurface : public ServerObject { void(int32_t x, int32_t y, int32_t width, int32_t height)); MOCK_METHOD0(Commit, void()); - std::unique_ptr<MockXdgSurface> xdg_surface; + void set_xdg_surface(std::unique_ptr<MockXdgSurface> xdg_surface) { + xdg_surface_ = std::move(xdg_surface); + } + MockXdgSurface* xdg_surface() { return xdg_surface_.get(); } private: + std::unique_ptr<MockXdgSurface> xdg_surface_; + DISALLOW_COPY_AND_ASSIGN(MockSurface); }; class MockPointer : public ServerObject { public: - MockPointer(wl_resource* resource); + explicit MockPointer(wl_resource* resource); ~MockPointer() override; private: @@ -112,7 +122,7 @@ class MockPointer : public ServerObject { class MockKeyboard : public ServerObject { public: - MockKeyboard(wl_resource* resource); + explicit MockKeyboard(wl_resource* resource); ~MockKeyboard() override; private: @@ -207,11 +217,26 @@ class MockSeat : public Global { MockSeat(); ~MockSeat() override; - std::unique_ptr<MockPointer> pointer; - std::unique_ptr<MockKeyboard> keyboard; - std::unique_ptr<MockTouch> touch; + void set_pointer(std::unique_ptr<MockPointer> pointer) { + pointer_ = std::move(pointer); + } + MockPointer* pointer() { return pointer_.get(); } + + void set_keyboard(std::unique_ptr<MockKeyboard> keyboard) { + keyboard_ = std::move(keyboard); + } + MockKeyboard* keyboard() { return keyboard_.get(); } + + void set_touch(std::unique_ptr<MockTouch> touch) { + touch_ = std::move(touch); + } + MockTouch* touch() { return touch_.get(); } private: + std::unique_ptr<MockPointer> pointer_; + std::unique_ptr<MockKeyboard> keyboard_; + std::unique_ptr<MockTouch> touch_; + DISALLOW_COPY_AND_ASSIGN(MockSeat); }; @@ -244,7 +269,7 @@ struct DisplayDeleter { void operator()(wl_display* display); }; -class FakeServer : public base::Thread, base::MessagePumpLibevent::Watcher { +class FakeServer : public base::Thread, base::MessagePumpLibevent::FdWatcher { public: FakeServer(); ~FakeServer() override; @@ -278,7 +303,7 @@ class FakeServer : public base::Thread, base::MessagePumpLibevent::Watcher { std::unique_ptr<base::MessagePump> CreateMessagePump(); - // base::MessagePumpLibevent::Watcher + // base::MessagePumpLibevent::FdWatcher void OnFileCanReadWithoutBlocking(int fd) override; void OnFileCanWriteWithoutBlocking(int fd) override; @@ -296,7 +321,7 @@ class FakeServer : public base::Thread, base::MessagePumpLibevent::Watcher { MockXdgShell xdg_shell_; MockXdgShellV6 zxdg_shell_v6_; - base::MessagePumpLibevent::FileDescriptorWatcher controller_; + base::MessagePumpLibevent::FdWatchController controller_; DISALLOW_COPY_AND_ASSIGN(FakeServer); }; diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc index 843d7c99fdf..f0774bf1211 100644 --- a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -4,7 +4,6 @@ #include "ui/ozone/platform/wayland/ozone_platform_wayland.h" -#include "base/memory/ptr_util.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/base/ui_features.h" #include "ui/display/manager/fake_display_delegate.h" diff --git a/chromium/ui/ozone/platform/wayland/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/wayland_connection.cc index 89cf855b386..a90445be880 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_connection.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_connection.cc @@ -92,7 +92,8 @@ void WaylandConnection::ScheduleFlush() { return; DCHECK(base::MessageLoopForUI::IsCurrent()); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&WaylandConnection::Flush, base::Unretained(this))); + FROM_HERE, + base::BindOnce(&WaylandConnection::Flush, base::Unretained(this))); scheduled_flush_ = true; } @@ -253,8 +254,8 @@ void WaylandConnection::Capabilities(void* data, return; } connection->pointer_ = std::make_unique<WaylandPointer>( - pointer, base::Bind(&WaylandConnection::DispatchUiEvent, - base::Unretained(connection))); + pointer, base::BindRepeating(&WaylandConnection::DispatchUiEvent, + base::Unretained(connection))); connection->pointer_->set_connection(connection); } } else if (connection->pointer_) { @@ -268,8 +269,8 @@ void WaylandConnection::Capabilities(void* data, return; } connection->keyboard_ = std::make_unique<WaylandKeyboard>( - keyboard, base::Bind(&WaylandConnection::DispatchUiEvent, - base::Unretained(connection))); + keyboard, base::BindRepeating(&WaylandConnection::DispatchUiEvent, + base::Unretained(connection))); connection->keyboard_->set_connection(connection); } } else if (connection->keyboard_) { @@ -283,8 +284,8 @@ void WaylandConnection::Capabilities(void* data, return; } connection->touch_ = std::make_unique<WaylandTouch>( - touch, base::Bind(&WaylandConnection::DispatchUiEvent, - base::Unretained(connection))); + touch, base::BindRepeating(&WaylandConnection::DispatchUiEvent, + base::Unretained(connection))); connection->touch_->set_connection(connection); } } else if (connection->touch_) { diff --git a/chromium/ui/ozone/platform/wayland/wayland_connection.h b/chromium/ui/ozone/platform/wayland/wayland_connection.h index e745efd5b6b..747a11a465a 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_connection.h +++ b/chromium/ui/ozone/platform/wayland/wayland_connection.h @@ -21,7 +21,7 @@ namespace ui { class WaylandWindow; class WaylandConnection : public PlatformEventSource, - public base::MessagePumpLibevent::Watcher { + public base::MessagePumpLibevent::FdWatcher { public: WaylandConnection(); ~WaylandConnection() override; @@ -53,6 +53,9 @@ class WaylandConnection : public PlatformEventSource, int GetKeyboardModifiers(); + // Returns the current pointer, which may be null. + WaylandPointer* pointer() { return pointer_.get(); } + private: void Flush(); void DispatchUiEvent(Event* event); @@ -60,7 +63,7 @@ class WaylandConnection : public PlatformEventSource, // PlatformEventSource void OnDispatcherListChanged() override; - // base::MessagePumpLibevent::Watcher + // base::MessagePumpLibevent::FdWatcher void OnFileCanReadWithoutBlocking(int fd) override; void OnFileCanWriteWithoutBlocking(int fd) override; @@ -98,7 +101,7 @@ class WaylandConnection : public PlatformEventSource, bool scheduled_flush_ = false; bool watching_ = false; - base::MessagePumpLibevent::FileDescriptorWatcher controller_; + base::MessagePumpLibevent::FdWatchController controller_; uint32_t serial_ = 0; diff --git a/chromium/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc index 7ce077a7e97..492152b68b2 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc @@ -31,13 +31,13 @@ class WaylandKeyboardTest : public WaylandTest { void SetUp() override { WaylandTest::SetUp(); - wl_seat_send_capabilities(server.seat()->resource(), + wl_seat_send_capabilities(server_.seat()->resource(), WL_SEAT_CAPABILITY_KEYBOARD); Sync(); - keyboard = server.seat()->keyboard.get(); - ASSERT_TRUE(keyboard); + keyboard_ = server_.seat()->keyboard(); + ASSERT_TRUE(keyboard_); #if BUILDFLAG(USE_XKBCOMMON) // Set up XKB bits and set the keymap to the client. @@ -54,14 +54,14 @@ class WaylandKeyboardTest : public WaylandTest { bool rv = shared_keymap.CreateAndMapAnonymous(keymap_size); DCHECK(rv); memcpy(shared_keymap.memory(), keymap_string.get(), keymap_size); - wl_keyboard_send_keymap(keyboard->resource(), + wl_keyboard_send_keymap(keyboard_->resource(), WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, shared_keymap.handle().GetHandle(), keymap_size); #endif } protected: - wl::MockKeyboard* keyboard; + wl::MockKeyboard* keyboard_; private: #if BUILDFLAG(USE_XKBCOMMON) @@ -81,14 +81,15 @@ ACTION_P(CloneEvent, ptr) { TEST_P(WaylandKeyboardTest, Keypress) { struct wl_array empty; wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); - wl_keyboard_send_key(keyboard->resource(), 2, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); ASSERT_TRUE(event); @@ -98,27 +99,28 @@ TEST_P(WaylandKeyboardTest, Keypress) { EXPECT_EQ(ui::VKEY_A, key_event->key_code()); EXPECT_EQ(ET_KEY_PRESSED, key_event->type()); - wl_keyboard_send_leave(keyboard->resource(), 3, surface->resource()); + wl_keyboard_send_leave(keyboard_->resource(), 3, surface_->resource()); Sync(); - wl_keyboard_send_key(keyboard->resource(), 3, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 3, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_PRESSED); - EXPECT_CALL(delegate, DispatchEvent(_)).Times(0); + EXPECT_CALL(delegate_, DispatchEvent(_)).Times(0); } TEST_P(WaylandKeyboardTest, AltModifierKeypress) { struct wl_array empty; wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Alt - wl_keyboard_send_key(keyboard->resource(), 2, 0, 56 /* left Alt */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 56 /* left Alt */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); ASSERT_TRUE(event); @@ -134,15 +136,16 @@ TEST_P(WaylandKeyboardTest, AltModifierKeypress) { TEST_P(WaylandKeyboardTest, ControlModifierKeypress) { struct wl_array empty; wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Control - wl_keyboard_send_key(keyboard->resource(), 2, 0, 29 /* left Control */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 29 /* left Control */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); ASSERT_TRUE(event); @@ -158,15 +161,16 @@ TEST_P(WaylandKeyboardTest, ControlModifierKeypress) { TEST_P(WaylandKeyboardTest, ShiftModifierKeypress) { struct wl_array empty; wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Shift - wl_keyboard_send_key(keyboard->resource(), 2, 0, 42 /* left Shift */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 42 /* left Shift */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); ASSERT_TRUE(event); @@ -183,41 +187,42 @@ TEST_P(WaylandKeyboardTest, ControlShiftModifiers) { struct wl_array empty; wl_array_init(&empty); wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Pressing control. - wl_keyboard_send_key(keyboard->resource(), 2, 0, 29 /* Control */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 29 /* Control */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); - wl_keyboard_send_modifiers(keyboard->resource(), 3, 4 /* mods_depressed*/, + wl_keyboard_send_modifiers(keyboard_->resource(), 3, 4 /* mods_depressed*/, 0 /* mods_latched */, 0 /* mods_locked */, 0 /* group */); Sync(); // Pressing shift (with control key still held down). - wl_keyboard_send_key(keyboard->resource(), 4, 0, 42 /* Shift */, + wl_keyboard_send_key(keyboard_->resource(), 4, 0, 42 /* Shift */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event2; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); Sync(); - wl_keyboard_send_modifiers(keyboard->resource(), 5, 5 /* mods_depressed*/, + wl_keyboard_send_modifiers(keyboard_->resource(), 5, 5 /* mods_depressed*/, 0 /* mods_latched */, 0 /* mods_locked */, 0 /* group */); Sync(); // Sending a reguard keypress, eg 'a'. - wl_keyboard_send_key(keyboard->resource(), 6, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 6, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event3; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event3)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event3)); Sync(); ASSERT_TRUE(event3); @@ -234,15 +239,16 @@ TEST_P(WaylandKeyboardTest, CapsLockKeypress) { struct wl_array empty; wl_array_init(&empty); wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Capslock - wl_keyboard_send_key(keyboard->resource(), 2, 0, 58 /* Capslock */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 58 /* Capslock */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); ASSERT_TRUE(event); @@ -256,11 +262,11 @@ TEST_P(WaylandKeyboardTest, CapsLockKeypress) { Sync(); - wl_keyboard_send_key(keyboard->resource(), 2, 0, 58 /* Capslock */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 58 /* Capslock */, WL_KEYBOARD_KEY_STATE_RELEASED); std::unique_ptr<Event> event2; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); Sync(); ASSERT_TRUE(event2); @@ -278,41 +284,42 @@ TEST_P(WaylandKeyboardTest, CapsLockModifier) { struct wl_array empty; wl_array_init(&empty); wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Pressing capslock (led ON). - wl_keyboard_send_key(keyboard->resource(), 2, 0, 58 /* Capslock */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 58 /* Capslock */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); - wl_keyboard_send_modifiers(keyboard->resource(), 3, 2 /* mods_depressed*/, + wl_keyboard_send_modifiers(keyboard_->resource(), 3, 2 /* mods_depressed*/, 0 /* mods_latched */, 2 /* mods_locked */, 0 /* group */); Sync(); // Releasing capslock (led ON). - wl_keyboard_send_key(keyboard->resource(), 4, 0, 58 /* Capslock */, + wl_keyboard_send_key(keyboard_->resource(), 4, 0, 58 /* Capslock */, WL_KEYBOARD_KEY_STATE_RELEASED); std::unique_ptr<Event> event2; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); Sync(); - wl_keyboard_send_modifiers(keyboard->resource(), 5, 0 /* mods_depressed*/, + wl_keyboard_send_modifiers(keyboard_->resource(), 5, 0 /* mods_depressed*/, 0 /* mods_latched */, 2 /* mods_locked */, 0 /* group */); Sync(); // Sending a reguard keypress, eg 'a'. - wl_keyboard_send_key(keyboard->resource(), 6, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 6, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event3; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event3)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event3)); Sync(); ASSERT_TRUE(event3); @@ -330,20 +337,21 @@ TEST_P(WaylandKeyboardTest, EventAutoRepeat) { struct wl_array empty; wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Auto repeat info in ms. uint32_t rate = 75; uint32_t delay = 25; - wl_keyboard_send_repeat_info(keyboard->resource(), rate, delay); + wl_keyboard_send_repeat_info(keyboard_->resource(), rate, delay); - wl_keyboard_send_key(keyboard->resource(), 2, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); @@ -361,9 +369,9 @@ TEST_P(WaylandKeyboardTest, EventAutoRepeat) { Sync(); std::unique_ptr<Event> event2; - EXPECT_CALL(delegate, DispatchEvent(_)).WillRepeatedly(CloneEvent(&event2)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly(CloneEvent(&event2)); - wl_keyboard_send_key(keyboard->resource(), 3, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 3, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_RELEASED); Sync(); } @@ -372,20 +380,21 @@ TEST_P(WaylandKeyboardTest, NoEventAutoRepeatOnLeave) { struct wl_array empty; wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Auto repeat info in ms. uint32_t rate = 75; uint32_t delay = 25; - wl_keyboard_send_repeat_info(keyboard->resource(), rate, delay); + wl_keyboard_send_repeat_info(keyboard_->resource(), rate, delay); - wl_keyboard_send_key(keyboard->resource(), 2, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); @@ -400,13 +409,13 @@ TEST_P(WaylandKeyboardTest, NoEventAutoRepeatOnLeave) { base::TimeDelta::FromMilliseconds(rate * 2)); } - wl_keyboard_send_leave(keyboard->resource(), 3, surface->resource()); + wl_keyboard_send_leave(keyboard_->resource(), 3, surface_->resource()); Sync(); - EXPECT_CALL(delegate, DispatchEvent(_)).Times(0); + EXPECT_CALL(delegate_, DispatchEvent(_)).Times(0); - wl_keyboard_send_key(keyboard->resource(), 4, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 4, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_RELEASED); Sync(); } @@ -415,20 +424,21 @@ TEST_P(WaylandKeyboardTest, NoEventAutoRepeatBeforeTimeout) { struct wl_array empty; wl_array_init(&empty); - wl_keyboard_send_enter(keyboard->resource(), 1, surface->resource(), &empty); + wl_keyboard_send_enter(keyboard_->resource(), 1, surface_->resource(), + &empty); wl_array_release(&empty); // Auto repeat info in ms. uint32_t rate = 500; uint32_t delay = 50; - wl_keyboard_send_repeat_info(keyboard->resource(), rate, delay); + wl_keyboard_send_repeat_info(keyboard_->resource(), rate, delay); - wl_keyboard_send_key(keyboard->resource(), 2, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 2, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_PRESSED); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); @@ -444,11 +454,11 @@ TEST_P(WaylandKeyboardTest, NoEventAutoRepeatBeforeTimeout) { base::TimeDelta::FromMilliseconds(rate / 5)); } - wl_keyboard_send_key(keyboard->resource(), 4, 0, 30 /* a */, + wl_keyboard_send_key(keyboard_->resource(), 4, 0, 30 /* a */, WL_KEYBOARD_KEY_STATE_RELEASED); std::unique_ptr<Event> event2; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event2)); Sync(); ASSERT_TRUE(event2); diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/wayland_pointer.cc index 9d6f4ae78e3..8d74daf9b73 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_pointer.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer.cc @@ -23,6 +23,12 @@ bool VerifyFlagsAfterMasking(int flags, int original_flags, int modifiers) { return flags == original_flags; } +bool HasAnyButtonFlag(int flags) { + return (flags & (EF_LEFT_MOUSE_BUTTON | EF_MIDDLE_MOUSE_BUTTON | + EF_RIGHT_MOUSE_BUTTON | EF_BACK_MOUSE_BUTTON | + EF_FORWARD_MOUSE_BUTTON)) != 0; +} + } // namespace WaylandPointer::WaylandPointer(wl_pointer* pointer, @@ -38,7 +44,12 @@ WaylandPointer::WaylandPointer(wl_pointer* pointer, cursor_.reset(new WaylandCursor); } -WaylandPointer::~WaylandPointer() {} +WaylandPointer::~WaylandPointer() { + if (window_with_pointer_focus_) { + window_with_pointer_focus_->set_pointer_focus(false); + window_with_pointer_focus_->set_has_implicit_grab(false); + } +} // static void WaylandPointer::Enter(void* data, @@ -50,8 +61,11 @@ void WaylandPointer::Enter(void* data, WaylandPointer* pointer = static_cast<WaylandPointer*>(data); pointer->location_.SetPoint(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); - if (surface) - WaylandWindow::FromSurface(surface)->set_pointer_focus(true); + if (surface) { + WaylandWindow* window = WaylandWindow::FromSurface(surface); + window->set_pointer_focus(true); + pointer->window_with_pointer_focus_ = window; + } } // static @@ -63,8 +77,11 @@ void WaylandPointer::Leave(void* data, MouseEvent event(ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), EventTimeForNow(), pointer->flags_, 0); pointer->callback_.Run(&event); - if (surface) - WaylandWindow::FromSurface(surface)->set_pointer_focus(false); + if (surface) { + WaylandWindow* window = WaylandWindow::FromSurface(surface); + window->set_pointer_focus(false); + pointer->window_with_pointer_focus_ = nullptr; + } } // static @@ -92,22 +109,22 @@ void WaylandPointer::Button(void* data, uint32_t button, uint32_t state) { WaylandPointer* pointer = static_cast<WaylandPointer*>(data); - int flag; + int changed_button; switch (button) { case BTN_LEFT: - flag = EF_LEFT_MOUSE_BUTTON; + changed_button = EF_LEFT_MOUSE_BUTTON; break; case BTN_MIDDLE: - flag = EF_MIDDLE_MOUSE_BUTTON; + changed_button = EF_MIDDLE_MOUSE_BUTTON; break; case BTN_RIGHT: - flag = EF_RIGHT_MOUSE_BUTTON; + changed_button = EF_RIGHT_MOUSE_BUTTON; break; case BTN_BACK: - flag = EF_BACK_MOUSE_BUTTON; + changed_button = EF_BACK_MOUSE_BUTTON; break; case BTN_FORWARD: - flag = EF_FORWARD_MOUSE_BUTTON; + changed_button = EF_FORWARD_MOUSE_BUTTON; break; default: return; @@ -116,17 +133,24 @@ void WaylandPointer::Button(void* data, EventType type; if (state == WL_POINTER_BUTTON_STATE_PRESSED) { type = ET_MOUSE_PRESSED; - pointer->flags_ |= flag; + pointer->flags_ |= changed_button; pointer->connection_->set_serial(serial); } else { type = ET_MOUSE_RELEASED; - pointer->flags_ &= ~flag; + pointer->flags_ &= ~changed_button; + } + + if (pointer->window_with_pointer_focus_) { + pointer->window_with_pointer_focus_->set_has_implicit_grab( + HasAnyButtonFlag(pointer->flags_)); } - int flags = pointer->GetFlagsWithKeyboardModifiers() | flag; + // 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, flag); + flags, changed_button); + event.set_location_f(pointer->location_); event.set_root_location_f(pointer->location_); pointer->callback_.Run(&event); diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/wayland_pointer.h index 9550da69334..9515dfccdac 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_pointer.h +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer.h @@ -5,6 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_WAYLAND_POINTER_H_ #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_POINTER_H_ +#include "base/macros.h" #include "ui/events/ozone/evdev/event_dispatch_callback.h" #include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/wayland_cursor.h" @@ -12,6 +13,8 @@ namespace ui { +class WaylandWindow; + class WaylandPointer { public: WaylandPointer(wl_pointer* pointer, const EventDispatchCallback& callback); @@ -26,6 +29,10 @@ class WaylandPointer { WaylandCursor* cursor() { return cursor_.get(); } + void reset_window_with_pointer_focus() { + window_with_pointer_focus_ = nullptr; + } + private: // wl_pointer_listener static void Enter(void* data, @@ -60,11 +67,18 @@ class WaylandPointer { wl::Object<wl_pointer> obj_; EventDispatchCallback callback_; gfx::PointF location_; + // Flags is a bitmask of EventFlags corresponding to the pointer/keyboard + // state. int flags_ = 0; // Keeps track of current modifiers. These are needed in order to properly // update |flags_| with newest modifiers. int keyboard_modifiers_ = 0; + + // The window the mouse is over. + WaylandWindow* window_with_pointer_focus_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(WaylandPointer); }; } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_pointer_unittest.cc index ab2a8085d69..5c0e98a78d7 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_pointer_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer_unittest.cc @@ -25,17 +25,17 @@ class WaylandPointerTest : public WaylandTest { void SetUp() override { WaylandTest::SetUp(); - wl_seat_send_capabilities(server.seat()->resource(), + wl_seat_send_capabilities(server_.seat()->resource(), WL_SEAT_CAPABILITY_POINTER); Sync(); - pointer = server.seat()->pointer.get(); - ASSERT_TRUE(pointer); + pointer_ = server_.seat()->pointer(); + ASSERT_TRUE(pointer_); } protected: - wl::MockPointer* pointer; + wl::MockPointer* pointer_; private: DISALLOW_COPY_AND_ASSIGN(WaylandPointerTest); @@ -43,7 +43,7 @@ class WaylandPointerTest : public WaylandTest { TEST_P(WaylandPointerTest, Leave) { MockPlatformWindowDelegate other_delegate; - WaylandWindow other_window(&other_delegate, connection.get(), + WaylandWindow other_window(&other_delegate, connection_.get(), gfx::Rect(0, 0, 10, 10)); gfx::AcceleratedWidget other_widget = gfx::kNullAcceleratedWidget; EXPECT_CALL(other_delegate, OnAcceleratedWidgetAvailable(_, _)) @@ -54,16 +54,16 @@ TEST_P(WaylandPointerTest, Leave) { Sync(); wl::MockSurface* other_surface = - server.GetObject<wl::MockSurface>(other_widget); + server_.GetObject<wl::MockSurface>(other_widget); ASSERT_TRUE(other_surface); - wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0); - wl_pointer_send_leave(pointer->resource(), 2, surface->resource()); - wl_pointer_send_enter(pointer->resource(), 3, other_surface->resource(), 0, + wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), 0, 0); + wl_pointer_send_leave(pointer_->resource(), 2, surface_->resource()); + wl_pointer_send_enter(pointer_->resource(), 3, other_surface->resource(), 0, 0); - wl_pointer_send_button(pointer->resource(), 4, 1004, BTN_LEFT, + wl_pointer_send_button(pointer_->resource(), 4, 1004, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); - EXPECT_CALL(delegate, DispatchEvent(_)).Times(1); + EXPECT_CALL(delegate_, DispatchEvent(_)).Times(1); // Do an extra Sync() here so that we process the second enter event before we // destroy |other_window|. @@ -75,12 +75,13 @@ ACTION_P(CloneEvent, ptr) { } TEST_P(WaylandPointerTest, Motion) { - wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0); - wl_pointer_send_motion(pointer->resource(), 1002, wl_fixed_from_double(10.75), + wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), 0, 0); + wl_pointer_send_motion(pointer_->resource(), 1002, + wl_fixed_from_double(10.75), wl_fixed_from_double(20.375)); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); Sync(); @@ -95,15 +96,15 @@ TEST_P(WaylandPointerTest, Motion) { } TEST_P(WaylandPointerTest, MotionDragged) { - wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), 0, 0); - wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_MIDDLE, + wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), 0, 0); + wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_MIDDLE, WL_POINTER_BUTTON_STATE_PRESSED); Sync(); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); - wl_pointer_send_motion(pointer->resource(), 1003, wl_fixed_from_int(400), + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + wl_pointer_send_motion(pointer_->resource(), 1003, wl_fixed_from_int(400), wl_fixed_from_int(500)); Sync(); @@ -119,44 +120,59 @@ TEST_P(WaylandPointerTest, MotionDragged) { } TEST_P(WaylandPointerTest, ButtonPress) { - wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), wl_fixed_from_int(200), wl_fixed_from_int(150)); - wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_RIGHT, - WL_POINTER_BUTTON_STATE_PRESSED); - Sync(); - std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); - wl_pointer_send_button(pointer->resource(), 3, 1003, BTN_LEFT, + wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_RIGHT, + WL_POINTER_BUTTON_STATE_PRESSED); + std::unique_ptr<Event> right_press_event; + EXPECT_CALL(delegate_, DispatchEvent(_)) + .WillOnce(CloneEvent(&right_press_event)); + Sync(); + ASSERT_TRUE(right_press_event); + ASSERT_TRUE(right_press_event->IsMouseEvent()); + auto* right_press_mouse_event = right_press_event->AsMouseEvent(); + EXPECT_EQ(ET_MOUSE_PRESSED, right_press_mouse_event->type()); + EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, right_press_mouse_event->button_flags()); + EXPECT_EQ(EF_RIGHT_MOUSE_BUTTON, + right_press_mouse_event->changed_button_flags()); + + std::unique_ptr<Event> left_press_event; + EXPECT_CALL(delegate_, DispatchEvent(_)) + .WillOnce(CloneEvent(&left_press_event)); + wl_pointer_send_button(pointer_->resource(), 3, 1003, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); Sync(); - ASSERT_TRUE(event); - ASSERT_TRUE(event->IsMouseEvent()); - auto* mouse_event = event->AsMouseEvent(); - EXPECT_EQ(ET_MOUSE_PRESSED, mouse_event->type()); + ASSERT_TRUE(left_press_event); + ASSERT_TRUE(left_press_event->IsMouseEvent()); + auto* left_press_mouse_event = left_press_event->AsMouseEvent(); + EXPECT_EQ(ET_MOUSE_PRESSED, left_press_mouse_event->type()); EXPECT_EQ(EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, - mouse_event->button_flags()); - EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, mouse_event->changed_button_flags()); - EXPECT_EQ(gfx::PointF(200, 150), mouse_event->location_f()); - EXPECT_EQ(gfx::PointF(200, 150), mouse_event->root_location_f()); + left_press_mouse_event->button_flags()); + EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, + left_press_mouse_event->changed_button_flags()); + EXPECT_EQ(EF_LEFT_MOUSE_BUTTON, + left_press_mouse_event->changed_button_flags()); + EXPECT_EQ(gfx::PointF(200, 150), left_press_mouse_event->location_f()); + EXPECT_EQ(gfx::PointF(200, 150), left_press_mouse_event->root_location_f()); } TEST_P(WaylandPointerTest, ButtonRelease) { - wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), wl_fixed_from_int(50), wl_fixed_from_int(50)); - wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_BACK, + wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_BACK, WL_POINTER_BUTTON_STATE_PRESSED); - wl_pointer_send_button(pointer->resource(), 3, 1003, BTN_LEFT, + wl_pointer_send_button(pointer_->resource(), 3, 1003, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); Sync(); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); - wl_pointer_send_button(pointer->resource(), 4, 1004, BTN_LEFT, + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + wl_pointer_send_button(pointer_->resource(), 4, 1004, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); Sync(); @@ -173,17 +189,17 @@ TEST_P(WaylandPointerTest, ButtonRelease) { } TEST_P(WaylandPointerTest, AxisVertical) { - wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), wl_fixed_from_int(0), wl_fixed_from_int(0)); - wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_RIGHT, + wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_RIGHT, WL_POINTER_BUTTON_STATE_PRESSED); Sync(); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); // Wayland servers typically send a value of 10 per mouse wheel click. - wl_pointer_send_axis(pointer->resource(), 1003, + wl_pointer_send_axis(pointer_->resource(), 1003, WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_from_int(20)); Sync(); @@ -200,17 +216,17 @@ TEST_P(WaylandPointerTest, AxisVertical) { } TEST_P(WaylandPointerTest, AxisHorizontal) { - wl_pointer_send_enter(pointer->resource(), 1, surface->resource(), + wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), wl_fixed_from_int(50), wl_fixed_from_int(75)); - wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_LEFT, + wl_pointer_send_button(pointer_->resource(), 2, 1002, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); Sync(); std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); // Wayland servers typically send a value of 10 per mouse wheel click. - wl_pointer_send_axis(pointer->resource(), 1003, + wl_pointer_send_axis(pointer_->resource(), 1003, WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_int(10)); diff --git a/chromium/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc index 2ee97407685..ac537496dc7 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc @@ -19,13 +19,13 @@ namespace ui { class WaylandSurfaceFactoryTest : public WaylandTest { public: - WaylandSurfaceFactoryTest() : surface_factory(connection.get()) {} + WaylandSurfaceFactoryTest() : surface_factory(connection_.get()) {} ~WaylandSurfaceFactoryTest() override {} void SetUp() override { WaylandTest::SetUp(); - canvas = surface_factory.CreateCanvasForWidget(widget); + canvas = surface_factory.CreateCanvasForWidget(widget_); ASSERT_TRUE(canvas); } @@ -41,11 +41,11 @@ TEST_P(WaylandSurfaceFactoryTest, Canvas) { canvas->GetSurface(); canvas->PresentCanvas(gfx::Rect(5, 10, 20, 15)); - Expectation damage = EXPECT_CALL(*surface, Damage(5, 10, 20, 15)); + Expectation damage = EXPECT_CALL(*surface_, Damage(5, 10, 20, 15)); wl_resource* buffer_resource = nullptr; - Expectation attach = EXPECT_CALL(*surface, Attach(_, 0, 0)) + Expectation attach = EXPECT_CALL(*surface_, Attach(_, 0, 0)) .WillOnce(SaveArg<0>(&buffer_resource)); - EXPECT_CALL(*surface, Commit()).After(damage, attach); + EXPECT_CALL(*surface_, Commit()).After(damage, attach); Sync(); @@ -65,11 +65,11 @@ TEST_P(WaylandSurfaceFactoryTest, CanvasResize) { canvas->GetSurface(); canvas->PresentCanvas(gfx::Rect(0, 0, 100, 50)); - Expectation damage = EXPECT_CALL(*surface, Damage(0, 0, 100, 50)); + Expectation damage = EXPECT_CALL(*surface_, Damage(0, 0, 100, 50)); wl_resource* buffer_resource = nullptr; - Expectation attach = EXPECT_CALL(*surface, Attach(_, 0, 0)) + Expectation attach = EXPECT_CALL(*surface_, Attach(_, 0, 0)) .WillOnce(SaveArg<0>(&buffer_resource)); - EXPECT_CALL(*surface, Commit()).After(damage, attach); + EXPECT_CALL(*surface_, Commit()).After(damage, attach); Sync(); diff --git a/chromium/ui/ozone/platform/wayland/wayland_test.cc b/chromium/ui/ozone/platform/wayland/wayland_test.cc index 7bc6c91e462..a56cbcb8a4f 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_test.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_test.cc @@ -27,48 +27,48 @@ WaylandTest::WaylandTest() { KeyboardLayoutEngineManager::SetKeyboardLayoutEngine( std::make_unique<StubKeyboardLayoutEngine>()); #endif - connection.reset(new WaylandConnection); - window = std::make_unique<WaylandWindow>(&delegate, connection.get(), - gfx::Rect(0, 0, 800, 600)); + connection_.reset(new WaylandConnection); + window_ = std::make_unique<WaylandWindow>(&delegate_, connection_.get(), + gfx::Rect(0, 0, 800, 600)); } WaylandTest::~WaylandTest() {} void WaylandTest::SetUp() { - ASSERT_TRUE(server.Start(GetParam())); - ASSERT_TRUE(connection->Initialize()); - EXPECT_CALL(delegate, OnAcceleratedWidgetAvailable(_, _)) - .WillOnce(SaveArg<0>(&widget)); - ASSERT_TRUE(window->Initialize()); - ASSERT_NE(widget, gfx::kNullAcceleratedWidget); + ASSERT_TRUE(server_.Start(GetParam())); + ASSERT_TRUE(connection_->Initialize()); + EXPECT_CALL(delegate_, OnAcceleratedWidgetAvailable(_, _)) + .WillOnce(SaveArg<0>(&widget_)); + ASSERT_TRUE(window_->Initialize()); + ASSERT_NE(widget_, gfx::kNullAcceleratedWidget); // Wait for the client to flush all pending requests from initialization. base::RunLoop().RunUntilIdle(); // Pause the server after it has responded to all incoming events. - server.Pause(); + server_.Pause(); - surface = server.GetObject<wl::MockSurface>(widget); - ASSERT_TRUE(surface); + surface_ = server_.GetObject<wl::MockSurface>(widget_); + ASSERT_TRUE(surface_); - initialized = true; + initialized_ = true; } void WaylandTest::TearDown() { - if (initialized) + if (initialized_) Sync(); } void WaylandTest::Sync() { // Resume the server, flushing its pending events. - server.Resume(); + server_.Resume(); // Wait for the client to finish processing these events. base::RunLoop().RunUntilIdle(); // Pause the server, after it has finished processing any follow-up requests // from the client. - server.Pause(); + server_.Pause(); } } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_test.h b/chromium/ui/ozone/platform/wayland/wayland_test.h index 80587745e1a..20f7dfa47ad 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_test.h +++ b/chromium/ui/ozone/platform/wayland/wayland_test.h @@ -35,17 +35,17 @@ class WaylandTest : public ::testing::TestWithParam<uint32_t> { void Sync(); private: - bool initialized = false; - base::MessageLoopForUI message_loop; + base::MessageLoopForUI message_loop_; + bool initialized_ = false; protected: - wl::FakeServer server; - wl::MockSurface* surface; + wl::FakeServer server_; + wl::MockSurface* surface_; - MockPlatformWindowDelegate delegate; - std::unique_ptr<WaylandConnection> connection; - std::unique_ptr<WaylandWindow> window; - gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + MockPlatformWindowDelegate delegate_; + std::unique_ptr<WaylandConnection> connection_; + std::unique_ptr<WaylandWindow> window_; + gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget; private: #if BUILDFLAG(USE_XKBCOMMON) diff --git a/chromium/ui/ozone/platform/wayland/wayland_touch.cc b/chromium/ui/ozone/platform/wayland/wayland_touch.cc index b8dc5437f79..9052eaf09b3 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_touch.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_touch.cc @@ -8,7 +8,6 @@ #include <wayland-client.h> #include "base/files/scoped_file.h" -#include "base/memory/ptr_util.h" #include "ui/base/ui_features.h" #include "ui/events/event.h" #include "ui/ozone/platform/wayland/wayland_connection.h" diff --git a/chromium/ui/ozone/platform/wayland/wayland_touch_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_touch_unittest.cc index c9afacb6a22..13a830f4baf 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_touch_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_touch_unittest.cc @@ -33,13 +33,13 @@ class WaylandTouchTest : public WaylandTest { void SetUp() override { WaylandTest::SetUp(); - wl_seat_send_capabilities(server.seat()->resource(), + wl_seat_send_capabilities(server_.seat()->resource(), WL_SEAT_CAPABILITY_TOUCH); Sync(); - touch = server.seat()->touch.get(); - ASSERT_TRUE(touch); + touch_ = server_.seat()->touch(); + ASSERT_TRUE(touch_); } protected: @@ -51,7 +51,7 @@ class WaylandTouchTest : public WaylandTest { EXPECT_EQ(event_type, key_event->type()); } - wl::MockTouch* touch; + wl::MockTouch* touch_; private: DISALLOW_COPY_AND_ASSIGN(WaylandTouchTest); @@ -59,21 +59,21 @@ class WaylandTouchTest : public WaylandTest { TEST_P(WaylandTouchTest, KeypressAndMotion) { std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillRepeatedly(CloneEvent(&event)); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillRepeatedly(CloneEvent(&event)); - wl_touch_send_down(touch->resource(), 1, 0, surface->resource(), 0 /* id */, + wl_touch_send_down(touch_->resource(), 1, 0, surface_->resource(), 0 /* id */, wl_fixed_from_int(50), wl_fixed_from_int(100)); Sync(); CheckEventType(ui::ET_TOUCH_PRESSED, event.get()); - wl_touch_send_motion(touch->resource(), 500, 0 /* id */, + wl_touch_send_motion(touch_->resource(), 500, 0 /* id */, wl_fixed_from_int(100), wl_fixed_from_int(100)); Sync(); CheckEventType(ui::ET_TOUCH_MOVED, event.get()); - wl_touch_send_up(touch->resource(), 1, 1000, 0 /* id */); + wl_touch_send_up(touch_->resource(), 1, 1000, 0 /* id */); Sync(); CheckEventType(ui::ET_TOUCH_RELEASED, event.get()); diff --git a/chromium/ui/ozone/platform/wayland/wayland_window.cc b/chromium/ui/ozone/platform/wayland/wayland_window.cc index bed831b995c..6ebe40755d8 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_window.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_window.cc @@ -7,11 +7,11 @@ #include <wayland-client.h> #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/events/event.h" #include "ui/events/ozone/events_ozone.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_pointer.h" #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v5.h" #include "ui/ozone/platform/wayland/xdg_surface_wrapper_v6.h" @@ -55,6 +55,8 @@ WaylandWindow::~WaylandWindow() { PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); connection_->RemoveWindow(surface_.id()); } + if (has_pointer_focus_) + connection_->pointer()->reset_window_with_pointer_focus(); } // static @@ -134,13 +136,21 @@ void WaylandWindow::SetTitle(const base::string16& title) { } void WaylandWindow::SetCapture() { + // Wayland does implicit grabs, and doesn't allow for explicit grabs. The + // exception to that seems to be popups, which can do a grab during show. Need + // to evaluate under what circumstances we need this. NOTIMPLEMENTED(); } void WaylandWindow::ReleaseCapture() { + // See comment in SetCapture() for details on wayland and grabs. NOTIMPLEMENTED(); } +bool WaylandWindow::HasCapture() const { + return has_implicit_grab_; +} + void WaylandWindow::ToggleFullscreen() { DCHECK(xdg_surface_); @@ -234,8 +244,7 @@ PlatformImeController* WaylandWindow::GetPlatformImeController() { return nullptr; } -bool WaylandWindow::CanDispatchEvent(const PlatformEvent& native_event) { - Event* event = static_cast<Event*>(native_event); +bool WaylandWindow::CanDispatchEvent(const PlatformEvent& event) { if (event->IsMouseEvent()) return has_pointer_focus_; if (event->IsKeyEvent()) @@ -268,14 +277,21 @@ void WaylandWindow::HandleSurfaceConfigure(int32_t width, else state_ = PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL; - if (old_state != state_) - delegate_->OnWindowStateChanged(state_); + // Update state before notifying delegate. + const bool did_active_change = is_active_ != is_activated; + is_active_ = is_activated; // Rather than call SetBounds here for every configure event, just save the // most recent bounds, and have WaylandConnection call ApplyPendingBounds // when it has finished processing events. We may get many configure events // in a row during an interactive resize, and only the last one matters. SetPendingBounds(width, height); + + if (old_state != state_) + delegate_->OnWindowStateChanged(state_); + + if (did_active_change) + delegate_->OnActivationChanged(is_active_); } void WaylandWindow::OnCloseRequest() { diff --git a/chromium/ui/ozone/platform/wayland/wayland_window.h b/chromium/ui/ozone/platform/wayland/wayland_window.h index 8d205fa48b4..b534566ea63 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_window.h +++ b/chromium/ui/ozone/platform/wayland/wayland_window.h @@ -43,6 +43,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Set whether this window has pointer focus and should dispatch mouse events. void set_pointer_focus(bool focus) { has_pointer_focus_ = focus; } + bool has_pointer_focus() const { return has_pointer_focus_; } // Set whether this window has keyboard focus and should dispatch key events. void set_keyboard_focus(bool focus) { has_keyboard_focus_ = focus; } @@ -50,6 +51,13 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Set whether this window has touch focus and should dispatch touch events. void set_touch_focus(bool focus) { has_touch_focus_ = focus; } + // Set whether this window has an implicit grab (often referred to as capture + // in Chrome code). Implicit grabs happen while a pointer is down. + void set_has_implicit_grab(bool value) { has_implicit_grab_ = value; } + bool has_implicit_grab() const { return has_implicit_grab_; } + + bool is_active() const { return is_active_; } + // PlatformWindow void Show() override; void Hide() override; @@ -60,6 +68,7 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { void SetTitle(const base::string16& title) override; void SetCapture() override; void ReleaseCapture() override; + bool HasCapture() const override; void ToggleFullscreen() override; void Maximize() override; void Minimize() override; @@ -113,10 +122,13 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { bool has_pointer_focus_ = false; bool has_keyboard_focus_ = false; bool has_touch_focus_ = false; + bool has_implicit_grab_ = false; // Stores current states of the window. ui::PlatformWindowState state_; + bool is_active_ = false; + DISALLOW_COPY_AND_ASSIGN(WaylandWindow); }; diff --git a/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc index 2c2e2679a2a..77c1084fac8 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc @@ -2,6 +2,8 @@ // 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_window.h" + #include <wayland-server-core.h> #include <xdg-shell-unstable-v5-server-protocol.h> #include <xdg-shell-unstable-v6-server-protocol.h> @@ -14,7 +16,6 @@ #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_window.h" #include "ui/ozone/test/mock_platform_window_delegate.h" using ::testing::Eq; @@ -28,18 +29,18 @@ namespace ui { class WaylandWindowTest : public WaylandTest { public: WaylandWindowTest() - : test_mouse_event(ET_MOUSE_PRESSED, - gfx::Point(10, 15), - gfx::Point(10, 15), - ui::EventTimeStampFromSeconds(123456), - EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, - EF_LEFT_MOUSE_BUTTON) {} + : test_mouse_event_(ET_MOUSE_PRESSED, + gfx::Point(10, 15), + gfx::Point(10, 15), + ui::EventTimeStampFromSeconds(123456), + EF_LEFT_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, + EF_LEFT_MOUSE_BUTTON) {} void SetUp() override { WaylandTest::SetUp(); - xdg_surface = surface->xdg_surface.get(); - ASSERT_TRUE(xdg_surface); + xdg_surface_ = surface_->xdg_surface(); + ASSERT_TRUE(xdg_surface_); } protected: @@ -47,26 +48,26 @@ class WaylandWindowTest : public WaylandTest { int height, uint32_t serial, struct wl_array* states) { - if (!xdg_surface->xdg_toplevel) { - xdg_surface_send_configure(xdg_surface->resource(), width, height, states, - serial); + if (!xdg_surface_->xdg_toplevel()) { + xdg_surface_send_configure(xdg_surface_->resource(), width, height, + states, serial); return; } // In xdg_shell_v6, both surfaces send serial configure event and toplevel // surfaces send other data like states, heights and widths. - zxdg_surface_v6_send_configure(xdg_surface->resource(), serial); - ASSERT_TRUE(xdg_surface->xdg_toplevel); - zxdg_toplevel_v6_send_configure(xdg_surface->xdg_toplevel->resource(), + zxdg_surface_v6_send_configure(xdg_surface_->resource(), serial); + ASSERT_TRUE(xdg_surface_->xdg_toplevel()); + zxdg_toplevel_v6_send_configure(xdg_surface_->xdg_toplevel()->resource(), width, height, states); } - // Depending on a shell version, xdg_surface or xdg_toplevel surface should + // Depending on a shell version, xdg_surface_ or xdg_toplevel surface should // get the mock calls. This method decided, which surface to use. wl::MockXdgSurface* GetXdgSurface() { if (GetParam() == kXdgShellV5) - return xdg_surface; - return xdg_surface->xdg_toplevel.get(); + return xdg_surface_; + return xdg_surface_->xdg_toplevel(); } void SetWlArrayWithState(uint32_t state, wl_array* states) { @@ -80,9 +81,9 @@ class WaylandWindowTest : public WaylandTest { SetWlArrayWithState(XDG_SURFACE_STATE_ACTIVATED, states); } - wl::MockXdgSurface* xdg_surface; + wl::MockXdgSurface* xdg_surface_; - MouseEvent test_mouse_event; + MouseEvent test_mouse_event_; private: DISALLOW_COPY_AND_ASSIGN(WaylandWindowTest); @@ -90,25 +91,26 @@ class WaylandWindowTest : public WaylandTest { TEST_P(WaylandWindowTest, SetTitle) { EXPECT_CALL(*GetXdgSurface(), SetTitle(StrEq("hello"))); - window->SetTitle(base::ASCIIToUTF16("hello")); + window_->SetTitle(base::ASCIIToUTF16("hello")); } TEST_P(WaylandWindowTest, MaximizeAndRestore) { wl_array states; InitializeWlArrayWithActivatedState(&states); - EXPECT_CALL(delegate, + EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_MAXIMIZED))); SetWlArrayWithState(XDG_SURFACE_STATE_MAXIMIZED, &states); EXPECT_CALL(*GetXdgSurface(), SetMaximized()); - window->Maximize(); + window_->Maximize(); SendConfigureEvent(0, 0, 1, &states); Sync(); - EXPECT_CALL(delegate, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); + EXPECT_CALL(delegate_, + OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); EXPECT_CALL(*GetXdgSurface(), UnsetMaximized()); - window->Restore(); + window_->Restore(); // Reinitialize wl_array, which removes previous old states. InitializeWlArrayWithActivatedState(&states); SendConfigureEvent(0, 0, 2, &states); @@ -120,7 +122,8 @@ TEST_P(WaylandWindowTest, Minimize) { wl_array_init(&states); // Initialize to normal first. - EXPECT_CALL(delegate, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); + EXPECT_CALL(delegate_, + OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); SendConfigureEvent(0, 0, 1, &states); Sync(); @@ -129,8 +132,8 @@ TEST_P(WaylandWindowTest, Minimize) { // notified about the state, because 1) minimized state was set manually // in WaylandWindow, and it has been confirmed in a back call from the server, // which resulted in the same state as before. - EXPECT_CALL(delegate, OnWindowStateChanged(_)).Times(0); - window->Minimize(); + EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0); + window_->Minimize(); // Reinitialize wl_array, which removes previous old states. wl_array_init(&states); SendConfigureEvent(0, 0, 2, &states); @@ -144,15 +147,16 @@ TEST_P(WaylandWindowTest, SetFullscreenAndRestore) { SetWlArrayWithState(XDG_SURFACE_STATE_FULLSCREEN, &states); EXPECT_CALL(*GetXdgSurface(), SetFullscreen()); - EXPECT_CALL(delegate, + EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_FULLSCREEN))); - window->ToggleFullscreen(); + window_->ToggleFullscreen(); SendConfigureEvent(0, 0, 1, &states); Sync(); EXPECT_CALL(*GetXdgSurface(), UnsetFullscreen()); - EXPECT_CALL(delegate, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); - window->Restore(); + EXPECT_CALL(delegate_, + OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); + window_->Restore(); // Reinitialize wl_array, which removes previous old states. InitializeWlArrayWithActivatedState(&states); SendConfigureEvent(0, 0, 2, &states); @@ -164,25 +168,26 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { InitializeWlArrayWithActivatedState(&states); EXPECT_CALL(*GetXdgSurface(), SetMaximized()); - EXPECT_CALL(delegate, + EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_MAXIMIZED))); - window->Maximize(); + window_->Maximize(); SetWlArrayWithState(XDG_SURFACE_STATE_MAXIMIZED, &states); SendConfigureEvent(0, 0, 2, &states); Sync(); EXPECT_CALL(*GetXdgSurface(), SetFullscreen()); - EXPECT_CALL(delegate, + EXPECT_CALL(delegate_, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_FULLSCREEN))); - window->ToggleFullscreen(); + window_->ToggleFullscreen(); SetWlArrayWithState(XDG_SURFACE_STATE_FULLSCREEN, &states); SendConfigureEvent(0, 0, 3, &states); Sync(); EXPECT_CALL(*GetXdgSurface(), UnsetFullscreen()); EXPECT_CALL(*GetXdgSurface(), UnsetMaximized()); - EXPECT_CALL(delegate, OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); - window->Restore(); + EXPECT_CALL(delegate_, + OnWindowStateChanged(Eq(PLATFORM_WINDOW_STATE_NORMAL))); + window_->Restore(); // Reinitialize wl_array, which removes previous old states. InitializeWlArrayWithActivatedState(&states); SendConfigureEvent(0, 0, 4, &states); @@ -190,26 +195,26 @@ TEST_P(WaylandWindowTest, SetMaximizedFullscreenAndRestore) { } TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) { - const gfx::Rect current_bounds = window->GetBounds(); + const gfx::Rect current_bounds = window_->GetBounds(); wl_array states; InitializeWlArrayWithActivatedState(&states); const gfx::Rect maximized_bounds = gfx::Rect(0, 0, 1024, 768); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(maximized_bounds))); - window->Maximize(); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds))); + window_->Maximize(); SetWlArrayWithState(XDG_SURFACE_STATE_MAXIMIZED, &states); SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 1, &states); Sync(); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(current_bounds))); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(current_bounds))); // Both in XdgV5 and XdgV6, surfaces implement SetWindowGeometry method. // Thus, using a toplevel object in XdgV6 case is not right thing. Use a // surface here instead. - EXPECT_CALL(*xdg_surface, SetWindowGeometry(0, 0, current_bounds.width(), - current_bounds.height())); - window->Restore(); + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, current_bounds.width(), + current_bounds.height())); + window_->Restore(); // Reinitialize wl_array, which removes previous old states. InitializeWlArrayWithActivatedState(&states); SendConfigureEvent(0, 0, 2, &states); @@ -217,26 +222,26 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximize) { } TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) { - const gfx::Rect current_bounds = window->GetBounds(); + const gfx::Rect current_bounds = window_->GetBounds(); wl_array states; InitializeWlArrayWithActivatedState(&states); const gfx::Rect fullscreen_bounds = gfx::Rect(0, 0, 1280, 720); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(fullscreen_bounds))); - window->ToggleFullscreen(); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds))); + window_->ToggleFullscreen(); SetWlArrayWithState(XDG_SURFACE_STATE_FULLSCREEN, &states); SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 1, &states); Sync(); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(current_bounds))); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(current_bounds))); // Both in XdgV5 and XdgV6, surfaces implement SetWindowGeometry method. // Thus, using a toplevel object in XdgV6 case is not right thing. Use a // surface here instead. - EXPECT_CALL(*xdg_surface, SetWindowGeometry(0, 0, current_bounds.width(), - current_bounds.height())); - window->Restore(); + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, current_bounds.width(), + current_bounds.height())); + window_->Restore(); // Reinitialize wl_array, which removes previous old states. InitializeWlArrayWithActivatedState(&states); SendConfigureEvent(0, 0, 2, &states); @@ -244,29 +249,29 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterFullscreen) { } TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) { - const gfx::Rect current_bounds = window->GetBounds(); + const gfx::Rect current_bounds = window_->GetBounds(); wl_array states; InitializeWlArrayWithActivatedState(&states); const gfx::Rect maximized_bounds = gfx::Rect(0, 0, 1024, 768); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(maximized_bounds))); - window->Maximize(); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds))); + window_->Maximize(); SetWlArrayWithState(XDG_SURFACE_STATE_MAXIMIZED, &states); SendConfigureEvent(maximized_bounds.width(), maximized_bounds.height(), 1, &states); Sync(); const gfx::Rect fullscreen_bounds = gfx::Rect(0, 0, 1280, 720); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(fullscreen_bounds))); - window->ToggleFullscreen(); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(fullscreen_bounds))); + window_->ToggleFullscreen(); SetWlArrayWithState(XDG_SURFACE_STATE_FULLSCREEN, &states); SendConfigureEvent(fullscreen_bounds.width(), fullscreen_bounds.height(), 2, &states); Sync(); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(maximized_bounds))); - window->Maximize(); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(maximized_bounds))); + window_->Maximize(); // Reinitialize wl_array, which removes previous old states. InitializeWlArrayWithActivatedState(&states); SetWlArrayWithState(XDG_SURFACE_STATE_MAXIMIZED, &states); @@ -274,13 +279,13 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) { &states); Sync(); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(current_bounds))); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(current_bounds))); // Both in XdgV5 and XdgV6, surfaces implement SetWindowGeometry method. // Thus, using a toplevel object in XdgV6 case is not right thing. Use a // surface here instead. - EXPECT_CALL(*xdg_surface, SetWindowGeometry(0, 0, current_bounds.width(), - current_bounds.height())); - window->Restore(); + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, current_bounds.width(), + current_bounds.height())); + window_->Restore(); // Reinitialize wl_array, which removes previous old states. InitializeWlArrayWithActivatedState(&states); SendConfigureEvent(0, 0, 4, &states); @@ -288,19 +293,19 @@ TEST_P(WaylandWindowTest, RestoreBoundsAfterMaximizeAndFullscreen) { } TEST_P(WaylandWindowTest, SendsBoundsOnRequest) { - const gfx::Rect initial_bounds = window->GetBounds(); + const gfx::Rect initial_bounds = window_->GetBounds(); const gfx::Rect new_bounds = gfx::Rect(0, 0, initial_bounds.width() + 10, initial_bounds.height() + 10); - EXPECT_CALL(delegate, OnBoundsChanged(Eq(new_bounds))); - window->SetBounds(new_bounds); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(new_bounds))); + window_->SetBounds(new_bounds); wl_array states; InitializeWlArrayWithActivatedState(&states); // First case is when Wayland sends a configure event with 0,0 height and // widht. - EXPECT_CALL(*xdg_surface, + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, new_bounds.width(), new_bounds.height())) .Times(2); SendConfigureEvent(0, 0, 2, &states); @@ -314,17 +319,22 @@ TEST_P(WaylandWindowTest, SendsBoundsOnRequest) { } TEST_P(WaylandWindowTest, CanDispatchMouseEventDefault) { - EXPECT_FALSE(window->CanDispatchEvent(&test_mouse_event)); + EXPECT_FALSE(window_->CanDispatchEvent(&test_mouse_event_)); } TEST_P(WaylandWindowTest, CanDispatchMouseEventFocus) { - window->set_pointer_focus(true); - EXPECT_TRUE(window->CanDispatchEvent(&test_mouse_event)); + // set_pointer_focus(true) requires a WaylandPointer. + wl_seat_send_capabilities(server_.seat()->resource(), + WL_SEAT_CAPABILITY_POINTER); + Sync(); + ASSERT_TRUE(connection_->pointer()); + window_->set_pointer_focus(true); + EXPECT_TRUE(window_->CanDispatchEvent(&test_mouse_event_)); } TEST_P(WaylandWindowTest, CanDispatchMouseEventUnfocus) { - window->set_pointer_focus(false); - EXPECT_FALSE(window->CanDispatchEvent(&test_mouse_event)); + EXPECT_FALSE(window_->has_pointer_focus()); + EXPECT_FALSE(window_->CanDispatchEvent(&test_mouse_event_)); } ACTION_P(CloneEvent, ptr) { @@ -333,17 +343,47 @@ ACTION_P(CloneEvent, ptr) { TEST_P(WaylandWindowTest, DispatchEvent) { std::unique_ptr<Event> event; - EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); - window->DispatchEvent(&test_mouse_event); + EXPECT_CALL(delegate_, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + window_->DispatchEvent(&test_mouse_event_); ASSERT_TRUE(event); ASSERT_TRUE(event->IsMouseEvent()); auto* mouse_event = event->AsMouseEvent(); - EXPECT_EQ(mouse_event->location_f(), test_mouse_event.location_f()); - EXPECT_EQ(mouse_event->root_location_f(), test_mouse_event.root_location_f()); - EXPECT_EQ(mouse_event->time_stamp(), test_mouse_event.time_stamp()); - EXPECT_EQ(mouse_event->button_flags(), test_mouse_event.button_flags()); + EXPECT_EQ(mouse_event->location_f(), test_mouse_event_.location_f()); + EXPECT_EQ(mouse_event->root_location_f(), + test_mouse_event_.root_location_f()); + EXPECT_EQ(mouse_event->time_stamp(), test_mouse_event_.time_stamp()); + EXPECT_EQ(mouse_event->button_flags(), test_mouse_event_.button_flags()); EXPECT_EQ(mouse_event->changed_button_flags(), - test_mouse_event.changed_button_flags()); + test_mouse_event_.changed_button_flags()); +} + +TEST_P(WaylandWindowTest, HasCaptureUpdatedOnPointerEvents) { + wl_seat_send_capabilities(server_.seat()->resource(), + WL_SEAT_CAPABILITY_POINTER); + + Sync(); + + wl::MockPointer* pointer = server_.seat()->pointer(); + ASSERT_TRUE(pointer); + + wl_pointer_send_enter(pointer->resource(), 1, surface_->resource(), 0, 0); + Sync(); + EXPECT_FALSE(window_->HasCapture()); + + wl_pointer_send_button(pointer->resource(), 2, 1002, BTN_LEFT, + WL_POINTER_BUTTON_STATE_PRESSED); + Sync(); + EXPECT_TRUE(window_->HasCapture()); + + wl_pointer_send_motion(pointer->resource(), 1003, wl_fixed_from_int(400), + wl_fixed_from_int(500)); + Sync(); + EXPECT_TRUE(window_->HasCapture()); + + wl_pointer_send_button(pointer->resource(), 4, 1004, BTN_LEFT, + WL_POINTER_BUTTON_STATE_RELEASED); + Sync(); + EXPECT_FALSE(window_->HasCapture()); } TEST_P(WaylandWindowTest, ConfigureEvent) { @@ -354,14 +394,14 @@ TEST_P(WaylandWindowTest, ConfigureEvent) { // Make sure that the implementation does not call OnBoundsChanged for each // configure event if it receives multiple in a row. - EXPECT_CALL(delegate, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000)))); + EXPECT_CALL(delegate_, OnBoundsChanged(Eq(gfx::Rect(0, 0, 1500, 1000)))); // Responding to a configure event, the window geometry in here must respect // the sizing negotiations specified by the configure event. - // |xdg_surface| must receive the following calls in both xdg_shell_v5 and + // |xdg_surface_| must receive the following calls in both xdg_shell_v5 and // xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by - // xdg_toplevel in xdg_shell_v6 and by xdg_surface in xdg_shell_v5. - EXPECT_CALL(*xdg_surface, SetWindowGeometry(0, 0, 1500, 1000)).Times(1); - EXPECT_CALL(*xdg_surface, AckConfigure(13)); + // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5. + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 1500, 1000)).Times(1); + EXPECT_CALL(*xdg_surface_, AckConfigure(13)); } TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) { @@ -372,11 +412,31 @@ TEST_P(WaylandWindowTest, ConfigureEventWithNulledSize) { // call back with desired sizes. In this case, that's the actual size of // the window. SendConfigureEvent(0, 0, 14, &states); - // |xdg_surface| must receive the following calls in both xdg_shell_v5 and + // |xdg_surface_| must receive the following calls in both xdg_shell_v5 and // xdg_shell_v6. Other calls like SetTitle or SetMaximized are recieved by - // xdg_toplevel in xdg_shell_v6 and by xdg_surface in xdg_shell_v5. - EXPECT_CALL(*xdg_surface, SetWindowGeometry(0, 0, 800, 600)); - EXPECT_CALL(*xdg_surface, AckConfigure(14)); + // xdg_toplevel in xdg_shell_v6 and by xdg_surface_ in xdg_shell_v5. + EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, 800, 600)); + EXPECT_CALL(*xdg_surface_, AckConfigure(14)); +} + +TEST_P(WaylandWindowTest, OnActivationChanged) { + EXPECT_FALSE(window_->is_active()); + + { + wl_array states; + InitializeWlArrayWithActivatedState(&states); + EXPECT_CALL(delegate_, OnActivationChanged(Eq(true))); + SendConfigureEvent(0, 0, 1, &states); + Sync(); + EXPECT_TRUE(window_->is_active()); + } + + wl_array states; + wl_array_init(&states); + EXPECT_CALL(delegate_, OnActivationChanged(Eq(false))); + SendConfigureEvent(0, 0, 2, &states); + Sync(); + EXPECT_FALSE(window_->is_active()); } INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, diff --git a/chromium/ui/ozone/platform/x11/BUILD.gn b/chromium/ui/ozone/platform/x11/BUILD.gn index e73f2cf063b..24d53e267a5 100644 --- a/chromium/ui/ozone/platform/x11/BUILD.gn +++ b/chromium/ui/ozone/platform/x11/BUILD.gn @@ -59,12 +59,19 @@ source_set("x11_unittests") { testonly = true sources = [ "x11_cursor_factory_ozone_unittest.cc", + "x11_window_ozone_unittest.cc", ] deps = [ ":x11", + "//testing/gmock", "//testing/gtest", + "//ui/events:test_support", + "//ui/events/devices/x11", + "//ui/events/platform/x11", "//ui/ozone:platform", + "//ui/ozone:test_support", "//ui/ozone/common", + "//ui/platform_window/x11:x11", ] } diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc index 476f69ff287..9dd37001e47 100644 --- a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc +++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -7,7 +7,6 @@ #include <memory> #include <utility> -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/x/x11_util.h" @@ -108,15 +107,18 @@ class OzonePlatformX11 : public OzonePlatform { private: // Performs initialization steps need by both UI and GPU. void InitializeCommon(const InitParams& params) { - // TODO(kylechar): Add DCHECK we only enter InitializeCommon() twice for - // single process mode. if (common_initialized_) return; - // Always initialze in multi-thread mode, since this is used only during + // Always initialize in multi-thread mode, since this is used only during // development. XInitThreads(); + // If XOpenDisplay() failed there is nothing we can do. Crash here instead + // of crashing later. If you are crashing here, make sure there is an X + // server running and $DISPLAY is set. + CHECK(gfx::GetXDisplay()) << "Missing X server or $DISPLAY"; + ui::SetDefaultX11ErrorHandlers(); common_initialized_ = true; diff --git a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc index 95ec3306ad3..b0207c19c71 100644 --- a/chromium/ui/ozone/platform/x11/x11_surface_factory.cc +++ b/chromium/ui/ozone/platform/x11/x11_surface_factory.cc @@ -4,7 +4,6 @@ #include "ui/ozone/platform/x11/x11_surface_factory.h" -#include "base/memory/ptr_util.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_types.h" #include "ui/gl/gl_surface_egl.h" diff --git a/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.cc b/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.cc index 494c43cd6f2..100bc2e793e 100644 --- a/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.cc @@ -13,14 +13,20 @@ X11WindowManagerOzone::X11WindowManagerOzone() : event_grabber_(nullptr) {} X11WindowManagerOzone::~X11WindowManagerOzone() {} void X11WindowManagerOzone::GrabEvents(X11WindowOzone* window) { - if (event_grabber_ != window) + if (event_grabber_ == window) return; + + X11WindowOzone* old_grabber = event_grabber_; + if (old_grabber) + old_grabber->OnLostCapture(); + event_grabber_ = window; } void X11WindowManagerOzone::UngrabEvents(X11WindowOzone* window) { if (event_grabber_ != window) return; + event_grabber_->OnLostCapture(); event_grabber_ = nullptr; } diff --git a/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.h b/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.h index 884425c8571..81250ddf6ae 100644 --- a/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_window_manager_ozone.h @@ -16,11 +16,12 @@ class X11WindowManagerOzone { X11WindowManagerOzone(); ~X11WindowManagerOzone(); - // Tries to set a given X11WindowOzone as the recipient for events. It will - // fail if there is already another X11WindowOzone as recipient. + // Sets a given X11WindowOzone as the recipient for events and calls + // OnLostCapture for another |event_grabber_| if it has been set previously. void GrabEvents(X11WindowOzone* window); - // Unsets a given X11WindowOzone as the recipient for events. + // Unsets a given X11WindowOzone as the recipient for events and calls + // OnLostCapture. void UngrabEvents(X11WindowOzone* window); // Gets the current X11WindowOzone recipient of mouse events. diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone.cc b/chromium/ui/ozone/platform/x11/x11_window_ozone.cc index 1ce459877dc..c3f17315009 100644 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone.cc +++ b/chromium/ui/ozone/platform/x11/x11_window_ozone.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "ui/events/event.h" +#include "ui/events/event_utils.h" #include "ui/events/ozone/events_ozone.h" #include "ui/events/platform/x11/x11_event_source.h" #include "ui/gfx/geometry/point.h" @@ -68,17 +69,33 @@ bool X11WindowOzone::DispatchXEvent(XEvent* xev) { return true; } -bool X11WindowOzone::CanDispatchEvent(const PlatformEvent& platform_event) { +bool X11WindowOzone::CanDispatchEvent(const PlatformEvent& event) { return handle_next_event_; } -uint32_t X11WindowOzone::DispatchEvent(const PlatformEvent& platform_event) { - // This is unfortunately needed otherwise events that depend on global state - // (eg. double click) are broken. - DispatchEventFromNativeUiEvent( - platform_event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent, - base::Unretained(delegate()))); - return POST_DISPATCH_STOP_PROPAGATION; +uint32_t X11WindowOzone::DispatchEvent(const PlatformEvent& event) { + if (!window_manager_->event_grabber() || + window_manager_->event_grabber() == this) { + // This is unfortunately needed otherwise events that depend on global state + // (eg. double click) are broken. + DispatchEventFromNativeUiEvent( + event, base::BindOnce(&PlatformWindowDelegate::DispatchEvent, + base::Unretained(delegate()))); + return POST_DISPATCH_STOP_PROPAGATION; + } + + if (event->IsLocatedEvent()) { + // Another X11WindowOzone has installed itself as capture. Translate the + // event's location and dispatch to the other. + ConvertEventLocationToTargetWindowLocation( + window_manager_->event_grabber()->GetBounds().origin(), + GetBounds().origin(), event->AsLocatedEvent()); + } + return window_manager_->event_grabber()->DispatchEvent(event); +} + +void X11WindowOzone::OnLostCapture() { + delegate()->OnLostCapture(); } } // namespace ui diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone.h b/chromium/ui/ozone/platform/x11/x11_window_ozone.h index 48568ca420c..e1586341f19 100644 --- a/chromium/ui/ozone/platform/x11/x11_window_ozone.h +++ b/chromium/ui/ozone/platform/x11/x11_window_ozone.h @@ -24,6 +24,9 @@ class X11WindowOzone : public X11WindowBase, const gfx::Rect& bounds); ~X11WindowOzone() override; + // Called by |window_manager_| once capture is set to another X11WindowOzone. + void OnLostCapture(); + // PlatformWindow: void PrepareForShutdown() override; void SetCapture() override; diff --git a/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc new file mode 100644 index 00000000000..f770a61a243 --- /dev/null +++ b/chromium/ui/ozone/platform/x11/x11_window_ozone_unittest.cc @@ -0,0 +1,150 @@ +// 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/ozone/platform/x11/x11_window_ozone.h" + +#include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/devices/x11/touch_factory_x11.h" +#include "ui/events/event.h" +#include "ui/events/platform/x11/x11_event_source_libevent.h" +#include "ui/events/test/events_test_utils_x11.h" +#include "ui/ozone/platform/x11/x11_window_manager_ozone.h" +#include "ui/ozone/test/mock_platform_window_delegate.h" +#include "ui/platform_window/platform_window_delegate.h" + +namespace ui { + +namespace { + +using ::testing::Eq; +using ::testing::_; + +constexpr int kPointerDeviceId = 1; + +ACTION_P(StoreWidget, widget_ptr) { + *widget_ptr = arg0; +} + +ACTION_P(CloneEvent, event_ptr) { + *event_ptr = Event::Clone(*arg0); +} + +} // namespace + +class X11WindowOzoneTest : public testing::Test { + public: + X11WindowOzoneTest() + : task_env_(std::make_unique<base::test::ScopedTaskEnvironment>( + base::test::ScopedTaskEnvironment::MainThreadType::UI)) {} + + ~X11WindowOzoneTest() override = default; + + void SetUp() override { + XDisplay* display = gfx::GetXDisplay(); + event_source_ = std::make_unique<X11EventSourceLibevent>(display); + window_manager_ = std::make_unique<X11WindowManagerOzone>(); + + TouchFactory::GetInstance()->SetPointerDeviceForTest({kPointerDeviceId}); + } + + protected: + std::unique_ptr<PlatformWindow> CreatePlatformWindow( + MockPlatformWindowDelegate* delegate, + const gfx::Rect& bounds, + gfx::AcceleratedWidget* widget) { + EXPECT_CALL(*delegate, OnAcceleratedWidgetAvailable(_, _)) + .WillOnce(StoreWidget(widget)); + auto window = std::make_unique<X11WindowOzone>(window_manager_.get(), + delegate, bounds); + return std::move(window); + } + + void DispatchXEvent(XEvent* event, gfx::AcceleratedWidget widget) { + DCHECK_EQ(GenericEvent, event->type); + XIDeviceEvent* device_event = + static_cast<XIDeviceEvent*>(event->xcookie.data); + device_event->event = widget; + event_source_->ProcessXEvent(event); + } + + private: + std::unique_ptr<base::test::ScopedTaskEnvironment> task_env_; + std::unique_ptr<X11WindowManagerOzone> window_manager_; + std::unique_ptr<X11EventSourceLibevent> event_source_; + + DISALLOW_COPY_AND_ASSIGN(X11WindowOzoneTest); +}; + +// This test ensures that events are handled by a right target(widget). +TEST_F(X11WindowOzoneTest, SendPlatformEventToRightTarget) { + MockPlatformWindowDelegate delegate; + gfx::AcceleratedWidget widget; + constexpr gfx::Rect bounds(30, 80, 800, 600); + auto window = CreatePlatformWindow(&delegate, bounds, &widget); + + ScopedXI2Event xi_event; + xi_event.InitGenericButtonEvent(kPointerDeviceId, ET_MOUSE_PRESSED, + gfx::Point(218, 290), EF_NONE); + + // First check events can be received by a target window. + std::unique_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + + DispatchXEvent(xi_event, widget); + EXPECT_EQ(ET_MOUSE_PRESSED, event->type()); + testing::Mock::VerifyAndClearExpectations(&delegate); + + MockPlatformWindowDelegate delegate_2; + gfx::AcceleratedWidget widget_2; + gfx::Rect bounds_2(525, 155, 296, 407); + + auto window_2 = CreatePlatformWindow(&delegate_2, bounds_2, &widget_2); + + // Check event goes to right target without capture being set. + event.reset(); + EXPECT_CALL(delegate, DispatchEvent(_)).Times(0); + EXPECT_CALL(delegate_2, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + + DispatchXEvent(xi_event, widget_2); + EXPECT_EQ(ET_MOUSE_PRESSED, event->type()); + + EXPECT_CALL(delegate, OnClosed()).Times(1); + EXPECT_CALL(delegate_2, OnClosed()).Times(1); +} + +// This test case ensures that events are consumed by a window with explicit +// capture, even though the event is sent to other window. +TEST_F(X11WindowOzoneTest, SendPlatformEventToCapturedWindow) { + MockPlatformWindowDelegate delegate; + gfx::AcceleratedWidget widget; + constexpr gfx::Rect bounds(30, 80, 800, 600); + EXPECT_CALL(delegate, OnClosed()).Times(1); + auto window = CreatePlatformWindow(&delegate, bounds, &widget); + + MockPlatformWindowDelegate delegate_2; + gfx::AcceleratedWidget widget_2; + gfx::Rect bounds_2(525, 155, 296, 407); + EXPECT_CALL(delegate_2, OnClosed()).Times(1); + auto window_2 = CreatePlatformWindow(&delegate_2, bounds_2, &widget_2); + + ScopedXI2Event xi_event; + xi_event.InitGenericButtonEvent(kPointerDeviceId, ET_MOUSE_PRESSED, + gfx::Point(218, 290), EF_NONE); + + // Set capture to the second window, but send an event to another window + // target. The event must have its location converted and received by the + // captured window instead. + window_2->SetCapture(); + std::unique_ptr<Event> event; + EXPECT_CALL(delegate, DispatchEvent(_)).Times(0); + EXPECT_CALL(delegate_2, DispatchEvent(_)).WillOnce(CloneEvent(&event)); + + DispatchXEvent(xi_event, widget); + EXPECT_EQ(ET_MOUSE_PRESSED, event->type()); + EXPECT_EQ(gfx::Point(-277, 215), event->AsLocatedEvent()->location()); +} + +} // namespace ui diff --git a/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.cc b/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.cc index 110f816c557..7d6428b48d8 100644 --- a/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.cc +++ b/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.cc @@ -4,50 +4,17 @@ #include "ui/ozone/public/client_native_pixmap_factory_ozone.h" -#include <memory> - -#include "base/memory/singleton.h" #include "base/trace_event/trace_event.h" -#include "ui/gfx/client_native_pixmap_factory.h" #include "ui/ozone/platform_object.h" #include "ui/ozone/platform_selection.h" namespace ui { -namespace { - -// Thread-safe owner of the gfx::ClientNativePixmapFactory. Not a LazyInstance -// because it uses PlatformObject<>::Create() for factory construction. -// TODO(jamescook|spang): This exists to solve a startup race for chrome with -// mash http://crbug.com/807781. Removing the factory entirely would be better, -// with something like http://crrev.com/c/899949. -class PixmapFactorySingleton { - public: - static PixmapFactorySingleton* GetInstance() { - return base::Singleton<PixmapFactorySingleton>::get(); - } - - private: - friend struct base::DefaultSingletonTraits<PixmapFactorySingleton>; - - PixmapFactorySingleton() { - TRACE_EVENT1("ozone", "CreateClientNativePixmapFactoryOzone", "platform", - GetOzonePlatformName()); - pixmap_factory_ = PlatformObject<gfx::ClientNativePixmapFactory>::Create(); - gfx::ClientNativePixmapFactory::SetInstance(pixmap_factory_.get()); - } - - std::unique_ptr<gfx::ClientNativePixmapFactory> pixmap_factory_; - - DISALLOW_COPY_AND_ASSIGN(PixmapFactorySingleton); -}; - -} // namespace -void CreateClientNativePixmapFactoryOzone() { - // Multiple threads may race to create the factory (e.g. when the UI service - // and ash service are running in the same process). Create the object as a - // side effect of a thread-safe singleton. - PixmapFactorySingleton::GetInstance(); +std::unique_ptr<gfx::ClientNativePixmapFactory> +CreateClientNativePixmapFactoryOzone() { + TRACE_EVENT1("ozone", "CreateClientNativePixmapFactoryOzone", "platform", + GetOzonePlatformName()); + return PlatformObject<gfx::ClientNativePixmapFactory>::Create(); } } // namespace ui diff --git a/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.h b/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.h index 29d6b5019e4..744e868ad85 100644 --- a/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.h +++ b/chromium/ui/ozone/public/client_native_pixmap_factory_ozone.h @@ -5,14 +5,13 @@ #ifndef UI_OZONE_PUBLIC_CLIENT_NATIVE_PIXMAP_FACTORY_OZONE_H_ #define UI_OZONE_PUBLIC_CLIENT_NATIVE_PIXMAP_FACTORY_OZONE_H_ +#include "ui/gfx/client_native_pixmap_factory.h" #include "ui/ozone/ozone_export.h" namespace ui { -// Creates a factory for pixmaps that can use be transported from the client to -// the GPU process using a low-level ozone-provided platform specific mechanism. -// The factory is installed as the gfx::ClientNativePixmapFactory instance. -OZONE_EXPORT void CreateClientNativePixmapFactoryOzone(); +OZONE_EXPORT std::unique_ptr<gfx::ClientNativePixmapFactory> +CreateClientNativePixmapFactoryOzone(); } // namespace ui diff --git a/chromium/ui/ozone/public/interfaces/BUILD.gn b/chromium/ui/ozone/public/interfaces/BUILD.gn index b9e7663b08c..ec7f11f2836 100644 --- a/chromium/ui/ozone/public/interfaces/BUILD.gn +++ b/chromium/ui/ozone/public/interfaces/BUILD.gn @@ -12,7 +12,7 @@ mojom("interfaces") { ] public_deps = [ - "//mojo/common:common_custom_types", + "//mojo/public/mojom/base", "//skia/public/interfaces:interfaces", "//ui/display/mojo:interfaces", "//ui/gfx/geometry/mojo", diff --git a/chromium/ui/ozone/public/interfaces/drm_device.mojom b/chromium/ui/ozone/public/interfaces/drm_device.mojom index eede42437b0..48ea6df2d89 100644 --- a/chromium/ui/ozone/public/interfaces/drm_device.mojom +++ b/chromium/ui/ozone/public/interfaces/drm_device.mojom @@ -4,8 +4,8 @@ module ui.ozone.mojom; -import "mojo/common/file.mojom"; -import "mojo/common/file_path.mojom"; +import "mojo/public/mojom/base/file.mojom"; +import "mojo/public/mojom/base/file_path.mojom"; import "ui/display/mojo/display_constants.mojom"; import "ui/display/mojo/display_mode.mojom"; import "ui/display/mojo/display_snapshot.mojom"; @@ -46,11 +46,11 @@ interface DrmDevice { (array<display.mojom.DisplaySnapshot> display_snapshots); // Transfers ownership of a DRM device to the GPU process. - AddGraphicsDevice(mojo.common.mojom.FilePath path, - mojo.common.mojom.File file); + AddGraphicsDevice(mojo_base.mojom.FilePath path, + mojo_base.mojom.File file); // Instructs the GPU to abandon a DRM device. - RemoveGraphicsDevice(mojo.common.mojom.FilePath path); + RemoveGraphicsDevice(mojo_base.mojom.FilePath path); // Instructs the GPU to disable a DRM device. DisableNativeDisplay(int64 display_id) => (int64 display_id, bool success); diff --git a/chromium/ui/ozone/public/ozone_gpu_test_helper.cc b/chromium/ui/ozone/public/ozone_gpu_test_helper.cc index cc89a0e3cac..3ade4fe580f 100644 --- a/chromium/ui/ozone/public/ozone_gpu_test_helper.cc +++ b/chromium/ui/ozone/public/ozone_gpu_test_helper.cc @@ -5,6 +5,7 @@ #include "ui/ozone/public/ozone_gpu_test_helper.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "ipc/ipc_listener.h" @@ -55,7 +56,7 @@ class FakeGpuProcess : public IPC::Channel { // IPC::Channel implementation: bool Send(IPC::Message* msg) override { ui_task_runner_->PostTask( - FROM_HERE, base::Bind(&DispatchToGpuPlatformSupportHostTask, msg)); + FROM_HERE, base::BindOnce(&DispatchToGpuPlatformSupportHostTask, msg)); return true; } @@ -109,16 +110,24 @@ bool OzoneGpuTestHelper::Initialize( fake_gpu_process_.reset(new FakeGpuProcess(ui_task_runner)); io_helper_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&FakeGpuProcess::InitOnIO, - base::Unretained(fake_gpu_process_.get()))); + FROM_HERE, base::BindOnce(&FakeGpuProcess::InitOnIO, + base::Unretained(fake_gpu_process_.get()))); fake_gpu_process_host_.reset(new FakeGpuProcessHost( ui_task_runner, io_helper_thread_->task_runner())); io_helper_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&FakeGpuProcessHost::InitOnIO, - base::Unretained(fake_gpu_process_host_.get()))); + FROM_HERE, + base::BindOnce(&FakeGpuProcessHost::InitOnIO, + base::Unretained(fake_gpu_process_host_.get()))); io_helper_thread_->FlushForTesting(); + // Give the UI thread a chance to run any tasks posted from the IO thread + // after the GPU process is launched. This is needed for Ozone DRM, see + // https://crbug.com/830233. + base::RunLoop run_loop; + ui_task_runner->PostTask(FROM_HERE, run_loop.QuitClosure()); + run_loop.Run(); + return true; } diff --git a/chromium/ui/ozone/public/ozone_platform.cc b/chromium/ui/ozone/public/ozone_platform.cc index 58c114ba430..2457d0b89e2 100644 --- a/chromium/ui/ozone/public/ozone_platform.cc +++ b/chromium/ui/ozone/public/ozone_platform.cc @@ -42,8 +42,11 @@ void OzonePlatform::InitializeForUI(const InitParams& args) { EnsureInstance(); if (g_platform_initialized_ui) return; - g_platform_initialized_ui = true; instance_->InitializeUI(args); + { + base::AutoLock lock(GetOzoneInstanceLock()); + g_platform_initialized_ui = true; + } // This is deliberately created after initializing so that the platform can // create its own version of DDM. DeviceDataManager::CreateInstance(); @@ -56,8 +59,11 @@ void OzonePlatform::InitializeForGPU(const InitParams& args) { EnsureInstance(); if (g_platform_initialized_gpu) return; - g_platform_initialized_gpu = true; instance_->InitializeGPU(args); + { + base::AutoLock lock(GetOzoneInstanceLock()); + g_platform_initialized_gpu = true; + } if (!args.single_process && !instance_callback.Get().is_null()) std::move(instance_callback.Get()).Run(instance_); } diff --git a/chromium/ui/ozone/public/ozone_platform.h b/chromium/ui/ozone/public/ozone_platform.h index f715a6af03a..008e2c20d29 100644 --- a/chromium/ui/ozone/public/ozone_platform.h +++ b/chromium/ui/ozone/public/ozone_platform.h @@ -13,7 +13,6 @@ #include "services/service_manager/public/cpp/binder_registry.h" #include "ui/events/system_input_injector.h" #include "ui/ozone/ozone_export.h" -//#include "ui/ozone/public/interfaces/drm_device.mojom.h" namespace display { class NativeDisplayDelegate; @@ -78,9 +77,9 @@ class OZONE_EXPORT OzonePlatform { // split between a host and viz specific portion. bool single_process = false; - // Setting this to true indicates that the platform implementation should - // use mojo. Setting this to true requires calling |AddInterfaces| - // afterwards in the Viz process and providing a connector as part + // Setting this to true indicates that the platform implementation should + // use mojo. Setting this to true requires calling |AddInterfaces| + // afterwards in the Viz process and providing a connector as part. bool using_mojo = false; }; diff --git a/chromium/ui/platform_window/android/platform_window_android.cc b/chromium/ui/platform_window/android/platform_window_android.cc index c32b24a3dac..ed53df532b3 100644 --- a/chromium/ui/platform_window/android/platform_window_android.cc +++ b/chromium/ui/platform_window/android/platform_window_android.cc @@ -196,6 +196,11 @@ void PlatformWindowAndroid::ReleaseCapture() { NOTIMPLEMENTED(); } +bool PlatformWindowAndroid::HasCapture() const { + NOTIMPLEMENTED(); + return false; +} + void PlatformWindowAndroid::ToggleFullscreen() { NOTIMPLEMENTED(); } diff --git a/chromium/ui/platform_window/android/platform_window_android.h b/chromium/ui/platform_window/android/platform_window_android.h index 599ff71883f..803ab18de84 100644 --- a/chromium/ui/platform_window/android/platform_window_android.h +++ b/chromium/ui/platform_window/android/platform_window_android.h @@ -71,6 +71,7 @@ class ANDROID_WINDOW_EXPORT PlatformWindowAndroid : public PlatformWindow { void SetTitle(const base::string16& title) override; void SetCapture() override; void ReleaseCapture() override; + bool HasCapture() const override; void ToggleFullscreen() override; void Maximize() override; void Minimize() override; diff --git a/chromium/ui/platform_window/platform_window.h b/chromium/ui/platform_window/platform_window.h index bd8dbb01ef9..1af4694264a 100644 --- a/chromium/ui/platform_window/platform_window.h +++ b/chromium/ui/platform_window/platform_window.h @@ -11,6 +11,7 @@ #include "ui/base/cursor/cursor.h" namespace gfx { +class Point; class Rect; } @@ -44,6 +45,7 @@ class PlatformWindow { virtual void SetCapture() = 0; virtual void ReleaseCapture() = 0; + virtual bool HasCapture() const = 0; virtual void ToggleFullscreen() = 0; virtual void Maximize() = 0; diff --git a/chromium/ui/platform_window/stub/stub_window.cc b/chromium/ui/platform_window/stub/stub_window.cc index e646ca49792..d5fc54cca11 100644 --- a/chromium/ui/platform_window/stub/stub_window.cc +++ b/chromium/ui/platform_window/stub/stub_window.cc @@ -53,6 +53,10 @@ void StubWindow::SetCapture() { void StubWindow::ReleaseCapture() { } +bool StubWindow::HasCapture() const { + return false; +} + void StubWindow::ToggleFullscreen() { } diff --git a/chromium/ui/platform_window/stub/stub_window.h b/chromium/ui/platform_window/stub/stub_window.h index fa01974c31f..c94a8473b7c 100644 --- a/chromium/ui/platform_window/stub/stub_window.h +++ b/chromium/ui/platform_window/stub/stub_window.h @@ -37,6 +37,7 @@ class STUB_WINDOW_EXPORT StubWindow : public PlatformWindow { void SetCapture() override; void ReleaseCapture() override; void ToggleFullscreen() override; + bool HasCapture() const override; void Maximize() override; void Minimize() override; void Restore() override; diff --git a/chromium/ui/platform_window/win/win_window.cc b/chromium/ui/platform_window/win/win_window.cc index 2f32390a5f5..39fd290bb0c 100644 --- a/chromium/ui/platform_window/win/win_window.cc +++ b/chromium/ui/platform_window/win/win_window.cc @@ -101,15 +101,19 @@ void WinWindow::SetTitle(const base::string16& title) { } void WinWindow::SetCapture() { - if (::GetCapture() != hwnd()) + if (!HasCapture()) ::SetCapture(hwnd()); } void WinWindow::ReleaseCapture() { - if (::GetCapture() == hwnd()) + if (HasCapture()) ::ReleaseCapture(); } +bool WinWindow::HasCapture() const { + return ::GetCapture() == hwnd(); +} + void WinWindow::ToggleFullscreen() {} void WinWindow::Maximize() {} diff --git a/chromium/ui/platform_window/win/win_window.h b/chromium/ui/platform_window/win/win_window.h index c6774fde675..c2f7c224c6a 100644 --- a/chromium/ui/platform_window/win/win_window.h +++ b/chromium/ui/platform_window/win/win_window.h @@ -36,6 +36,7 @@ class WIN_WINDOW_EXPORT WinWindow : public PlatformWindow, void SetTitle(const base::string16& title) override; void SetCapture() override; void ReleaseCapture() override; + bool HasCapture() const override; void ToggleFullscreen() override; void Maximize() override; void Minimize() override; diff --git a/chromium/ui/platform_window/x11/x11_window_base.cc b/chromium/ui/platform_window/x11/x11_window_base.cc index 75cc3eef6ab..b6c4214e1f1 100644 --- a/chromium/ui/platform_window/x11/x11_window_base.cc +++ b/chromium/ui/platform_window/x11/x11_window_base.cc @@ -224,6 +224,10 @@ void X11WindowBase::SetCapture() {} void X11WindowBase::ReleaseCapture() {} +bool X11WindowBase::HasCapture() const { + return false; +} + void X11WindowBase::ToggleFullscreen() { ui::SetWMSpecState(xwindow_, !IsFullscreen(), gfx::GetAtom("_NET_WM_STATE_FULLSCREEN"), x11::None); diff --git a/chromium/ui/platform_window/x11/x11_window_base.h b/chromium/ui/platform_window/x11/x11_window_base.h index b419dc19e8f..1a062b9290d 100644 --- a/chromium/ui/platform_window/x11/x11_window_base.h +++ b/chromium/ui/platform_window/x11/x11_window_base.h @@ -39,6 +39,7 @@ class X11_WINDOW_EXPORT X11WindowBase : public PlatformWindow { void SetTitle(const base::string16& title) override; void SetCapture() override; void ReleaseCapture() override; + bool HasCapture() const override; void ToggleFullscreen() override; void Maximize() override; void Minimize() override; diff --git a/chromium/ui/resources/BUILD.gn b/chromium/ui/resources/BUILD.gn index 8286ba501b5..c9ec04a2aea 100644 --- a/chromium/ui/resources/BUILD.gn +++ b/chromium/ui/resources/BUILD.gn @@ -5,7 +5,7 @@ import("//build/config/jumbo.gni") import("//tools/grit/grit_rule.gni") import("//tools/grit/repack.gni") -import("//ui/base/ui_features.gni") +import("//ui/webui/webui_features.gni") group("resources") { public_deps = [ @@ -191,7 +191,7 @@ repack("repack_ui_test_pak_100_percent") { ] if (!is_ios) { - deps += [ "//third_party/WebKit/public:resources_grit" ] + deps += [ "//third_party/blink/public:resources_grit" ] } if (!is_mac) { @@ -208,7 +208,7 @@ repack("repack_ui_test_pak_100_percent") { if (toolkit_views) { deps += [ "//ui/views/resources" ] sources += [ - "$root_gen_dir/blink/public/resources/blink_resources.pak", + "$root_gen_dir/third_party/blink/public/resources/blink_resources.pak", "$root_gen_dir/ui/views/resources/views_resources_100_percent.pak", ] } diff --git a/chromium/ui/resources/default_100_percent/common/emoji_menu_item.png b/chromium/ui/resources/default_100_percent/common/emoji_menu_item.png Binary files differnew file mode 100644 index 00000000000..a5750ab283e --- /dev/null +++ b/chromium/ui/resources/default_100_percent/common/emoji_menu_item.png diff --git a/chromium/ui/resources/default_200_percent/common/emoji_menu_item.png b/chromium/ui/resources/default_200_percent/common/emoji_menu_item.png Binary files differnew file mode 100644 index 00000000000..f490a52c25f --- /dev/null +++ b/chromium/ui/resources/default_200_percent/common/emoji_menu_item.png diff --git a/chromium/ui/resources/default_300_percent/common/emoji_menu_item.png b/chromium/ui/resources/default_300_percent/common/emoji_menu_item.png Binary files differnew file mode 100644 index 00000000000..10851aa55af --- /dev/null +++ b/chromium/ui/resources/default_300_percent/common/emoji_menu_item.png diff --git a/chromium/ui/resources/ui_resources.grd b/chromium/ui/resources/ui_resources.grd index 10be1c5aa10..6d38ed3f886 100644 --- a/chromium/ui/resources/ui_resources.grd +++ b/chromium/ui/resources/ui_resources.grd @@ -108,6 +108,7 @@ <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED" file="common/easy_unlock_unlocked.png" /> <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_HOVER" file="common/easy_unlock_unlocked_hover.png" /> <structure type="chrome_scaled_image" name="IDR_EASY_UNLOCK_UNLOCKED_PRESSED" file="common/easy_unlock_unlocked_pressed.png" /> + <structure type="chrome_scaled_image" name="IDR_EMOJI_FAVICON" file="common/emoji_menu_item.png" /> <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED" file="common/folder_closed.png" /> <structure type="chrome_scaled_image" name="IDR_FOLDER_CLOSED_RTL" file="common/folder_closed_rtl.png" /> <if expr="is_macosx"> diff --git a/chromium/ui/shell_dialogs/BUILD.gn b/chromium/ui/shell_dialogs/BUILD.gn index e5018b276b1..80b4c176b1c 100644 --- a/chromium/ui/shell_dialogs/BUILD.gn +++ b/chromium/ui/shell_dialogs/BUILD.gn @@ -89,6 +89,8 @@ if (is_mac) { mac_framework_bundle("shell_dialogs_unittests_bundle") { testonly = true + framework_version = "S" + framework_contents = [ "Resources" ] info_plist = "//ui/base/test/framework-Info.plist" deps = [ ":shell_dialogs_unittests_xibs", diff --git a/chromium/ui/shell_dialogs/base_shell_dialog_win.cc b/chromium/ui/shell_dialogs/base_shell_dialog_win.cc index 9ccd1bbc456..ab67062109f 100644 --- a/chromium/ui/shell_dialogs/base_shell_dialog_win.cc +++ b/chromium/ui/shell_dialogs/base_shell_dialog_win.cc @@ -64,6 +64,7 @@ void BaseShellDialogImpl::DisableOwner(HWND owner) { // 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); diff --git a/chromium/ui/shell_dialogs/run_all_unittests.cc b/chromium/ui/shell_dialogs/run_all_unittests.cc index 6b5b9bf19fb..90123181653 100644 --- a/chromium/ui/shell_dialogs/run_all_unittests.cc +++ b/chromium/ui/shell_dialogs/run_all_unittests.cc @@ -68,7 +68,7 @@ void ShellDialogsTestSuite::Shutdown() { int main(int argc, char** argv) { ShellDialogsTestSuite test_suite(argc, argv); - return base::LaunchUnitTests( - argc, argv, - base::Bind(&ShellDialogsTestSuite::Run, base::Unretained(&test_suite))); + return base::LaunchUnitTests(argc, argv, + base::BindOnce(&ShellDialogsTestSuite::Run, + base::Unretained(&test_suite))); } diff --git a/chromium/ui/shell_dialogs/select_file_dialog.cc b/chromium/ui/shell_dialogs/select_file_dialog.cc index 9b60383e547..70163ef65e1 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog.cc @@ -115,7 +115,7 @@ void SelectFileDialog::SelectFile( // that the listener is called asynchronously. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&SelectFileDialog::CancelFileSelection, this, params)); + base::BindOnce(&SelectFileDialog::CancelFileSelection, this, params)); return; } diff --git a/chromium/ui/shell_dialogs/select_file_dialog_win.cc b/chromium/ui/shell_dialogs/select_file_dialog_win.cc index be73a0677db..edbceef44b6 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_win.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog_win.cc @@ -289,6 +289,7 @@ class SelectFileDialogImpl : public ui::SelectFileDialog, // semantics for input paramaters as RunOpenFileDialog. bool RunOpenMultiFileDialog(const std::wstring& title, const std::wstring& filter, + const base::FilePath& initial_path, HWND owner, std::vector<base::FilePath>* paths); @@ -344,8 +345,8 @@ void SelectFileDialogImpl::SelectFileImpl( default_extension, BeginRun(owner), owner, params); execute_params.run_state.dialog_thread->task_runner()->PostTask( - FROM_HERE, base::Bind(&SelectFileDialogImpl::ExecuteSelectFile, this, - execute_params)); + FROM_HERE, base::BindOnce(&SelectFileDialogImpl::ExecuteSelectFile, this, + execute_params)); } bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { @@ -387,23 +388,25 @@ void SelectFileDialogImpl::ExecuteSelectFile( params.run_state.owner, &path); } else if (params.type == SELECT_OPEN_MULTI_FILE) { std::vector<base::FilePath> paths; - if (RunOpenMultiFileDialog(params.title, filter, + if (RunOpenMultiFileDialog(params.title, filter, path, params.run_state.owner, &paths)) { params.ui_task_runner->PostTask( - FROM_HERE, base::Bind(&SelectFileDialogImpl::MultiFilesSelected, this, - paths, params.params, params.run_state)); + FROM_HERE, + base::BindOnce(&SelectFileDialogImpl::MultiFilesSelected, this, paths, + params.params, params.run_state)); return; } } if (success) { params.ui_task_runner->PostTask( - FROM_HERE, base::Bind(&SelectFileDialogImpl::FileSelected, this, path, - filter_index, params.params, params.run_state)); + FROM_HERE, + base::BindOnce(&SelectFileDialogImpl::FileSelected, this, path, + filter_index, params.params, params.run_state)); } else { params.ui_task_runner->PostTask( - FROM_HERE, base::Bind(&SelectFileDialogImpl::FileNotSelected, this, - params.params, params.run_state)); + FROM_HERE, base::BindOnce(&SelectFileDialogImpl::FileNotSelected, this, + params.params, params.run_state)); } } @@ -597,13 +600,12 @@ bool SelectFileDialogImpl::RunSelectFolderDialog( return result; } -bool SelectFileDialogImpl::RunOpenFileDialog( - const std::wstring& title, - const std::wstring& filter, - HWND owner, - base::FilePath* path) { - // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory - // without having to close Chrome first. +bool SelectFileDialogImpl::RunOpenFileDialog(const std::wstring& title, + const std::wstring& filter, + HWND owner, + base::FilePath* path) { + // We use OFN_NOCHANGEDIR so that the user can rename or delete the + // directory without having to close Chrome first. ui::win::OpenFileName ofn(owner, OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR); if (!path->empty()) { if (IsDirectory(*path)) @@ -625,6 +627,7 @@ bool SelectFileDialogImpl::RunOpenFileDialog( bool SelectFileDialogImpl::RunOpenMultiFileDialog( const std::wstring& title, const std::wstring& filter, + const base::FilePath& initial_path, HWND owner, std::vector<base::FilePath>* paths) { // We use OFN_NOCHANGEDIR so that the user can rename or delete the directory @@ -633,7 +636,12 @@ bool SelectFileDialogImpl::RunOpenMultiFileDialog( OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT | OFN_NOCHANGEDIR); - + if (!initial_path.empty()) { + if (IsDirectory(initial_path)) + ofn.SetInitialSelection(initial_path, base::FilePath()); + else + ofn.SetInitialSelection(initial_path.DirName(), base::FilePath()); + } if (!filter.empty()) ofn.GetOPENFILENAME()->lpstrFilter = filter.c_str(); diff --git a/chromium/ui/snapshot/BUILD.gn b/chromium/ui/snapshot/BUILD.gn index f2379880561..840df0e7452 100644 --- a/chromium/ui/snapshot/BUILD.gn +++ b/chromium/ui/snapshot/BUILD.gn @@ -100,7 +100,7 @@ test("snapshot_unittests") { ":snapshot", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gtest", "//ui/base", diff --git a/chromium/ui/snapshot/snapshot_aura.cc b/chromium/ui/snapshot/snapshot_aura.cc index 8e3d10a794a..a5b80a03c42 100644 --- a/chromium/ui/snapshot/snapshot_aura.cc +++ b/chromium/ui/snapshot/snapshot_aura.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/memory/ptr_util.h" #include "base/task_runner_util.h" #include "components/viz/common/frame_sinks/copy_output_request.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -59,7 +58,7 @@ static void FinishedAsyncCopyRequest( aura::Window* window = tracker->windows()[0]; MakeAsyncCopyRequest( window->layer(), source_rect, - base::BindOnce(&FinishedAsyncCopyRequest, base::Passed(&tracker), + base::BindOnce(&FinishedAsyncCopyRequest, std::move(tracker), source_rect, std::move(callback), retry_count + 1)); return; } @@ -75,8 +74,8 @@ static void MakeInitialAsyncCopyRequest( tracker->Add(window); MakeAsyncCopyRequest( window->layer(), source_rect, - base::BindOnce(&FinishedAsyncCopyRequest, base::Passed(&tracker), - source_rect, std::move(callback), 0)); + base::BindOnce(&FinishedAsyncCopyRequest, std::move(tracker), source_rect, + std::move(callback), 0)); } void GrabWindowSnapshotAndScaleAsyncAura( diff --git a/chromium/ui/strings/translations/ui_strings_am.xtb b/chromium/ui/strings/translations/ui_strings_am.xtb index 1927ae2550e..b8481ee5bc2 100644 --- a/chromium/ui/strings/translations/ui_strings_am.xtb +++ b/chromium/ui/strings/translations/ui_strings_am.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">ተመለስ</translation> <translation id="3889424535448813030">ቀኝ ቀስት</translation> <translation id="3892641579809465218">የውስጥ ማሳያ</translation> +<translation id="3897092660631435901">ምናሌ</translation> <translation id="3909791450649380159">&ቁረጥ</translation> <translation id="3990502903496589789">የቀኝ ጠርዝ</translation> <translation id="4202807286478387388">ዝለል</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">ይፋ ማሳወቂያ ሦስት ማዕዘን</translation> <translation id="528468243742722775">ጨርስ</translation> <translation id="5329858601952122676">&ሠርዝ</translation> +<translation id="5463830097259460683">ስሜት ገላጭ ምስሎች እና ምልክቶች</translation> <translation id="5476505524087279545">አታመልክት</translation> <translation id="5574202486608032840">የ<ph name="IDS_SHORT_PRODUCT_OS_NAME" /> ሥርዓት</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 ደቂቃ ቀርቷል}one{# ደቂቃዎች ቀርቷል}other{# ደቂቃዎች ቀርተዋል}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> ቴባ</translation> +<translation id="7507604095951736240">ስሜት ገላጭ ምስል</translation> <translation id="7658239707568436148">ይቅር</translation> <translation id="7781829728241885113">ትናንት</translation> <translation id="7814458197256864873">&ቅዳ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ar.xtb b/chromium/ui/strings/translations/ui_strings_ar.xtb index 228d5cd924a..b3eb8f69c43 100644 --- a/chromium/ui/strings/translations/ui_strings_ar.xtb +++ b/chromium/ui/strings/translations/ui_strings_ar.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">الرجوع إلى الوراء</translation> <translation id="3889424535448813030">مفتاح سهم إلى اليمين</translation> <translation id="3892641579809465218">العرض الداخلي</translation> +<translation id="3897092660631435901">قائمة</translation> <translation id="3909791450649380159">&قص</translation> <translation id="3990502903496589789">الحافة اليسرى</translation> <translation id="4202807286478387388">الدخول</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">مثلث الإفصاح</translation> <translation id="528468243742722775">مفتاح End</translation> <translation id="5329858601952122676">&حذف</translation> +<translation id="5463830097259460683">الرموز التعبيرية والرموز</translation> <translation id="5476505524087279545">إزالة علامة الاختيار</translation> <translation id="5574202486608032840">نظام <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{يتبقى دقيقة واحدة}zero{يتبقى # من الدقائق}two{يتبقى دقيقتان (#)}few{يتبقى # دقائق}many{يتبقى # دقيقة}other{يتبقى # من الدقائق}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> تيرابايت</translation> +<translation id="7507604095951736240">الرموز التعبيرية</translation> <translation id="7658239707568436148">إلغاء</translation> <translation id="7781829728241885113">أمس</translation> <translation id="7814458197256864873">&نسخ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_bg.xtb b/chromium/ui/strings/translations/ui_strings_bg.xtb index bab7796969d..0aa6e52c4f5 100644 --- a/chromium/ui/strings/translations/ui_strings_bg.xtb +++ b/chromium/ui/strings/translations/ui_strings_bg.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Назад</translation> <translation id="3889424535448813030">Стрелка надясно</translation> <translation id="3892641579809465218">Показване на вътрешна информация</translation> +<translation id="3897092660631435901">Меню</translation> <translation id="3909791450649380159">Изрязва&не</translation> <translation id="3990502903496589789">Десн край</translation> <translation id="4202807286478387388">преминаване</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Триъгълник за разкриване на съдържание</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Изтриване</translation> +<translation id="5463830097259460683">Емоджи и символи</translation> <translation id="5476505524087279545">премахване на отметката</translation> <translation id="5574202486608032840">Система <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Остава 1 минута}other{Остават # минути}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> ТБ</translation> +<translation id="7507604095951736240">Емоджи</translation> <translation id="7658239707568436148">Отказ</translation> <translation id="7781829728241885113">Вчера</translation> <translation id="7814458197256864873">&Копиране</translation> diff --git a/chromium/ui/strings/translations/ui_strings_bn.xtb b/chromium/ui/strings/translations/ui_strings_bn.xtb index b9f1648d1fe..8475f3e90a7 100644 --- a/chromium/ui/strings/translations/ui_strings_bn.xtb +++ b/chromium/ui/strings/translations/ui_strings_bn.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">ফিরুন</translation> <translation id="3889424535448813030">Right Arrow</translation> <translation id="3892641579809465218">অভ্যন্তরীণ প্রদর্শন</translation> +<translation id="3897092660631435901">মেনু</translation> <translation id="3909791450649380159">কা&ট করুন</translation> <translation id="3990502903496589789">ডান প্রান্ত</translation> <translation id="4202807286478387388">লাফ দিন</translation> @@ -136,6 +137,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{১ মিনিট বাকি}one{# মিনিট বাকি}other{# মিনিট বাকি}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">ইমোজি</translation> <translation id="7658239707568436148">বাতিল</translation> <translation id="7781829728241885113">গতকাল</translation> <translation id="7814458197256864873">&কপি করুন</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ca.xtb b/chromium/ui/strings/translations/ui_strings_ca.xtb index b9815bdaf59..1e2c5d414a2 100644 --- a/chromium/ui/strings/translations/ui_strings_ca.xtb +++ b/chromium/ui/strings/translations/ui_strings_ca.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Enrere</translation> <translation id="3889424535448813030">Fletxa dreta</translation> <translation id="3892641579809465218">Pantalla interna</translation> +<translation id="3897092660631435901">Menú</translation> <translation id="3909791450649380159">Re&talla</translation> <translation id="3990502903496589789">Extrem dret</translation> <translation id="4202807286478387388">salta</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triangle desplegable</translation> <translation id="528468243742722775">Fi</translation> <translation id="5329858601952122676">&Suprimeix</translation> +<translation id="5463830097259460683">Emojis i símbols</translation> <translation id="5476505524087279545">desmarca</translation> <translation id="5574202486608032840">Sistema <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Retrocés</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">: <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minut restant}other{# minuts restants}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emojis</translation> <translation id="7658239707568436148">Cancel·la</translation> <translation id="7781829728241885113">Ahir</translation> <translation id="7814458197256864873">&Copia</translation> diff --git a/chromium/ui/strings/translations/ui_strings_cs.xtb b/chromium/ui/strings/translations/ui_strings_cs.xtb index b17832346ba..c0c2427efef 100644 --- a/chromium/ui/strings/translations/ui_strings_cs.xtb +++ b/chromium/ui/strings/translations/ui_strings_cs.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Zpět</translation> <translation id="3889424535448813030">Klávesa šipka vpravo</translation> <translation id="3892641579809465218">Interní displej</translation> +<translation id="3897092660631435901">Nabídka</translation> <translation id="3909791450649380159">Vyjmou&t</translation> <translation id="3990502903496589789">Pravý okraj</translation> <translation id="4202807286478387388">přejít</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Tlačítko k zobrazení skrytého obsahu</translation> <translation id="528468243742722775">Klávesa End</translation> <translation id="5329858601952122676">&Smazat</translation> +<translation id="5463830097259460683">Emodži a symboly</translation> <translation id="5476505524087279545">odstranit zaškrtnutí</translation> <translation id="5574202486608032840">Systém <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Zbývá 1 minuta}few{Zbývají # minuty}many{Zbývá # minuty}other{Zbývá # minut}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emodži</translation> <translation id="7658239707568436148">Zrušit</translation> <translation id="7781829728241885113">Včera</translation> <translation id="7814458197256864873">&Kopírovat</translation> diff --git a/chromium/ui/strings/translations/ui_strings_da.xtb b/chromium/ui/strings/translations/ui_strings_da.xtb index 7f9110a9eb2..fb2aaa73060 100644 --- a/chromium/ui/strings/translations/ui_strings_da.xtb +++ b/chromium/ui/strings/translations/ui_strings_da.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Tilbage</translation> <translation id="3889424535448813030">Højrepil</translation> <translation id="3892641579809465218">Intern skærm</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Kli&p</translation> <translation id="3990502903496589789">Højre kant</translation> <translation id="4202807286478387388">hop</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Trekant til at vise eller skjule indhold</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Slet</translation> +<translation id="5463830097259460683">Emojis og symboler</translation> <translation id="5476505524087279545">fjern markering</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" />-system</translation> <translation id="5583640892426849032">Returtast</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minut tilbage}one{# minutter tilbage}other{# minutter tilbage}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Annuller</translation> <translation id="7781829728241885113">I går</translation> <translation id="7814458197256864873">&Kopier</translation> diff --git a/chromium/ui/strings/translations/ui_strings_de.xtb b/chromium/ui/strings/translations/ui_strings_de.xtb index f01349c2950..f10b2bd7787 100644 --- a/chromium/ui/strings/translations/ui_strings_de.xtb +++ b/chromium/ui/strings/translations/ui_strings_de.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Zurück</translation> <translation id="3889424535448813030">Rechtspfeil</translation> <translation id="3892641579809465218">Interne Anzeige</translation> +<translation id="3897092660631435901">Menü</translation> <translation id="3909791450649380159">&Ausschneiden</translation> <translation id="3990502903496589789">Rechter Rand</translation> <translation id="4202807286478387388">springen</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Aufklappdreieck</translation> <translation id="528468243742722775">Ende</translation> <translation id="5329858601952122676">&Löschen</translation> +<translation id="5463830097259460683">Emojis && Symbole</translation> <translation id="5476505524087279545">Auswahl aufheben</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" />-System</translation> <translation id="5583640892426849032">Rücktaste</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 Minute übrig}other{# Minuten übrig}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emojis</translation> <translation id="7658239707568436148">Abbrechen</translation> <translation id="7781829728241885113">Gestern</translation> <translation id="7814458197256864873">&Kopieren</translation> diff --git a/chromium/ui/strings/translations/ui_strings_el.xtb b/chromium/ui/strings/translations/ui_strings_el.xtb index 1d1f001f571..2b56ba7d0bd 100644 --- a/chromium/ui/strings/translations/ui_strings_el.xtb +++ b/chromium/ui/strings/translations/ui_strings_el.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Πίσω</translation> <translation id="3889424535448813030">Δεξιό βέλος</translation> <translation id="3892641579809465218">Εσωτερική οθόνη</translation> +<translation id="3897092660631435901">Μενού</translation> <translation id="3909791450649380159">Απο&κοπή</translation> <translation id="3990502903496589789">Δεξιά άκρη</translation> <translation id="4202807286478387388">μεταπήδηση</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Τρίγωνο αποκάλυψης</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Διαγραφή</translation> +<translation id="5463830097259460683">Emoji και σύμβολα</translation> <translation id="5476505524087279545">απενεργοποίηση</translation> <translation id="5574202486608032840">Σύστημα <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Πλήκτρο Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Απομένει 1 λεπτό}other{Απομένουν # λεπτά}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Ακύρωση</translation> <translation id="7781829728241885113">Χθες</translation> <translation id="7814458197256864873">&Αντιγραφή</translation> diff --git a/chromium/ui/strings/translations/ui_strings_en-GB.xtb b/chromium/ui/strings/translations/ui_strings_en-GB.xtb index fd7b83c3ada..fe56a9e73c3 100644 --- a/chromium/ui/strings/translations/ui_strings_en-GB.xtb +++ b/chromium/ui/strings/translations/ui_strings_en-GB.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Back</translation> <translation id="3889424535448813030">Right Arrow</translation> <translation id="3892641579809465218">Internal Display</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Cu&t</translation> <translation id="3990502903496589789">Right Edge</translation> <translation id="4202807286478387388">jump</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Disclosure triangle</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Delete</translation> +<translation id="5463830097259460683">Emoji & Symbols</translation> <translation id="5476505524087279545">untick</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> system</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130"><ph name="MESSAGE" />..</translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minute left}other{# minutes left}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Cancel</translation> <translation id="7781829728241885113">Yesterday</translation> <translation id="7814458197256864873">&Copy</translation> diff --git a/chromium/ui/strings/translations/ui_strings_es-419.xtb b/chromium/ui/strings/translations/ui_strings_es-419.xtb index 2d51edc7332..4e9293a1b52 100644 --- a/chromium/ui/strings/translations/ui_strings_es-419.xtb +++ b/chromium/ui/strings/translations/ui_strings_es-419.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Atrás</translation> <translation id="3889424535448813030">Flecha derecha</translation> <translation id="3892641579809465218">Pantalla interna</translation> +<translation id="3897092660631435901">Menú</translation> <translation id="3909791450649380159">Cor&tar</translation> <translation id="3990502903496589789">Borde derecho</translation> <translation id="4202807286478387388">saltar</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triángulo desplegable</translation> <translation id="528468243742722775">Fin</translation> <translation id="5329858601952122676">&Suprimir</translation> +<translation id="5463830097259460683">Emoji y símbolos</translation> <translation id="5476505524087279545">desmarcar</translation> <translation id="5574202486608032840">Sistema <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Tecla de retroceso</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Falta 1 minuto.}other{Faltan # minutos.}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Cancelar</translation> <translation id="7781829728241885113">Ayer</translation> <translation id="7814458197256864873">&Copiar</translation> diff --git a/chromium/ui/strings/translations/ui_strings_es.xtb b/chromium/ui/strings/translations/ui_strings_es.xtb index 8d22d321cd7..e98988520e8 100644 --- a/chromium/ui/strings/translations/ui_strings_es.xtb +++ b/chromium/ui/strings/translations/ui_strings_es.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Atrás</translation> <translation id="3889424535448813030">Flecha derecha</translation> <translation id="3892641579809465218">Pantalla interna</translation> +<translation id="3897092660631435901">Menú</translation> <translation id="3909791450649380159">Cor&tar</translation> <translation id="3990502903496589789">Borde derecho</translation> <translation id="4202807286478387388">saltar</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triángulo de expansión</translation> <translation id="528468243742722775">Fin</translation> <translation id="5329858601952122676">&Suprimir</translation> +<translation id="5463830097259460683">Emojis y símbolos</translation> <translation id="5476505524087279545">desmarcar</translation> <translation id="5574202486608032840">Sistema <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Tecla de retroceso</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Queda 1 minuto}other{Quedan # minutos}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Cancelar</translation> <translation id="7781829728241885113">Ayer</translation> <translation id="7814458197256864873">&Copiar</translation> diff --git a/chromium/ui/strings/translations/ui_strings_et.xtb b/chromium/ui/strings/translations/ui_strings_et.xtb index f2da3fb29a3..b95bb2c95ff 100644 --- a/chromium/ui/strings/translations/ui_strings_et.xtb +++ b/chromium/ui/strings/translations/ui_strings_et.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Tagasi</translation> <translation id="3889424535448813030">Paremnool</translation> <translation id="3892641579809465218">Sisemine kuva</translation> +<translation id="3897092660631435901">Menüü</translation> <translation id="3909791450649380159">Lõ&ika</translation> <translation id="3990502903496589789">Parem serv</translation> <translation id="4202807286478387388">liigu</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Avalikustamise kolmnurk</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Kustuta</translation> +<translation id="5463830097259460683">Emotikonid ja sümbolid</translation> <translation id="5476505524087279545">eemalda mrgistus</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" />-i süsteem</translation> <translation id="5583640892426849032">Tagasilükkeklahv</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minut on jäänud}other{# minutit on jäänud}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emotikonid</translation> <translation id="7658239707568436148">Tühista</translation> <translation id="7781829728241885113">Eile</translation> <translation id="7814458197256864873">&Kopeeri</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fa.xtb b/chromium/ui/strings/translations/ui_strings_fa.xtb index ba3ceda90d6..a1973a57996 100644 --- a/chromium/ui/strings/translations/ui_strings_fa.xtb +++ b/chromium/ui/strings/translations/ui_strings_fa.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">بازگشت</translation> <translation id="3889424535448813030">پیکان راست</translation> <translation id="3892641579809465218">صفحه نمایش داخلی</translation> +<translation id="3897092660631435901">منو</translation> <translation id="3909791450649380159">&برش</translation> <translation id="3990502903496589789">حاشیه راست</translation> <translation id="4202807286478387388">پرش</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">مثلث افشا</translation> <translation id="528468243742722775">پایان</translation> <translation id="5329858601952122676">&حذف</translation> +<translation id="5463830097259460683">اموجی و نمادها</translation> <translation id="5476505524087279545">برداشتن علامت</translation> <translation id="5574202486608032840">سیستم <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{۱ دقیقه باقی مانده است}one{# دقیقه باقی مانده است}other{# دقیقه باقی مانده است}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> ترابایت</translation> +<translation id="7507604095951736240">اموجی</translation> <translation id="7658239707568436148">لغو</translation> <translation id="7781829728241885113">دیروز</translation> <translation id="7814458197256864873">&کپی</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fi.xtb b/chromium/ui/strings/translations/ui_strings_fi.xtb index 8a415d066cb..90cbcb38c58 100644 --- a/chromium/ui/strings/translations/ui_strings_fi.xtb +++ b/chromium/ui/strings/translations/ui_strings_fi.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Edellinen</translation> <translation id="3889424535448813030">Nuoli oik.</translation> <translation id="3892641579809465218">Sisäinen näyttö</translation> +<translation id="3897092660631435901">Valikko</translation> <translation id="3909791450649380159">L&eikkaa</translation> <translation id="3990502903496589789">Oikea reuna</translation> <translation id="4202807286478387388">siirry</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Näyttämiskolmio</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Poista</translation> +<translation id="5463830097259460683">Emojit ja merkit</translation> <translation id="5476505524087279545">poista valinta</translation> <translation id="5574202486608032840">Järjestelmä <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Askelpalautin</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minuutti jäljellä}other{# minuuttia jäljellä}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> Tt</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Peruuta</translation> <translation id="7781829728241885113">Eilen</translation> <translation id="7814458197256864873">K&opioi</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fil.xtb b/chromium/ui/strings/translations/ui_strings_fil.xtb index 6bee96d96f6..b28bc65213d 100644 --- a/chromium/ui/strings/translations/ui_strings_fil.xtb +++ b/chromium/ui/strings/translations/ui_strings_fil.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Bumalik</translation> <translation id="3889424535448813030">Right Arrow</translation> <translation id="3892641579809465218">Panloob na Display</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Al&isin</translation> <translation id="3990502903496589789">Tamang Lamang</translation> <translation id="4202807286478387388">tumalon</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Disclosure triangle</translation> <translation id="528468243742722775">Wakas na</translation> <translation id="5329858601952122676">&Tanggalin</translation> +<translation id="5463830097259460683">Emoji at Mga Simbolo</translation> <translation id="5476505524087279545">i-uncheck</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> system</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minuto na lang ang natitira}one{# minuto na lang ang natitira}other{# na minuto na lang ang natitira}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> (na) TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Ikansela</translation> <translation id="7781829728241885113">Kahapon</translation> <translation id="7814458197256864873">&Kopyahin</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fr.xtb b/chromium/ui/strings/translations/ui_strings_fr.xtb index 7bc623e0894..0b81cef5b62 100644 --- a/chromium/ui/strings/translations/ui_strings_fr.xtb +++ b/chromium/ui/strings/translations/ui_strings_fr.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Retour</translation> <translation id="3889424535448813030">Droite</translation> <translation id="3892641579809465218">Affichage interne</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Cou&per</translation> <translation id="3990502903496589789">Côté droit</translation> <translation id="4202807286478387388">accéder</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triangle d'expansion</translation> <translation id="528468243742722775">Fin</translation> <translation id="5329858601952122676">&Supprimer</translation> +<translation id="5463830097259460683">Emoji et symboles</translation> <translation id="5476505524087279545">décocher</translation> <translation id="5574202486608032840">Système <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Retour</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minute restante}one{# minute restante}other{# minutes restantes}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> To</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Annuler</translation> <translation id="7781829728241885113">Hier</translation> <translation id="7814458197256864873">&Copier</translation> diff --git a/chromium/ui/strings/translations/ui_strings_gu.xtb b/chromium/ui/strings/translations/ui_strings_gu.xtb index 10b2bc01571..b49d4dac292 100644 --- a/chromium/ui/strings/translations/ui_strings_gu.xtb +++ b/chromium/ui/strings/translations/ui_strings_gu.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">પાછળ</translation> <translation id="3889424535448813030">જમણો એરો</translation> <translation id="3892641579809465218">આંતરિક પ્રદર્શન</translation> +<translation id="3897092660631435901">મેનૂ</translation> <translation id="3909791450649380159">કા&પો</translation> <translation id="3990502903496589789">જમણી કિનારી</translation> <translation id="4202807286478387388">જંપ કરો</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">પ્રકટીકરણ ત્રિકોણ</translation> <translation id="528468243742722775">સમાપ્ત</translation> <translation id="5329858601952122676">&કાઢી નાખો</translation> +<translation id="5463830097259460683">ઇમોજી અને પ્રતીકો</translation> <translation id="5476505524087279545">અનચેક કરો</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> સિસ્ટમ</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 મિનિટ બાકી}one{# મિનિટ બાકી}other{# મિનિટ બાકી}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">ઇમોજી</translation> <translation id="7658239707568436148">રદ કરો</translation> <translation id="7781829728241885113">ગઈ કાલે</translation> <translation id="7814458197256864873">&કૉપિ કરો</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hi.xtb b/chromium/ui/strings/translations/ui_strings_hi.xtb index aa6556d2906..18129b8ff45 100644 --- a/chromium/ui/strings/translations/ui_strings_hi.xtb +++ b/chromium/ui/strings/translations/ui_strings_hi.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">वापस</translation> <translation id="3889424535448813030">दायां तीर</translation> <translation id="3892641579809465218">आंतरिक डिस्प्ले</translation> +<translation id="3897092660631435901">मेनू</translation> <translation id="3909791450649380159">&काटें</translation> <translation id="3990502903496589789">दायां सिरा</translation> <translation id="4202807286478387388">जाएं</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">प्रकटीकरण त्रिकोण</translation> <translation id="528468243742722775">समाप्त</translation> <translation id="5329858601952122676">&हटाएं</translation> +<translation id="5463830097259460683">इमोजी और चिह्न</translation> <translation id="5476505524087279545">अनचेक करें</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> सिस्टम</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 मिनट शेष}one{# मिनट शेष}other{# मिनट शेष}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">इमोजी</translation> <translation id="7658239707568436148">अभी नहीं</translation> <translation id="7781829728241885113">बीता कल</translation> <translation id="7814458197256864873">&प्रतिलिपि बनाएं</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hr.xtb b/chromium/ui/strings/translations/ui_strings_hr.xtb index 6cf47fc3fa9..750a01f70e6 100644 --- a/chromium/ui/strings/translations/ui_strings_hr.xtb +++ b/chromium/ui/strings/translations/ui_strings_hr.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Natrag</translation> <translation id="3889424535448813030">Strelica desno</translation> <translation id="3892641579809465218">Unutarnji zaslon</translation> +<translation id="3897092660631435901">Izbornik</translation> <translation id="3909791450649380159">Iz&reži</translation> <translation id="3990502903496589789">Desni rub</translation> <translation id="4202807286478387388">skoči</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Trokut za otkrivanje</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Obriši</translation> +<translation id="5463830097259460683">Emoji i simboli</translation> <translation id="5476505524087279545">ukloni oznaku</translation> <translation id="5574202486608032840">Sustav <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Još 1 minuta}one{Još # minuta}few{Još # minute}other{Još # minuta}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Odustani</translation> <translation id="7781829728241885113">Danas</translation> <translation id="7814458197256864873">&Kopiraj</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hu.xtb b/chromium/ui/strings/translations/ui_strings_hu.xtb index 0fdafd9d858..1e80c7233ef 100644 --- a/chromium/ui/strings/translations/ui_strings_hu.xtb +++ b/chromium/ui/strings/translations/ui_strings_hu.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Vissza</translation> <translation id="3889424535448813030">Jobb nyíl</translation> <translation id="3892641579809465218">Belső kijelző</translation> +<translation id="3897092660631435901">Menü</translation> <translation id="3909791450649380159">Ki&vágás</translation> <translation id="3990502903496589789">Jobb sarok</translation> <translation id="4202807286478387388">Mehet</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Plusz tartalom kibontására szolgáló háromszög</translation> <translation id="528468243742722775">Befejezés</translation> <translation id="5329858601952122676">&Törlés</translation> +<translation id="5463830097259460683">Hangulatjelek és szimbólumok</translation> <translation id="5476505524087279545">Megjelölés eltávolítása</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> rendszer</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 perc van hátra}other{# perc van hátra}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Hangulatjel</translation> <translation id="7658239707568436148">Mégse</translation> <translation id="7781829728241885113">Tegnap</translation> <translation id="7814458197256864873">&Másolás</translation> diff --git a/chromium/ui/strings/translations/ui_strings_id.xtb b/chromium/ui/strings/translations/ui_strings_id.xtb index e1c9196b510..18ffa8fd29a 100644 --- a/chromium/ui/strings/translations/ui_strings_id.xtb +++ b/chromium/ui/strings/translations/ui_strings_id.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Mundur</translation> <translation id="3889424535448813030">Panah Kanan</translation> <translation id="3892641579809465218">Tampilan Internal</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Po&tong</translation> <translation id="3990502903496589789">Tepi Kanan</translation> <translation id="4202807286478387388">lompati</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Segitiga aksesibilitas</translation> <translation id="528468243742722775">Berakhir</translation> <translation id="5329858601952122676">&Hapus</translation> +<translation id="5463830097259460683">Emoji && Simbol</translation> <translation id="5476505524087279545">batalkan centang</translation> <translation id="5574202486608032840">Sistem <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130"><ph name="MESSAGE" /> -</translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 menit lagi}other{# menit lagi}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Batal</translation> <translation id="7781829728241885113">Kemarin</translation> <translation id="7814458197256864873">&Salin</translation> diff --git a/chromium/ui/strings/translations/ui_strings_it.xtb b/chromium/ui/strings/translations/ui_strings_it.xtb index 332b8572fe1..bc8a1fa0687 100644 --- a/chromium/ui/strings/translations/ui_strings_it.xtb +++ b/chromium/ui/strings/translations/ui_strings_it.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Indietro</translation> <translation id="3889424535448813030">Freccia destra</translation> <translation id="3892641579809465218">Display interno</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">T&aglia</translation> <translation id="3990502903496589789">Margine destro</translation> <translation id="4202807286478387388">vai</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triangolo di apertura</translation> <translation id="528468243742722775">Fine</translation> <translation id="5329858601952122676">&Elimina</translation> +<translation id="5463830097259460683">Emoji e simboli</translation> <translation id="5476505524087279545">deseleziona</translation> <translation id="5574202486608032840">Sistema <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minuto rimanente}other{# minuti rimanenti}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Annulla</translation> <translation id="7781829728241885113">Ieri</translation> <translation id="7814458197256864873">&Copia</translation> diff --git a/chromium/ui/strings/translations/ui_strings_iw.xtb b/chromium/ui/strings/translations/ui_strings_iw.xtb index 3bb12bb04df..2f8680de334 100644 --- a/chromium/ui/strings/translations/ui_strings_iw.xtb +++ b/chromium/ui/strings/translations/ui_strings_iw.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">חזור</translation> <translation id="3889424535448813030">חץ לימין</translation> <translation id="3892641579809465218">תצוגה פנימית</translation> +<translation id="3897092660631435901">תפריט</translation> <translation id="3909791450649380159">גז&ור</translation> <translation id="3990502903496589789">קצה ימני</translation> <translation id="4202807286478387388">קפוץ</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">משולש הצגה</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&מחק</translation> +<translation id="5463830097259460683">אמוג'י וסמלים</translation> <translation id="5476505524087279545">בטל סימון</translation> <translation id="5574202486608032840">מערכת <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{נותרה דקה אחת}two{נותרו # דקות}many{נותרו # דקות}other{נותרו # דקות}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">אמוג'י</translation> <translation id="7658239707568436148">ביטול</translation> <translation id="7781829728241885113">אתמול</translation> <translation id="7814458197256864873">&העתק</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ja.xtb b/chromium/ui/strings/translations/ui_strings_ja.xtb index ff073ffb2b4..5bfe1b27291 100644 --- a/chromium/ui/strings/translations/ui_strings_ja.xtb +++ b/chromium/ui/strings/translations/ui_strings_ja.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">戻る</translation> <translation id="3889424535448813030">右矢印キー</translation> <translation id="3892641579809465218">内蔵ディスプレイ</translation> +<translation id="3897092660631435901">メニュー</translation> <translation id="3909791450649380159">切り取り(&T)</translation> <translation id="3990502903496589789">右端</translation> <translation id="4202807286478387388">ジャンプ</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">三角形の展開ボタン</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">削除(&D)</translation> +<translation id="5463830097259460683">絵文字と記号</translation> <translation id="5476505524087279545">チェックを外す</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> システム</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{残り 1 分}other{残り # 分}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">絵文字</translation> <translation id="7658239707568436148">キャンセル</translation> <translation id="7781829728241885113">昨日</translation> <translation id="7814458197256864873">コピー(&C)</translation> diff --git a/chromium/ui/strings/translations/ui_strings_kn.xtb b/chromium/ui/strings/translations/ui_strings_kn.xtb index 28bd579f118..41c4bf81481 100644 --- a/chromium/ui/strings/translations/ui_strings_kn.xtb +++ b/chromium/ui/strings/translations/ui_strings_kn.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">ಹಿಂದೆ</translation> <translation id="3889424535448813030">ಬಲ ಬಾಣದ ಗುರುತು</translation> <translation id="3892641579809465218">ಆಂತರಿಕ ಪ್ರದರ್ಶನ</translation> +<translation id="3897092660631435901">ಮೆನು</translation> <translation id="3909791450649380159">ಕತ್ತರಿ&ಸು</translation> <translation id="3990502903496589789">ಬಲ ತುದಿ</translation> <translation id="4202807286478387388">ಹಾರು</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">ತ್ರಿಕೋನ ಪ್ರಕಟಣೆ</translation> <translation id="528468243742722775">ಅಂತ್ಯ</translation> <translation id="5329858601952122676">&ಅಳಿಸು</translation> +<translation id="5463830097259460683">ಎಮೋಜಿ ಮತ್ತು ಸಂಕೇತಗಳು</translation> <translation id="5476505524087279545">ಪರೀಕ್ಷಿಸಬೇಡಿ</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> ಸಿಸ್ಟಂ</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130"><ph name="MESSAGE" /> -</translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 ನಿಮಿಷ ಉಳಿದಿದೆ}one{# ನಿಮಿಷಗಳು ಉಳಿದಿವೆ}other{# ನಿಮಿಷಗಳು ಉಳಿದಿವೆ}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">ಎಮೋಜಿ</translation> <translation id="7658239707568436148">ರದ್ದುಮಾಡಿ</translation> <translation id="7781829728241885113">ನಿನ್ನೆ</translation> <translation id="7814458197256864873">&ನಕಲಿಸಿ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ko.xtb b/chromium/ui/strings/translations/ui_strings_ko.xtb index b0e7af9a5a5..c2c66637c69 100644 --- a/chromium/ui/strings/translations/ui_strings_ko.xtb +++ b/chromium/ui/strings/translations/ui_strings_ko.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">뒤로</translation> <translation id="3889424535448813030">오른쪽 화살표</translation> <translation id="3892641579809465218">내부 디스플레이</translation> +<translation id="3897092660631435901">메뉴</translation> <translation id="3909791450649380159">잘라내기(&T)</translation> <translation id="3990502903496589789">오른쪽 모서리</translation> <translation id="4202807286478387388">건너뛰기</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">펼치기/접기 삼각형</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">삭제(&D)</translation> +<translation id="5463830097259460683">그림 이모티콘 && 기호</translation> <translation id="5476505524087279545">선택 해제</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> 시스템</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1분 남음}other{#분 남음}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" />TB</translation> +<translation id="7507604095951736240">그림 이모티콘</translation> <translation id="7658239707568436148">취소</translation> <translation id="7781829728241885113">어제</translation> <translation id="7814458197256864873">복사(&C)</translation> diff --git a/chromium/ui/strings/translations/ui_strings_lt.xtb b/chromium/ui/strings/translations/ui_strings_lt.xtb index fe75e98d64e..c7f1db3cf26 100644 --- a/chromium/ui/strings/translations/ui_strings_lt.xtb +++ b/chromium/ui/strings/translations/ui_strings_lt.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Grįžti</translation> <translation id="3889424535448813030">Rodyklė į dešinę</translation> <translation id="3892641579809465218">Vidinė pateiktis</translation> +<translation id="3897092660631435901">Meniu</translation> <translation id="3909791450649380159">Iškir&pti</translation> <translation id="3990502903496589789">Dešinysis kraštas</translation> <translation id="4202807286478387388">peršokti</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Paskelbimo trikampis</translation> <translation id="528468243742722775">Pabaiga</translation> <translation id="5329858601952122676">&Pašalinti</translation> +<translation id="5463830097259460683">Jaustukai ir simboliai</translation> <translation id="5476505524087279545">Nuimti žymėjimą</translation> <translation id="5574202486608032840">„<ph name="IDS_SHORT_PRODUCT_OS_NAME" />“ sistema</translation> <translation id="5583640892426849032">Grįžties klavišas</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Liko 1 minutė}one{Liko # minutė}few{Liko # minutės}many{Liko # minutės}other{Liko # minučių}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Jaustukai</translation> <translation id="7658239707568436148">Atšaukti</translation> <translation id="7781829728241885113">Vakar</translation> <translation id="7814458197256864873">&Kopijuoti</translation> diff --git a/chromium/ui/strings/translations/ui_strings_lv.xtb b/chromium/ui/strings/translations/ui_strings_lv.xtb index 66b620dbcc7..5159a73d256 100644 --- a/chromium/ui/strings/translations/ui_strings_lv.xtb +++ b/chromium/ui/strings/translations/ui_strings_lv.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Atpakaļ</translation> <translation id="3889424535448813030">Labā bulta</translation> <translation id="3892641579809465218">Iekšējais displejs</translation> +<translation id="3897092660631435901">Izvēlne</translation> <translation id="3909791450649380159">Izgrie&zt</translation> <translation id="3990502903496589789">Labā puse</translation> <translation id="4202807286478387388">lekt</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Atklāšanas trijstūris</translation> <translation id="528468243742722775">Beigas</translation> <translation id="5329858601952122676">Dzēst</translation> +<translation id="5463830097259460683">Emocijzīmes un simboli</translation> <translation id="5476505524087279545">neprbaudt</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> sistēma</translation> <translation id="5583640892426849032">Atkāpšanās taustiņš</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Atlikusi 1 minūte}zero{Atlikušas # minūtes}one{Atlikusi # minūte}other{Atlikušas # minūtes}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emocijzīmes</translation> <translation id="7658239707568436148">Atcelt</translation> <translation id="7781829728241885113">Vakar</translation> <translation id="7814458197256864873">Ko&pēt</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ml.xtb b/chromium/ui/strings/translations/ui_strings_ml.xtb index e70bb0a838f..bdc85332e76 100644 --- a/chromium/ui/strings/translations/ui_strings_ml.xtb +++ b/chromium/ui/strings/translations/ui_strings_ml.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">പിന്നോട്ട്</translation> <translation id="3889424535448813030">വലതുഭാഗത്തെ അമ്പടയാളം</translation> <translation id="3892641579809465218">ആന്തരിക പ്രദർശനം</translation> +<translation id="3897092660631435901">മെനു</translation> <translation id="3909791450649380159">&മുറിക്കുക</translation> <translation id="3990502903496589789">വലത് അഗ്രം</translation> <translation id="4202807286478387388">jump</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">ഡിസ്ക്ലോഷർ ത്രികോണം</translation> <translation id="528468243742722775">അവസാനം</translation> <translation id="5329858601952122676">&ഇല്ലാതാക്കൂ</translation> +<translation id="5463830097259460683">ഇമോജിയും ചിഹ്നങ്ങളും</translation> <translation id="5476505524087279545">അണ്ചെക്ക് ചെയ്യുക</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> സിസ്റ്റം</translation> <translation id="5583640892426849032">ബാക്ക്സ്പെയ്സ്</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{ഒരു മിനിറ്റ് ശേഷിക്കുന്നു}other{# മിനിറ്റ് ശേഷിക്കുന്നു}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">ഇമോജി</translation> <translation id="7658239707568436148">റദ്ദാക്കൂ</translation> <translation id="7781829728241885113">ഇന്നലെ</translation> <translation id="7814458197256864873">&പകര്ത്തൂ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_mr.xtb b/chromium/ui/strings/translations/ui_strings_mr.xtb index 7ae05d047ca..88b069f7341 100644 --- a/chromium/ui/strings/translations/ui_strings_mr.xtb +++ b/chromium/ui/strings/translations/ui_strings_mr.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">मागील</translation> <translation id="3889424535448813030">Right Arrow</translation> <translation id="3892641579809465218">अंतर्गत डिस्प्ले</translation> +<translation id="3897092660631435901">मेनू</translation> <translation id="3909791450649380159">क&ट करा</translation> <translation id="3990502903496589789">उजवा काठ</translation> <translation id="4202807286478387388">जंप करा</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">प्रकटन त्रिकोण</translation> <translation id="528468243742722775">समाप्त</translation> <translation id="5329858601952122676">&हटवा</translation> +<translation id="5463830097259460683">इमोजी && चिन्हे</translation> <translation id="5476505524087279545">अनचेक</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> सिस्टम</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 मिनिट शिल्लक}one{# मिनिट शिल्लक}other{# मिनिटे शिल्लक}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">इमोजी</translation> <translation id="7658239707568436148">रद्द करा</translation> <translation id="7781829728241885113">काल</translation> <translation id="7814458197256864873">&कॉपी करा</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ms.xtb b/chromium/ui/strings/translations/ui_strings_ms.xtb index de467150e5a..fadd4aa7bca 100644 --- a/chromium/ui/strings/translations/ui_strings_ms.xtb +++ b/chromium/ui/strings/translations/ui_strings_ms.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Kembali</translation> <translation id="3889424535448813030">Anak Panah Kanan</translation> <translation id="3892641579809465218">Paparan Dalaman</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Po&tong</translation> <translation id="3990502903496589789">Tepi Kanan</translation> <translation id="4202807286478387388">lompat</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Segi tiga pendedahan</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Padam</translation> +<translation id="5463830097259460683">Emoji && Simbol</translation> <translation id="5476505524087279545">nyahpilih</translation> <translation id="5574202486608032840">Sistem <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Undur ruang</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minit lagi}other{# minit lagi}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Batal</translation> <translation id="7781829728241885113">Semalam</translation> <translation id="7814458197256864873">&Salin</translation> diff --git a/chromium/ui/strings/translations/ui_strings_nl.xtb b/chromium/ui/strings/translations/ui_strings_nl.xtb index 8d248d1be85..4b9446d6aa6 100644 --- a/chromium/ui/strings/translations/ui_strings_nl.xtb +++ b/chromium/ui/strings/translations/ui_strings_nl.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Vorige</translation> <translation id="3889424535448813030">Pijl-rechts</translation> <translation id="3892641579809465218">Interne display</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">&Knippen</translation> <translation id="3990502903496589789">Rechterzijde</translation> <translation id="4202807286478387388">Gaan naar</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Driehoek voor samen-/uitvouwen</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">Verwij&deren</translation> +<translation id="5463830097259460683">Emoji en symbolen</translation> <translation id="5476505524087279545">Deselecteren</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" />-systeem</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minuut resterend}other{# minuten resterend}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji's</translation> <translation id="7658239707568436148">Annuleren</translation> <translation id="7781829728241885113">Gisteren</translation> <translation id="7814458197256864873">&Kopiëren</translation> diff --git a/chromium/ui/strings/translations/ui_strings_no.xtb b/chromium/ui/strings/translations/ui_strings_no.xtb index 75a5e61eab4..e067e8cb72e 100644 --- a/chromium/ui/strings/translations/ui_strings_no.xtb +++ b/chromium/ui/strings/translations/ui_strings_no.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Tilbake</translation> <translation id="3889424535448813030">Pil høyre</translation> <translation id="3892641579809465218">Innebygd skjerm</translation> +<translation id="3897092660631435901">Meny</translation> <translation id="3909791450649380159">Klipp u&t</translation> <translation id="3990502903496589789">Høyre kant</translation> <translation id="4202807286478387388">hopp</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Trekant for mer innhold</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Slett</translation> +<translation id="5463830097259460683">Emoji og symboler</translation> <translation id="5476505524087279545">fjern merke</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" />-systemet</translation> <translation id="5583640892426849032">Tilbake-tasten</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minutt igjen}other{# minutter igjen}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Avbryt</translation> <translation id="7781829728241885113">I går</translation> <translation id="7814458197256864873">&Kopier</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pl.xtb b/chromium/ui/strings/translations/ui_strings_pl.xtb index 122dabf9eaa..1e13afa5ff4 100644 --- a/chromium/ui/strings/translations/ui_strings_pl.xtb +++ b/chromium/ui/strings/translations/ui_strings_pl.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Wstecz</translation> <translation id="3889424535448813030">Strzałka w prawo</translation> <translation id="3892641579809465218">Wyświetlacz wewnętrzny</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Wy&tnij</translation> <translation id="3990502903496589789">Krawędź po prawej</translation> <translation id="4202807286478387388">przejdź</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Trójkąt rozwinięcia</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Usuń</translation> +<translation id="5463830097259460683">Emotikony i symbole</translation> <translation id="5476505524087279545">odznacz</translation> <translation id="5574202486608032840">System <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Pozostała 1 minuta}few{Pozostały # minuty}many{Pozostały # minut}other{Pozostało # minuty}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emotikony</translation> <translation id="7658239707568436148">Anuluj</translation> <translation id="7781829728241885113">Wczoraj</translation> <translation id="7814458197256864873">&Kopiuj</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb index b6921f06ef2..81faabeb213 100644 --- a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb +++ b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Voltar</translation> <translation id="3889424535448813030">Seta para a direita</translation> <translation id="3892641579809465218">Display interno</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">&Recortar</translation> <translation id="3990502903496589789">Borda direita</translation> <translation id="4202807286478387388">pular</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triângulo de divulgação</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Excluir</translation> +<translation id="5463830097259460683">Emoticons e símbolos</translation> <translation id="5476505524087279545">desmarcar</translation> <translation id="5574202486608032840">Sistema <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Um minuto restante}one{# minutos restantes}other{# minutos restantes}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoticons</translation> <translation id="7658239707568436148">Cancelar</translation> <translation id="7781829728241885113">Ontem</translation> <translation id="7814458197256864873">Co&piar</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb index 161d2155cf8..99163a69641 100644 --- a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb +++ b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Anterior</translation> <translation id="3889424535448813030">Seta para a direita</translation> <translation id="3892641579809465218">Ecrã interno</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Cor&tar</translation> <translation id="3990502903496589789">Margem direita</translation> <translation id="4202807286478387388">ir para</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triângulo de divulgação</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">E&liminar</translation> +<translation id="5463830097259460683">Emoji e símbolos</translation> <translation id="5476505524087279545">desmarcar</translation> <translation id="5574202486608032840">Sistema <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Retrocesso</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Falta 1 minuto}other{Faltam # minutos}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Cancelar</translation> <translation id="7781829728241885113">Ontem</translation> <translation id="7814458197256864873">&Copiar</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ro.xtb b/chromium/ui/strings/translations/ui_strings_ro.xtb index 15f275f6fef..4c10c480920 100644 --- a/chromium/ui/strings/translations/ui_strings_ro.xtb +++ b/chromium/ui/strings/translations/ui_strings_ro.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Înapoi</translation> <translation id="3889424535448813030">Săgeată spre dreapta</translation> <translation id="3892641579809465218">Afișaj intern</translation> +<translation id="3897092660631435901">Meniu</translation> <translation id="3909791450649380159">&Taie</translation> <translation id="3990502903496589789">Marginea dreaptă</translation> <translation id="4202807286478387388">accesează</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Triunghi pentru afișare</translation> <translation id="528468243742722775">Sfârșit</translation> <translation id="5329858601952122676">&Șterge</translation> +<translation id="5463830097259460683">Emoji și simboluri</translation> <translation id="5476505524087279545">debifează</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Un minut rămas}few{# minute rămase}other{# de minute rămase}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Anulează</translation> <translation id="7781829728241885113">Ieri</translation> <translation id="7814458197256864873">&Copiază</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ru.xtb b/chromium/ui/strings/translations/ui_strings_ru.xtb index b15eefc26da..f68c6f150ac 100644 --- a/chromium/ui/strings/translations/ui_strings_ru.xtb +++ b/chromium/ui/strings/translations/ui_strings_ru.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Назад</translation> <translation id="3889424535448813030">Стрелка вправо</translation> <translation id="3892641579809465218">Встроенный дисплей</translation> +<translation id="3897092660631435901">Меню</translation> <translation id="3909791450649380159">Выре&зать</translation> <translation id="3990502903496589789">Правый край</translation> <translation id="4202807286478387388">перейти</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Треугольник развертывания</translation> <translation id="528468243742722775">Завершить</translation> <translation id="5329858601952122676">&Удалить</translation> +<translation id="5463830097259460683">Эмодзи и символы</translation> <translation id="5476505524087279545">снять галочку</translation> <translation id="5574202486608032840">Система <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Клавиша возврата (Backspace)</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Осталась 1 минута}one{Осталась # минута}few{Осталось # минуты}many{Осталось # минут}other{Осталось # минуты}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> ТБ</translation> +<translation id="7507604095951736240">Эмодзи</translation> <translation id="7658239707568436148">Отмена</translation> <translation id="7781829728241885113">Вчера</translation> <translation id="7814458197256864873">&Копировать</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sk.xtb b/chromium/ui/strings/translations/ui_strings_sk.xtb index 52a378f444e..f483bff60a9 100644 --- a/chromium/ui/strings/translations/ui_strings_sk.xtb +++ b/chromium/ui/strings/translations/ui_strings_sk.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Naspäť</translation> <translation id="3889424535448813030">Šípka doprava</translation> <translation id="3892641579809465218">Interný displej</translation> +<translation id="3897092660631435901">Ponuka</translation> <translation id="3909791450649380159">&Vystrihnúť</translation> <translation id="3990502903496589789">Pravý okraj</translation> <translation id="4202807286478387388">skok</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Trojuholníkové tlačidlo na zobrazenie skrytého obsahu</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Odstrániť</translation> +<translation id="5463830097259460683">Emodži a symboly</translation> <translation id="5476505524087279545">zrušiť označenie</translation> <translation id="5574202486608032840">Systém <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Zostáva 1 minúta}few{Zostávajú # minúty}many{Zostáva # minúty}other{Zostáva # minút}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emodži</translation> <translation id="7658239707568436148">Zrušiť</translation> <translation id="7781829728241885113">Včera</translation> <translation id="7814458197256864873">&Kopírovať</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sl.xtb b/chromium/ui/strings/translations/ui_strings_sl.xtb index 9b23c7aee33..01f3b4e90f8 100644 --- a/chromium/ui/strings/translations/ui_strings_sl.xtb +++ b/chromium/ui/strings/translations/ui_strings_sl.xtb @@ -59,6 +59,7 @@ <translation id="385051799172605136">Nazaj</translation> <translation id="3889424535448813030">Puščica desno</translation> <translation id="3892641579809465218">Notranji zaslon</translation> +<translation id="3897092660631435901">Meni</translation> <translation id="3909791450649380159">Izrež&i</translation> <translation id="3990502903496589789">Desni rob</translation> <translation id="4202807286478387388">skoči</translation> @@ -80,6 +81,7 @@ <translation id="520299402983819650"><ph name="QUANTITY" /> PB</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Izbriši</translation> +<translation id="5463830097259460683">Emodži in drugi znaki</translation> <translation id="5476505524087279545">počisti izbor</translation> <translation id="5583640892426849032">Vračalka</translation> <translation id="5613020302032141669">Puščica levo</translation> @@ -129,6 +131,7 @@ <translation id="7365057348334984696">{MINUTES,plural, =1{Pred 1 min}one{Pred # min}two{Pred # min}few{Pred # min}other{Pred # min}}</translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Še 1 min}one{Še # min}two{Še # min}few{Še # min}other{Še # min}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Prekliči</translation> <translation id="7781829728241885113">Včeraj</translation> <translation id="7814458197256864873">&Kopiraj</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sr.xtb b/chromium/ui/strings/translations/ui_strings_sr.xtb index bee93c7b1a1..9a0ac7ef98f 100644 --- a/chromium/ui/strings/translations/ui_strings_sr.xtb +++ b/chromium/ui/strings/translations/ui_strings_sr.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Назад</translation> <translation id="3889424535448813030">Стрелица надесно</translation> <translation id="3892641579809465218">Интерни екран</translation> +<translation id="3897092660631435901">Мени</translation> <translation id="3909791450649380159">Ис&еци</translation> <translation id="3990502903496589789">Десна ивица</translation> <translation id="4202807286478387388">прескочи</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Троугао за откривање</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Избриши</translation> +<translation id="5463830097259460683">Емоџи и симболи</translation> <translation id="5476505524087279545">опозови избор</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> систем</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Још 1 минут}one{Још # минут}few{Још # минута}other{Још # минута}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Емоџи</translation> <translation id="7658239707568436148">Откажи</translation> <translation id="7781829728241885113">Јуче</translation> <translation id="7814458197256864873">&Копирај</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sv.xtb b/chromium/ui/strings/translations/ui_strings_sv.xtb index 2dcdbb2375c..1a78da15e1f 100644 --- a/chromium/ui/strings/translations/ui_strings_sv.xtb +++ b/chromium/ui/strings/translations/ui_strings_sv.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Bakåt</translation> <translation id="3889424535448813030">Högerpil</translation> <translation id="3892641579809465218">Intern bildskärm</translation> +<translation id="3897092660631435901">Meny</translation> <translation id="3909791450649380159">&Klipp ut</translation> <translation id="3990502903496589789">Högerkant</translation> <translation id="4202807286478387388">fortsätta</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Utökningstriangel</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Ta bort</translation> +<translation id="5463830097259460683">Emoji och symboler</translation> <translation id="5476505524087279545">kryssa av</translation> <translation id="5574202486608032840">Operativsystemet <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backsteg</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 minut kvar}other{# minuter kvar}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Avbryt</translation> <translation id="7781829728241885113">Igår</translation> <translation id="7814458197256864873">&Kopiera</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sw.xtb b/chromium/ui/strings/translations/ui_strings_sw.xtb index 5cfc38c89f7..d1ee5087c70 100644 --- a/chromium/ui/strings/translations/ui_strings_sw.xtb +++ b/chromium/ui/strings/translations/ui_strings_sw.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Nyuma</translation> <translation id="3889424535448813030">Mshale Kulia</translation> <translation id="3892641579809465218">Onyesho la Ndani</translation> +<translation id="3897092660631435901">Menyu</translation> <translation id="3909791450649380159">&Kata</translation> <translation id="3990502903496589789">Ncha ya Kulia</translation> <translation id="4202807286478387388">ruka</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Pembe tatu ya ufumbuzi</translation> <translation id="528468243742722775">Mwisho</translation> <translation id="5329858601952122676">&Futa</translation> +<translation id="5463830097259460683">Emoji na Ishara</translation> <translation id="5476505524087279545">toa tiki</translation> <translation id="5574202486608032840">Mfumo wa <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Imesalia dakika 1}other{Zimesalia dakika #}}</translation> <translation id="7460907917090416791">TB <ph name="QUANTITY" /></translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">Ghairi</translation> <translation id="7781829728241885113">Jana</translation> <translation id="7814458197256864873">&Nakili</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ta.xtb b/chromium/ui/strings/translations/ui_strings_ta.xtb index 66df98a941f..b85acb0aa92 100644 --- a/chromium/ui/strings/translations/ui_strings_ta.xtb +++ b/chromium/ui/strings/translations/ui_strings_ta.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">முந்தைய பக்கம்</translation> <translation id="3889424535448813030">வலது அம்பு</translation> <translation id="3892641579809465218">இணையக் காட்சி</translation> +<translation id="3897092660631435901">மெனு</translation> <translation id="3909791450649380159">வெட்&டு</translation> <translation id="3990502903496589789">வலது விளிம்பு</translation> <translation id="4202807286478387388">தாவு</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">கூடுதல் உள்ளடக்கத்தைக் காட்டும் முக்கோணம்</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&நீக்கு</translation> +<translation id="5463830097259460683">ஈமோஜி && குறியீடுகள்</translation> <translation id="5476505524087279545">தேர்வு நீக்கு</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> சாதனம்</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 நிமிடம் மீதமுள்ளது}other{# நிமிடங்கள் மீதமுள்ளன}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> டெ.பை</translation> +<translation id="7507604095951736240">ஈமோஜி</translation> <translation id="7658239707568436148">ரத்து செய்</translation> <translation id="7781829728241885113">நேற்று</translation> <translation id="7814458197256864873">&நகலெடு</translation> diff --git a/chromium/ui/strings/translations/ui_strings_te.xtb b/chromium/ui/strings/translations/ui_strings_te.xtb index 4e84a0122b2..948f7a18530 100644 --- a/chromium/ui/strings/translations/ui_strings_te.xtb +++ b/chromium/ui/strings/translations/ui_strings_te.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">వెనుకకు</translation> <translation id="3889424535448813030">కుడి బాణం</translation> <translation id="3892641579809465218">అంతర్గత ప్రదర్శన</translation> +<translation id="3897092660631435901">మెను</translation> <translation id="3909791450649380159">క&త్తిరించు</translation> <translation id="3990502903496589789">కుడి సరిహద్దు</translation> <translation id="4202807286478387388">వెళ్ళు</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">కంటెంట్ను విస్తరింపజేసే లేదా కుదించే త్రిభుజం</translation> <translation id="528468243742722775">ముగింపు</translation> <translation id="5329858601952122676">&తొలగించు</translation> +<translation id="5463830097259460683">ఎమోజి && చిహ్నాలు</translation> <translation id="5476505524087279545">ఎంపిక చెయ్యబడలేదు</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> సిస్టమ్</translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 నిమిషం మిగిలి ఉంది}other{# నిమిషాలు మిగిలి ఉన్నాయి}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">ఎమోజి</translation> <translation id="7658239707568436148">రద్దు చెయ్యి</translation> <translation id="7781829728241885113">నిన్న</translation> <translation id="7814458197256864873">&కాపీ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_th.xtb b/chromium/ui/strings/translations/ui_strings_th.xtb index 7aa7da8ca72..1a7d9b66377 100644 --- a/chromium/ui/strings/translations/ui_strings_th.xtb +++ b/chromium/ui/strings/translations/ui_strings_th.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">กลับ</translation> <translation id="3889424535448813030">ลูกศรขวา</translation> <translation id="3892641579809465218">จอแสดงผลภายใน</translation> +<translation id="3897092660631435901">เมนู</translation> <translation id="3909791450649380159">&ตัด</translation> <translation id="3990502903496589789">ขอบขวา</translation> <translation id="4202807286478387388">ข้าม</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">สามเหลี่ยมซ่อนเนื้อหา</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&ลบ</translation> +<translation id="5463830097259460683">อีโมจิและสัญลักษณ์</translation> <translation id="5476505524087279545">ยกเลิกการทำเครื่องหมาย</translation> <translation id="5574202486608032840">ระบบ <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{เหลือ 1 นาที}other{เหลือ # นาที}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">อีโมจิ</translation> <translation id="7658239707568436148">ยกเลิก</translation> <translation id="7781829728241885113">เมื่อวานนี้</translation> <translation id="7814458197256864873">&คัดลอก</translation> diff --git a/chromium/ui/strings/translations/ui_strings_tr.xtb b/chromium/ui/strings/translations/ui_strings_tr.xtb index ff48f705d6d..7bea7f3ff6a 100644 --- a/chromium/ui/strings/translations/ui_strings_tr.xtb +++ b/chromium/ui/strings/translations/ui_strings_tr.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Geri</translation> <translation id="3889424535448813030">Sağ Ok</translation> <translation id="3892641579809465218">Dahili Ekran</translation> +<translation id="3897092660631435901">Menü</translation> <translation id="3909791450649380159">&Kes</translation> <translation id="3990502903496589789">Sağ Kenar</translation> <translation id="4202807286478387388">git</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Açıklama üçgeni</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Sil</translation> +<translation id="5463830097259460683">Emoji ve Semboller</translation> <translation id="5476505524087279545">işareti kaldır</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> sistemi</translation> <translation id="5583640892426849032">Geri al tuşu</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{1 dakika kaldı}other{# dakika kaldı}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Emoji</translation> <translation id="7658239707568436148">İptal</translation> <translation id="7781829728241885113">Dün</translation> <translation id="7814458197256864873">K&opyala</translation> diff --git a/chromium/ui/strings/translations/ui_strings_uk.xtb b/chromium/ui/strings/translations/ui_strings_uk.xtb index e470d44a019..a20aa1e013f 100644 --- a/chromium/ui/strings/translations/ui_strings_uk.xtb +++ b/chromium/ui/strings/translations/ui_strings_uk.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Назад</translation> <translation id="3889424535448813030">Курсор праворуч</translation> <translation id="3892641579809465218">Внутрішній екран</translation> +<translation id="3897092660631435901">Меню</translation> <translation id="3909791450649380159">Вирізат&и</translation> <translation id="3990502903496589789">Правий край</translation> <translation id="4202807286478387388">перейти</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Трикутник відкривання</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Видалити</translation> +<translation id="5463830097259460683">Смайли та символи</translation> <translation id="5476505524087279545">зняти прапорець</translation> <translation id="5574202486608032840">Система <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">– <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Залишилась 1 хвилина}one{Залишилася # хвилина}few{Залишилося # хвилини}many{Залишилося # хвилин}other{Залишилося # хвилини}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> ТБ</translation> +<translation id="7507604095951736240">Смайли</translation> <translation id="7658239707568436148">Скасувати</translation> <translation id="7781829728241885113">Учора</translation> <translation id="7814458197256864873">&Копіювати</translation> diff --git a/chromium/ui/strings/translations/ui_strings_vi.xtb b/chromium/ui/strings/translations/ui_strings_vi.xtb index 0278c309ae6..706148b670e 100644 --- a/chromium/ui/strings/translations/ui_strings_vi.xtb +++ b/chromium/ui/strings/translations/ui_strings_vi.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">Quay lại</translation> <translation id="3889424535448813030">Mũi tên phải</translation> <translation id="3892641579809465218">Màn hình nội bộ</translation> +<translation id="3897092660631435901">Menu</translation> <translation id="3909791450649380159">Cắ&t</translation> <translation id="3990502903496589789">Cạnh bên Phải</translation> <translation id="4202807286478387388">chuyển</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">Tam giác hiển thị</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">&Xoá</translation> +<translation id="5463830097259460683">Các ký hiệu && biểu tượng cảm xúc</translation> <translation id="5476505524087279545">bỏ chọn</translation> <translation id="5574202486608032840">Hệ thống <ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{Còn 1 phút}other{Còn # phút}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">Biểu tượng cảm xúc</translation> <translation id="7658239707568436148">Hủy</translation> <translation id="7781829728241885113">Hôm qua</translation> <translation id="7814458197256864873">Sao &chép</translation> diff --git a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb index 1dc0dbb27d5..29b5610e58c 100644 --- a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb +++ b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">后退</translation> <translation id="3889424535448813030">向右箭头</translation> <translation id="3892641579809465218">内部显示屏</translation> +<translation id="3897092660631435901">菜单</translation> <translation id="3909791450649380159">剪切(&T)</translation> <translation id="3990502903496589789">右边缘</translation> <translation id="4202807286478387388">略过</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">开合三角标记</translation> <translation id="528468243742722775">End</translation> <translation id="5329858601952122676">删除(&D)</translation> +<translation id="5463830097259460683">表情符号与符号</translation> <translation id="5476505524087279545">取消选中</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> 系统</translation> <translation id="5583640892426849032">退格</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{还剩 1 分钟}other{还剩 # 分钟}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">表情符号</translation> <translation id="7658239707568436148">取消</translation> <translation id="7781829728241885113">昨天</translation> <translation id="7814458197256864873">复制(&C)</translation> diff --git a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb index ad478c47f7b..a6b1941a96c 100644 --- a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb +++ b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb @@ -63,6 +63,7 @@ <translation id="385051799172605136">返回</translation> <translation id="3889424535448813030">向右鍵</translation> <translation id="3892641579809465218">內建顯示器</translation> +<translation id="3897092660631435901">選單</translation> <translation id="3909791450649380159">剪下(&T)</translation> <translation id="3990502903496589789">右邊緣</translation> <translation id="4202807286478387388">跳至另一頁</translation> @@ -85,6 +86,7 @@ <translation id="5266161281976477809">顯示/隱藏三角標記</translation> <translation id="528468243742722775">結束</translation> <translation id="5329858601952122676">刪除(&D)</translation> +<translation id="5463830097259460683">表情符號和符號</translation> <translation id="5476505524087279545">取消選取</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /></translation> <translation id="5583640892426849032">Backspace 鍵</translation> @@ -136,6 +138,7 @@ <translation id="7389409599945284130">- <ph name="MESSAGE" /></translation> <translation id="7410957453383678442">{MINUTES,plural, =1{還剩 1 分鐘}other{還剩 # 分鐘}}</translation> <translation id="7460907917090416791"><ph name="QUANTITY" /> TB</translation> +<translation id="7507604095951736240">表情符號</translation> <translation id="7658239707568436148">取消</translation> <translation id="7781829728241885113">昨天</translation> <translation id="7814458197256864873">複製(&C)</translation> diff --git a/chromium/ui/strings/ui_strings.grd b/chromium/ui/strings/ui_strings.grd index 3e0b41a4055..29ebbdcf7ce 100644 --- a/chromium/ui/strings/ui_strings.grd +++ b/chromium/ui/strings/ui_strings.grd @@ -435,6 +435,9 @@ need to be translated for each locale.--> <message name="IDS_APP_ACCNAME_RESTORE" desc="The accessible name for the Restore button."> Restore </message> + <message name="IDS_APP_ACCNAME_MENU" desc="The accessible name for the Menu button."> + Menu + </message> <!-- Scroll Bar Context Menu Labels --> <message name="IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE" desc="The label for the 'Scroll Here' item"> @@ -497,6 +500,16 @@ need to be translated for each locale.--> Select &All </message> </if> + <if expr="not is_macosx"> + <message name="IDS_CONTENT_CONTEXT_EMOJI" desc="The context menu item to display the OS-provided emoji picker."> + Emoji + </message> + </if> + <if expr="is_macosx"> + <message name="IDS_CONTENT_CONTEXT_EMOJI" desc="The context menu item to display the OS-provided emoji picker."> + Emoji && Symbols + </message> + </if> <!-- Generic terms --> <message name="IDS_APP_OK" desc="Used for Ok on buttons"> diff --git a/chromium/ui/touch_selection/touch_handle.cc b/chromium/ui/touch_selection/touch_handle.cc index 65b1dba8e82..503f9eab771 100644 --- a/chromium/ui/touch_selection/touch_handle.cc +++ b/chromium/ui/touch_selection/touch_handle.cc @@ -330,6 +330,10 @@ void TouchHandle::UpdateHandleLayout() { drawable_->SetOrigin(ComputeHandleOrigin()); } +void TouchHandle::SetTransparent() { + SetAlpha(0.f); +} + gfx::PointF TouchHandle::ComputeHandleOrigin() const { gfx::PointF focus = mirror_vertical_ ? focus_top_ : focus_bottom_; gfx::RectF drawable_bounds = drawable_->GetVisibleBounds(); diff --git a/chromium/ui/touch_selection/touch_handle.h b/chromium/ui/touch_selection/touch_handle.h index 3815fea5237..3952593001c 100644 --- a/chromium/ui/touch_selection/touch_handle.h +++ b/chromium/ui/touch_selection/touch_handle.h @@ -120,6 +120,10 @@ class UI_TOUCH_SELECTION_EXPORT TouchHandle : public TouchSelectionDraggable { // for the same frame update due to more than one parameter updates. void UpdateHandleLayout(); + // Set the handle to transparent. Handle will be set to opaque again in + // EndDrag() call. + void SetTransparent(); + const gfx::PointF& focus_bottom() const { return focus_bottom_; } TouchHandleOrientation orientation() const { return orientation_; } float alpha() const { return alpha_; } diff --git a/chromium/ui/touch_selection/touch_handle_unittest.cc b/chromium/ui/touch_selection/touch_handle_unittest.cc index c723009d0f3..991f44b975d 100644 --- a/chromium/ui/touch_selection/touch_handle_unittest.cc +++ b/chromium/ui/touch_selection/touch_handle_unittest.cc @@ -4,7 +4,6 @@ #include "ui/touch_selection/touch_handle.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/test/motion_event_test_utils.h" #include "ui/gfx/geometry/rect_f.h" diff --git a/chromium/ui/touch_selection/touch_selection_controller.cc b/chromium/ui/touch_selection/touch_selection_controller.cc index 8ea5e0b5cac..49b991ec968 100644 --- a/chromium/ui/touch_selection/touch_selection_controller.cc +++ b/chromium/ui/touch_selection/touch_selection_controller.cc @@ -46,7 +46,8 @@ TouchSelectionController::Config::Config() : max_tap_duration(base::TimeDelta::FromMilliseconds(300)), tap_slop(8), enable_adaptive_handle_orientation(false), - enable_longpress_drag_selection(false) {} + enable_longpress_drag_selection(false), + hide_active_handle(false) {} TouchSelectionController::Config::~Config() { } @@ -341,6 +342,8 @@ void TouchSelectionController::OnDragBegin( const gfx::PointF& drag_position) { if (&draggable == insertion_handle_.get()) { DCHECK_EQ(active_status_, INSERTION_ACTIVE); + if (config_.hide_active_handle) + insertion_handle_->SetTransparent(); client_->OnSelectionEvent(INSERTION_HANDLE_DRAG_STARTED); anchor_drag_to_selection_start_ = true; return; @@ -359,6 +362,14 @@ void TouchSelectionController::OnDragBegin( (drag_position - GetEndPosition()).LengthSquared(); } + if (config_.hide_active_handle) { + if (&draggable == start_selection_handle_.get()) { + start_selection_handle_->SetTransparent(); + } else if (&draggable == end_selection_handle_.get()) { + end_selection_handle_->SetTransparent(); + } + } + gfx::PointF base = GetStartPosition() + GetStartLineOffset(); gfx::PointF extent = GetEndPosition() + GetEndLineOffset(); if (anchor_drag_to_selection_start_) diff --git a/chromium/ui/touch_selection/touch_selection_controller.h b/chromium/ui/touch_selection/touch_selection_controller.h index 2214024f7ef..24c1a8e5250 100644 --- a/chromium/ui/touch_selection/touch_selection_controller.h +++ b/chromium/ui/touch_selection/touch_selection_controller.h @@ -66,6 +66,9 @@ class UI_TOUCH_SELECTION_EXPORT TouchSelectionController // Controls whether drag selection after a longpress is enabled. // Defaults to false. bool enable_longpress_drag_selection; + + // Should we hide the active handle. + bool hide_active_handle; }; TouchSelectionController(TouchSelectionControllerClient* client, diff --git a/chromium/ui/touch_selection/touch_selection_controller_test_api.cc b/chromium/ui/touch_selection/touch_selection_controller_test_api.cc index fe5a14306f1..5132672b2a0 100644 --- a/chromium/ui/touch_selection/touch_selection_controller_test_api.cc +++ b/chromium/ui/touch_selection/touch_selection_controller_test_api.cc @@ -34,6 +34,13 @@ float TouchSelectionControllerTestApi::GetEndAlpha() const { return 0.f; } +float TouchSelectionControllerTestApi::GetInsertionHandleAlpha() const { + if (controller_->active_status_ == TouchSelectionController::INSERTION_ACTIVE) + return controller_->insertion_handle_->alpha(); + + return 0.f; +} + TouchHandleOrientation TouchSelectionControllerTestApi::GetStartHandleOrientation() const { if (controller_->active_status_ != TouchSelectionController::SELECTION_ACTIVE) diff --git a/chromium/ui/touch_selection/touch_selection_controller_test_api.h b/chromium/ui/touch_selection/touch_selection_controller_test_api.h index 6454b9e8999..c9cb0c7551a 100644 --- a/chromium/ui/touch_selection/touch_selection_controller_test_api.h +++ b/chromium/ui/touch_selection/touch_selection_controller_test_api.h @@ -22,6 +22,7 @@ class TouchSelectionControllerTestApi { bool GetEndVisible() const; float GetStartAlpha() const; float GetEndAlpha() const; + float GetInsertionHandleAlpha() const; TouchHandleOrientation GetStartHandleOrientation() const; TouchHandleOrientation GetEndHandleOrientation() const; diff --git a/chromium/ui/touch_selection/touch_selection_controller_unittest.cc b/chromium/ui/touch_selection/touch_selection_controller_unittest.cc index d0bf65b32bc..84331436c7e 100644 --- a/chromium/ui/touch_selection/touch_selection_controller_unittest.cc +++ b/chromium/ui/touch_selection/touch_selection_controller_unittest.cc @@ -7,7 +7,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/test/motion_event_test_utils.h" @@ -118,6 +117,12 @@ class TouchSelectionControllerTest : public testing::Test, controller_.reset(new TouchSelectionController(this, config)); } + void SetHideActiveHandle(bool hide) { + TouchSelectionController::Config config = DefaultConfig(); + config.hide_active_handle = hide; + controller_.reset(new TouchSelectionController(this, config)); + } + void SetAnimationEnabled(bool enabled) { animation_enabled_ = enabled; } void SetDraggingEnabled(bool enabled) { dragging_enabled_ = enabled; } @@ -1457,4 +1462,148 @@ TEST_F(TouchSelectionControllerTest, SelectionUpdateDragPosition) { EXPECT_THAT(GetAndResetEvents(), ElementsAre(SELECTION_HANDLE_DRAG_STOPPED)); } +TEST_F(TouchSelectionControllerTest, NoHideActiveInsertionHandle) { + SetHideActiveHandle(false); + TouchSelectionControllerTestApi test_controller(&controller()); + + base::TimeTicks event_time = base::TimeTicks::Now(); + float line_height = 10.f; + gfx::RectF insertion_rect(10, 0, 0, line_height); + bool visible = true; + OnTapEvent(); + + ChangeInsertion(insertion_rect, visible); + EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_SHOWN)); + + SetDraggingEnabled(true); + EXPECT_EQ(1.f, test_controller.GetInsertionHandleAlpha()); + MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetInsertionHandleAlpha()); +} + +TEST_F(TouchSelectionControllerTest, HideActiveInsertionHandle) { + SetHideActiveHandle(true); + TouchSelectionControllerTestApi test_controller(&controller()); + + base::TimeTicks event_time = base::TimeTicks::Now(); + float line_height = 10.f; + gfx::RectF insertion_rect(10, 0, 0, line_height); + bool visible = true; + OnTapEvent(); + + ChangeInsertion(insertion_rect, visible); + EXPECT_THAT(GetAndResetEvents(), ElementsAre(INSERTION_HANDLE_SHOWN)); + + SetDraggingEnabled(true); + EXPECT_EQ(1.f, test_controller.GetInsertionHandleAlpha()); + MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(0.f, test_controller.GetInsertionHandleAlpha()); + + event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(0.f, test_controller.GetInsertionHandleAlpha()); + + event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs); + // UP will reset the alpha to visible. + event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 0, 0); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetInsertionHandleAlpha()); +} + +TEST_F(TouchSelectionControllerTest, NoHideActiveSelectionHandle) { + SetHideActiveHandle(false); + TouchSelectionControllerTestApi test_controller(&controller()); + + base::TimeTicks event_time = base::TimeTicks::Now(); + float line_height = 10.f; + gfx::RectF start_rect(10, 0, 0, line_height); + gfx::RectF end_rect(50, 0, 0, line_height); + bool visible = true; + OnLongPressEvent(); + + ChangeSelection(start_rect, visible, end_rect, visible); + + // Start handle. + SetDraggingEnabled(true); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + + event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + + // End handle. + event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 50, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + + event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs); + event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 50, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); +} + +TEST_F(TouchSelectionControllerTest, HideActiveSelectionHandle) { + SetHideActiveHandle(true); + TouchSelectionControllerTestApi test_controller(&controller()); + + base::TimeTicks event_time = base::TimeTicks::Now(); + float line_height = 10.f; + gfx::RectF start_rect(10, 0, 0, line_height); + gfx::RectF end_rect(50, 0, 0, line_height); + bool visible = true; + OnLongPressEvent(); + + ChangeSelection(start_rect, visible, end_rect, visible); + + // Start handle. + SetDraggingEnabled(true); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + MockMotionEvent event(MockMotionEvent::Action::DOWN, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + + event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + + event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs); + // UP will reset alpha to be visible. + event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 10, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + + // End handle. + event = MockMotionEvent(MockMotionEvent::Action::DOWN, event_time, 50, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(0.f, test_controller.GetEndAlpha()); + + event = MockMotionEvent(MockMotionEvent::Action::MOVE, event_time, 50, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(0.f, test_controller.GetEndAlpha()); + + event_time += base::TimeDelta::FromMilliseconds(2 * kDefaultTapTimeoutMs); + // UP will reset alpha to be visible. + event = MockMotionEvent(MockMotionEvent::Action::UP, event_time, 50, 5); + EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); +} + } // namespace ui diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn index 8d72c2a2b31..32f534f12e3 100644 --- a/chromium/ui/views/BUILD.gn +++ b/chromium/ui/views/BUILD.gn @@ -22,14 +22,24 @@ aggregate_vector_icons("views_vector_icons") { icons = [ "checkbox_active.icon", "checkbox_normal.icon", - "menu_check.1x.icon", + "close.icon", + "ic_close.icon", + "info.icon", + "launch.icon", "menu_check.icon", "menu_radio_empty.icon", "menu_radio_selected.icon", + "new_incognito_window.icon", + "new_tab.icon", + "new_window.icon", + "open.icon", + "options.icon", + "pin.icon", "radio_button_active.icon", "radio_button_normal.icon", - "submenu_arrow.1x.icon", "submenu_arrow.icon", + "uninstall.icon", + "unpin.icon", ] } @@ -110,6 +120,7 @@ jumbo_component("views") { "controls/label.h", "controls/link.h", "controls/link_listener.h", + "controls/menu/menu_closure_animation_mac.h", "controls/menu/menu_config.h", "controls/menu/menu_controller.h", "controls/menu/menu_controller_delegate.h", @@ -307,6 +318,7 @@ jumbo_component("views") { "controls/label.cc", "controls/link.cc", "controls/menu/display_change_listener_mac.cc", + "controls/menu/menu_closure_animation_mac.mm", "controls/menu/menu_config.cc", "controls/menu/menu_config_chromeos.cc", "controls/menu/menu_config_linux.cc", @@ -626,6 +638,7 @@ jumbo_component("views") { deps += [ "//services/ui/public/interfaces", "//ui/aura", + "//ui/platform_window", "//ui/touch_selection", "//ui/wm", "//ui/wm/public", @@ -685,6 +698,11 @@ jumbo_component("views") { } } + if (is_linux) { + sources += [ "widget/desktop_aura/desktop_window_tree_host_platform.cc" ] + public += [ "widget/desktop_aura/desktop_window_tree_host_platform.h" ] + } + if (is_mac) { sources -= [ "controls/views_text_services_context_menu.cc" ] deps += [ @@ -783,6 +801,8 @@ jumbo_source_set("test_support_internal") { "test/views_test_helper_mac.mm", "test/widget_test.cc", "test/widget_test.h", + "test/widget_test_api.cc", + "test/widget_test_api.h", "test/widget_test_mac.mm", "test/x11_property_change_waiter.cc", "test/x11_property_change_waiter.h", @@ -803,7 +823,7 @@ jumbo_source_set("test_support_internal") { "//base/test:test_support", "//gpu/ipc/service", "//ipc:test_support", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gtest", "//ui/base", @@ -915,6 +935,7 @@ source_set("views_unittests_sources") { "cocoa/drag_drop_client_mac_unittest.mm", "controls/button/blue_button_unittest.cc", "controls/button/button_unittest.cc", + "controls/button/checkbox_unittest.cc", "controls/button/image_button_factory_unittest.cc", "controls/button/image_button_unittest.cc", "controls/button/label_button_label_unittest.cc", @@ -1117,7 +1138,7 @@ test("views_unittests") { deps = [ ":views_unittests_sources", - "//mojo/edk/system", + "//mojo/edk", ] } @@ -1135,7 +1156,7 @@ source_set("views_interactive_ui_tests") { ":views", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gtest", "//ui/base:test_support", @@ -1194,13 +1215,13 @@ test("views_perftests") { ":test_support", "//base/test:test_support", "//cc/base:base", - "//mojo/edk/system", + "//mojo/edk", "//testing/perf", "//ui/resources:ui_test_pak", ] data_deps = [ "//ui/resources:ui_test_pak_data", - "//testing:run_gtest_perf_test", + "//testing:run_perf_test", ] } diff --git a/chromium/ui/views/DEPS b/chromium/ui/views/DEPS index 41eaa521433..e6f8d868ebf 100644 --- a/chromium/ui/views/DEPS +++ b/chromium/ui/views/DEPS @@ -16,6 +16,7 @@ include_rules = [ "+ui/gl/test/gl_surface_test_support.h", # To initialize GL for tests. "+ui/native_theme", "+ui/ozone/public", + "+ui/platform_window", "+ui/resources/grit/ui_resources.h", "+ui/strings/grit/ui_strings.h", "+ui/touch_selection", diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc index 68aaca376d8..5a5ca2dd36c 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc @@ -151,4 +151,11 @@ void AXWindowObjWrapper::OnWindowPropertyChanged(aura::Window* window, } } +void AXWindowObjWrapper::OnWindowVisibilityChanged(aura::Window* window, + bool visible) { + AXAuraObjCache::GetInstance()->FireEvent( + AXAuraObjCache::GetInstance()->GetOrCreate(window_), + ax::mojom::Event::kStateChanged); +} + } // namespace views diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h index a1f5834f5d3..9904dc471d7 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.h +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.h @@ -48,6 +48,7 @@ class AXWindowObjWrapper : public AXAuraObjWrapper, void OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) override; + void OnWindowVisibilityChanged(aura::Window* window, bool visible) override; private: aura::Window* window_; diff --git a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc index 0be9b7cfd75..1d155c6fa74 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc @@ -9,7 +9,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/memory/singleton.h" #include "base/stl_util.h" #include "ui/accessibility/ax_action_data.h" diff --git a/chromium/ui/views/accessibility/native_view_accessibility_base.cc b/chromium/ui/views/accessibility/native_view_accessibility_base.cc index 9997f88dd95..e895c9d3418 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_base.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_base.cc @@ -7,7 +7,8 @@ #include "ui/views/accessibility/native_view_accessibility_base.h" -#include "base/memory/ptr_util.h" +#include "base/lazy_instance.h" +#include "base/threading/thread_task_runner_handle.h" #include "ui/accessibility/platform/ax_platform_node.h" #include "ui/events/event_utils.h" #include "ui/gfx/native_widget_types.h" @@ -22,6 +23,17 @@ namespace { base::LazyInstance<std::map<int32_t, ui::AXPlatformNode*>>::Leaky g_unique_id_to_ax_platform_node = LAZY_INSTANCE_INITIALIZER; +// Information required to fire a delayed accessibility event. +struct QueuedEvent { + ax::mojom::Event type; + int32_t node_id; +}; + +base::LazyInstance<std::vector<QueuedEvent>>::Leaky g_event_queue = + LAZY_INSTANCE_INITIALIZER; + +bool g_is_queueing_events = false; + bool IsAccessibilityFocusableWhenEnabled(View* view) { return view->focus_behavior() != View::FocusBehavior::NEVER && view->IsDrawn(); @@ -59,8 +71,36 @@ ui::AXPlatformNode* FromNativeWindow(gfx::NativeWindow native_window) { return ui::AXPlatformNode::FromNativeViewAccessible(native_view_accessible); } +ui::AXPlatformNode* PlatformNodeFromNodeID(int32_t id) { + // Note: For Views, node IDs and unique IDs are the same - but that isn't + // necessarily true for all AXPlatformNodes. + auto it = g_unique_id_to_ax_platform_node.Get().find(id); + + if (it == g_unique_id_to_ax_platform_node.Get().end()) + return nullptr; + + return it->second; +} + +void FireEvent(QueuedEvent event) { + ui::AXPlatformNode* node = PlatformNodeFromNodeID(event.node_id); + if (node) + node->NotifyAccessibilityEvent(event.type); +} + +void FlushQueue() { + DCHECK(g_is_queueing_events); + for (QueuedEvent event : g_event_queue.Get()) + FireEvent(event); + g_is_queueing_events = false; + g_event_queue.Get().clear(); +} + } // namespace +// static +int32_t NativeViewAccessibilityBase::fake_focus_view_id_ = 0; + NativeViewAccessibilityBase::NativeViewAccessibilityBase(View* view) : ViewAccessibility(view) { ax_node_ = ui::AXPlatformNode::Create(this); @@ -87,7 +127,22 @@ gfx::NativeViewAccessible NativeViewAccessibilityBase::GetNativeObject() { void NativeViewAccessibilityBase::NotifyAccessibilityEvent( ax::mojom::Event event_type) { + if (g_is_queueing_events) { + g_event_queue.Get().push_back({event_type, GetUniqueId().Get()}); + return; + } + ax_node_->NotifyAccessibilityEvent(event_type); + + // A focus context event is intended to send a focus event and a delay + // before the next focus event. It makes sense to delay the entire next + // synchronous batch of next events so that ordering remains the same. + if (event_type == ax::mojom::Event::kFocusContext) { + // Begin queueing subsequent events and flush queue asynchronously. + g_is_queueing_events = true; + base::OnceCallback<void()> cb = base::BindOnce(&FlushQueue); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(cb)); + } } // ui::AXPlatformNodeDelegate @@ -226,22 +281,36 @@ gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x, return GetNativeObject(); } +void NativeViewAccessibilityBase::OnAutofillShown() { + // When the autofill is shown, treat it and the currently selected item as + // focused, even though the actual focus is in the browser's currently + // focused textfield. + DCHECK(!fake_focus_view_id_) << "Cannot have more that one fake focus."; + fake_focus_view_id_ = GetUniqueId().Get(); + ui::AXPlatformNode::OnAutofillShown(); +} + +void NativeViewAccessibilityBase::OnAutofillHidden() { + DCHECK(fake_focus_view_id_ == GetUniqueId().Get()) + << "Cannot clear fake focus on an object that did not have fake focus."; + fake_focus_view_id_ = 0; + ui::AXPlatformNode::OnAutofillHidden(); +} + gfx::NativeViewAccessible NativeViewAccessibilityBase::GetFocus() { FocusManager* focus_manager = view()->GetFocusManager(); View* focused_view = focus_manager ? focus_manager->GetFocusedView() : nullptr; + if (fake_focus_view_id_) { + ui::AXPlatformNode* ax_node = PlatformNodeFromNodeID(fake_focus_view_id_); + if (ax_node) + return ax_node->GetNativeViewAccessible(); + } return focused_view ? focused_view->GetNativeViewAccessible() : nullptr; } ui::AXPlatformNode* NativeViewAccessibilityBase::GetFromNodeID(int32_t id) { - // Note: For Views, node IDs and unique IDs are the same - but that isn't - // necessarily true for all AXPlatformNodes. - auto it = g_unique_id_to_ax_platform_node.Get().find(id); - - if (it == g_unique_id_to_ax_platform_node.Get().end()) - return nullptr; - - return it->second; + return PlatformNodeFromNodeID(id); } int NativeViewAccessibilityBase::GetIndexInParent() const { diff --git a/chromium/ui/views/accessibility/native_view_accessibility_base.h b/chromium/ui/views/accessibility/native_view_accessibility_base.h index 18fe003a7fc..cb3d15a8eec 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_base.h +++ b/chromium/ui/views/accessibility/native_view_accessibility_base.h @@ -37,6 +37,8 @@ class VIEWS_EXPORT NativeViewAccessibilityBase // ViewAccessibility: gfx::NativeViewAccessible GetNativeObject() override; void NotifyAccessibilityEvent(ax::mojom::Event event_type) override; + void OnAutofillShown() override; + void OnAutofillHidden() override; // ui::AXPlatformNodeDelegate const ui::AXNodeData& GetData() const override; @@ -74,6 +76,11 @@ class VIEWS_EXPORT NativeViewAccessibilityBase mutable ui::AXNodeData data_; + // This allows UI popups like autofill to act as if they are focused in the + // exposed platform accessibility API, even though true focus remains in + // underlying content. + static int32_t fake_focus_view_id_; + DISALLOW_COPY_AND_ASSIGN(NativeViewAccessibilityBase); }; diff --git a/chromium/ui/views/accessibility/native_view_accessibility_mac.mm b/chromium/ui/views/accessibility/native_view_accessibility_mac.mm index c6374ceaaa7..be5b1978fde 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_mac.mm +++ b/chromium/ui/views/accessibility/native_view_accessibility_mac.mm @@ -6,7 +6,6 @@ #include <memory> -#include "base/memory/ptr_util.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" diff --git a/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc b/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc index d146b33705f..c4904034e0c 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_unittest.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_node_data.h" #include "ui/gfx/geometry/rect_conversions.h" diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win.cc b/chromium/ui/views/accessibility/native_view_accessibility_win.cc index bc1ef854510..7491a60f6f0 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_win.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_win.cc @@ -10,7 +10,6 @@ #include <set> #include <vector> -#include "base/memory/ptr_util.h" #include "base/memory/singleton.h" #include "base/strings/utf_string_conversions.h" #include "base/win/windows_version.h" @@ -25,7 +24,6 @@ #include "ui/base/win/atl_module.h" #include "ui/display/win/screen_win.h" #include "ui/views/controls/button/button.h" -#include "ui/views/focus/focus_manager.h" #include "ui/views/widget/widget.h" #include "ui/views/win/hwnd_util.h" #include "ui/wm/core/window_util.h" diff --git a/chromium/ui/views/accessibility/view_accessibility.cc b/chromium/ui/views/accessibility/view_accessibility.cc index 133b4d4368d..91a0db6df8a 100644 --- a/chromium/ui/views/accessibility/view_accessibility.cc +++ b/chromium/ui/views/accessibility/view_accessibility.cc @@ -97,7 +97,7 @@ void ViewAccessibility::GetAccessibleNodeData(ui::AXNodeData* data) const { if (!owner_view_->enabled()) data->SetRestriction(ax::mojom::Restriction::kDisabled); - if (!owner_view_->visible()) + if (!owner_view_->visible() && data->role != ax::mojom::Role::kAlert) data->AddState(ax::mojom::State::kInvisible); if (owner_view_->context_menu_controller()) diff --git a/chromium/ui/views/accessibility/view_accessibility.h b/chromium/ui/views/accessibility/view_accessibility.h index 8dc947bd63a..6a366b4cb6f 100644 --- a/chromium/ui/views/accessibility/view_accessibility.h +++ b/chromium/ui/views/accessibility/view_accessibility.h @@ -51,6 +51,9 @@ class VIEWS_EXPORT ViewAccessibility { void OverrideDescription(const std::string& description); void OverrideIsLeaf(); // Force this node to be treated as a leaf node. + virtual void OnAutofillShown(){}; + virtual void OnAutofillHidden(){}; + virtual gfx::NativeViewAccessible GetNativeObject(); virtual void NotifyAccessibilityEvent(ax::mojom::Event event_type) {} diff --git a/chromium/ui/views/accessible_pane_view.cc b/chromium/ui/views/accessible_pane_view.cc index a22b6967a34..87cb386a30c 100644 --- a/chromium/ui/views/accessible_pane_view.cc +++ b/chromium/ui/views/accessible_pane_view.cc @@ -4,7 +4,6 @@ #include "ui/views/accessible_pane_view.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "ui/accessibility/ax_node_data.h" #include "ui/views/focus/focus_search.h" diff --git a/chromium/ui/views/animation/ink_drop.h b/chromium/ui/views/animation/ink_drop.h index 3569e01f261..00de74cc3e3 100644 --- a/chromium/ui/views/animation/ink_drop.h +++ b/chromium/ui/views/animation/ink_drop.h @@ -36,6 +36,14 @@ class VIEWS_EXPORT InkDrop { // Animates from the current InkDropState to |ink_drop_state|. virtual void AnimateToState(InkDropState ink_drop_state) = 0; + // Sets hover highlight fade animations to last for |duration_ms| + // milliseconds. + virtual void SetHoverHighlightFadeDurationMs(int duration_ms) = 0; + + // Clears any set hover highlight fade durations and uses the default + // durations instead. + virtual void UseDefaultHoverHighlightFadeDuration() = 0; + // Immediately snaps the InkDropState to ACTIVATED and HIDDEN specifically. // These are more specific implementations of the non-existent // SnapToState(InkDropState) function are the only ones available because they diff --git a/chromium/ui/views/animation/ink_drop_highlight_unittest.cc b/chromium/ui/views/animation/ink_drop_highlight_unittest.cc index f804b934b5a..22f43856bcc 100644 --- a/chromium/ui/views/animation/ink_drop_highlight_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_highlight_unittest.cc @@ -7,7 +7,6 @@ #include <memory> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/layer.h" diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc index 5a769f05902..8a60f37c9fb 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.cc +++ b/chromium/ui/views/animation/ink_drop_host_view.cc @@ -4,7 +4,6 @@ #include "ui/views/animation/ink_drop_host_view.h" -#include "base/memory/ptr_util.h" #include "ui/events/event.h" #include "ui/events/scoped_target_handler.h" #include "ui/gfx/color_palette.h" @@ -168,6 +167,10 @@ std::unique_ptr<InkDropHighlight> InkDropHostView::CreateInkDropHighlight() gfx::RectF(GetMirroredRect(GetContentsBounds())).CenterPoint()); } +std::unique_ptr<views::InkDropMask> InkDropHostView::CreateInkDropMask() const { + return nullptr; +} + std::unique_ptr<InkDropRipple> InkDropHostView::CreateDefaultInkDropRipple( const gfx::Point& center_point, const gfx::Size& size) const { @@ -278,10 +281,6 @@ SkColor InkDropHostView::GetInkDropBaseColor() const { return gfx::kPlaceholderColor; } -std::unique_ptr<views::InkDropMask> InkDropHostView::CreateInkDropMask() const { - return nullptr; -} - bool InkDropHostView::HasInkDrop() const { return !!ink_drop_; } @@ -298,12 +297,9 @@ InkDrop* InkDropHostView::GetInkDrop() { } void InkDropHostView::InstallInkDropMask(ui::Layer* ink_drop_layer) { -// Layer masks don't work on Windows. See crbug.com/713359 -#if !defined(OS_WIN) ink_drop_mask_ = CreateInkDropMask(); if (ink_drop_mask_) ink_drop_layer->SetMaskLayer(ink_drop_mask_->layer()); -#endif } void InkDropHostView::ResetInkDropMask() { @@ -316,13 +312,9 @@ void InkDropHostView::UpdateInkDropMaskLayerSize(const gfx::Size& new_size) { } std::unique_ptr<InkDropImpl> InkDropHostView::CreateDefaultInkDropImpl() { - std::unique_ptr<InkDropImpl> ink_drop = - std::make_unique<InkDropImpl>(this, size()); - views::InkDropImpl::AutoHighlightMode mode = - PlatformStyle::kUseRipples - ? views::InkDropImpl::AutoHighlightMode::HIDE_ON_RIPPLE - : views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE; - ink_drop->SetAutoHighlightMode(mode); + auto ink_drop = std::make_unique<InkDropImpl>(this, size()); + ink_drop->SetAutoHighlightMode( + InkDropImpl::AutoHighlightMode::HIDE_ON_RIPPLE); return ink_drop; } diff --git a/chromium/ui/views/animation/ink_drop_host_view.h b/chromium/ui/views/animation/ink_drop_host_view.h index 34ea050d06a..81d018254a5 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.h +++ b/chromium/ui/views/animation/ink_drop_host_view.h @@ -45,6 +45,12 @@ class VIEWS_EXPORT InkDropHostView : public View, public InkDropHost { std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override; std::unique_ptr<InkDropHighlight> CreateInkDropHighlight() const override; + // Subclasses can override to return a mask for the ink drop. By default, + // returns nullptr (i.e no mask). + // TODO(bruthig): InkDropMasks do not currently work on Windows. See + // https://crbug.com/713359. + virtual std::unique_ptr<views::InkDropMask> CreateInkDropMask() const; + // Toggle to enable/disable an InkDrop on this View. Descendants can override // CreateInkDropHighlight() and CreateInkDropRipple() to change the look/feel // of the InkDrop. @@ -107,12 +113,6 @@ class VIEWS_EXPORT InkDropHostView : public View, public InkDropHost { // ink drop. virtual SkColor GetInkDropBaseColor() const; - // Subclasses can override to return a mask for the ink drop. By default, - // returns nullptr (i.e no mask). - // TODO(bruthig): InkDropMasks do not currently work on Windows. See - // crbug.com/713359. - virtual std::unique_ptr<views::InkDropMask> CreateInkDropMask() const; - // Called after a new InkDrop instance is created. virtual void OnInkDropCreated() {} diff --git a/chromium/ui/views/animation/ink_drop_impl.cc b/chromium/ui/views/animation/ink_drop_impl.cc index f052e387114..96328205c27 100644 --- a/chromium/ui/views/animation/ink_drop_impl.cc +++ b/chromium/ui/views/animation/ink_drop_impl.cc @@ -5,12 +5,12 @@ #include "ui/views/animation/ink_drop_impl.h" #include "base/auto_reset.h" -#include "base/memory/ptr_util.h" #include "base/timer/timer.h" #include "ui/compositor/layer.h" #include "ui/views/animation/ink_drop_highlight.h" #include "ui/views/animation/ink_drop_host.h" #include "ui/views/animation/square_ink_drop_ripple.h" +#include "ui/views/style/platform_style.h" namespace views { @@ -170,11 +170,15 @@ void InkDropImpl::NoAutoHighlightHiddenState::Enter() { } void InkDropImpl::NoAutoHighlightHiddenState::ShowOnHoverChanged() { - HandleHoverAndFocusChangeChanges(kHighlightFadeInOnHoverChangeDurationMs); + HandleHoverAndFocusChangeChanges( + GetInkDrop()->hover_highlight_fade_duration_ms().value_or( + kHighlightFadeInOnHoverChangeDurationMs)); } void InkDropImpl::NoAutoHighlightHiddenState::OnHoverChanged() { - HandleHoverAndFocusChangeChanges(kHighlightFadeInOnHoverChangeDurationMs); + HandleHoverAndFocusChangeChanges( + GetInkDrop()->hover_highlight_fade_duration_ms().value_or( + kHighlightFadeInOnHoverChangeDurationMs)); } void InkDropImpl::NoAutoHighlightHiddenState::ShowOnFocusChanged() { @@ -215,11 +219,15 @@ void InkDropImpl::NoAutoHighlightVisibleState::Enter() { } void InkDropImpl::NoAutoHighlightVisibleState::ShowOnHoverChanged() { - HandleHoverAndFocusChangeChanges(kHighlightFadeOutOnHoverChangeDurationMs); + HandleHoverAndFocusChangeChanges( + GetInkDrop()->hover_highlight_fade_duration_ms().value_or( + kHighlightFadeOutOnHoverChangeDurationMs)); } void InkDropImpl::NoAutoHighlightVisibleState::OnHoverChanged() { - HandleHoverAndFocusChangeChanges(kHighlightFadeOutOnHoverChangeDurationMs); + HandleHoverAndFocusChangeChanges( + GetInkDrop()->hover_highlight_fade_duration_ms().value_or( + kHighlightFadeOutOnHoverChangeDurationMs)); } void InkDropImpl::NoAutoHighlightVisibleState::ShowOnFocusChanged() { @@ -612,6 +620,12 @@ void InkDropImpl::SetAutoHighlightMode(AutoHighlightMode auto_highlight_mode) { SetHighlightState(highlight_state_factory_->CreateStartState()); } +void InkDropImpl::SetAutoHighlightModeForPlatform() { + SetAutoHighlightMode(PlatformStyle::kUseRipples + ? AutoHighlightMode::HIDE_ON_RIPPLE + : AutoHighlightMode::SHOW_ON_RIPPLE); +} + void InkDropImpl::HostSizeChanged(const gfx::Size& new_size) { // |root_layer_| should fill the entire host because it affects the clipping // when a mask layer is applied to it. This will not affect clipping if no @@ -640,6 +654,14 @@ void InkDropImpl::AnimateToState(InkDropState ink_drop_state) { ink_drop_ripple_->AnimateToState(ink_drop_state); } +void InkDropImpl::SetHoverHighlightFadeDurationMs(int duration_ms) { + hover_highlight_fade_duration_ms_ = duration_ms; +} + +void InkDropImpl::UseDefaultHoverHighlightFadeDuration() { + hover_highlight_fade_duration_ms_.reset(); +} + void InkDropImpl::SnapToActivated() { DestroyHiddenTargetedAnimations(); if (!ink_drop_ripple_) diff --git a/chromium/ui/views/animation/ink_drop_impl.h b/chromium/ui/views/animation/ink_drop_impl.h index 98ec237e3f7..05862e299d8 100644 --- a/chromium/ui/views/animation/ink_drop_impl.h +++ b/chromium/ui/views/animation/ink_drop_impl.h @@ -8,6 +8,7 @@ #include <memory> #include "base/macros.h" +#include "base/optional.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/views/animation/ink_drop.h" @@ -61,10 +62,21 @@ class VIEWS_EXPORT InkDropImpl : public InkDrop, // InkDrop inherited functions. void SetAutoHighlightMode(AutoHighlightMode auto_highlight_mode); + // Sets the AutoHighlightMode as per the platform. Platforms that show ripples + // will be set to HIDE_ON_RIPPLE, and platforms that don't show ripples are + // set to SHOW_ON_RIPPLE highlight behavior. + void SetAutoHighlightModeForPlatform(); + + const base::Optional<int>& hover_highlight_fade_duration_ms() const { + return hover_highlight_fade_duration_ms_; + } + // InkDrop: void HostSizeChanged(const gfx::Size& new_size) override; InkDropState GetTargetInkDropState() const override; void AnimateToState(InkDropState ink_drop_state) override; + void SetHoverHighlightFadeDurationMs(int duration_ms) override; + void UseDefaultHoverHighlightFadeDuration() override; void SnapToActivated() override; void SnapToHidden() override; void SetHovered(bool is_hovered) override; @@ -301,6 +313,9 @@ class VIEWS_EXPORT InkDropImpl : public InkDrop, // of the |highlight_|. std::unique_ptr<HighlightState> highlight_state_; + // Overrides the default hover highlight fade durations when set. + base::Optional<int> hover_highlight_fade_duration_ms_; + // Used to ensure highlight state transitions are not triggered when exiting // the current state. bool exiting_highlight_state_; diff --git a/chromium/ui/views/animation/ink_drop_impl_unittest.cc b/chromium/ui/views/animation/ink_drop_impl_unittest.cc index 02ad6f75265..e485be3aba9 100644 --- a/chromium/ui/views/animation/ink_drop_impl_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_impl_unittest.cc @@ -7,7 +7,6 @@ #include "ui/views/animation/ink_drop_impl.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/test/gtest_util.h" #include "base/test/test_simple_task_runner.h" #include "base/threading/thread_task_runner_handle.h" diff --git a/chromium/ui/views/animation/ink_drop_stub.cc b/chromium/ui/views/animation/ink_drop_stub.cc index 9a8237b3c11..5cc5d006971 100644 --- a/chromium/ui/views/animation/ink_drop_stub.cc +++ b/chromium/ui/views/animation/ink_drop_stub.cc @@ -18,6 +18,10 @@ InkDropState InkDropStub::GetTargetInkDropState() const { void InkDropStub::AnimateToState(InkDropState state) {} +void InkDropStub::SetHoverHighlightFadeDurationMs(int duration_ms) {} + +void InkDropStub::UseDefaultHoverHighlightFadeDuration() {} + void InkDropStub::SnapToActivated() {} void InkDropStub::SnapToHidden() {} diff --git a/chromium/ui/views/animation/ink_drop_stub.h b/chromium/ui/views/animation/ink_drop_stub.h index 86ed8829824..a1b441a1bc8 100644 --- a/chromium/ui/views/animation/ink_drop_stub.h +++ b/chromium/ui/views/animation/ink_drop_stub.h @@ -22,6 +22,8 @@ class VIEWS_EXPORT InkDropStub : public InkDrop { void HostSizeChanged(const gfx::Size& new_size) override; InkDropState GetTargetInkDropState() const override; void AnimateToState(InkDropState state) override; + void SetHoverHighlightFadeDurationMs(int duration_ms) override; + void UseDefaultHoverHighlightFadeDuration() override; void SnapToActivated() override; void SnapToHidden() override; void SetHovered(bool is_hovered) override; diff --git a/chromium/ui/views/background.cc b/chromium/ui/views/background.cc index 93bd98fcf87..3b728d72dc7 100644 --- a/chromium/ui/views/background.cc +++ b/chromium/ui/views/background.cc @@ -6,7 +6,6 @@ #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/scoped_observer.h" #include "build/build_config.h" #include "ui/gfx/canvas.h" diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc index e2292a8f620..44e7b93fddc 100644 --- a/chromium/ui/views/bubble/bubble_border.cc +++ b/chromium/ui/views/bubble/bubble_border.cc @@ -62,13 +62,12 @@ BorderImages::~BorderImages() {} namespace { +// The border corner radius for material design bubble borders. +constexpr int kMaterialDesignCornerRadius = 2; + // The border is stroked at 1px, but for the purposes of reserving space we have // to deal in dip coordinates, so round up to 1dip. -const int kBorderThicknessDip = 1; - -bool UseMaterialDesign() { - return ui::MaterialDesignController::IsSecondaryUiMaterial(); -} +constexpr int kBorderThicknessDip = 1; // Utility functions for getting alignment points on the edge of a rectangle. gfx::Point CenterTop(const gfx::Rect& rect) { @@ -177,25 +176,30 @@ BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color) background_color_(color), use_theme_background_color_(false) { DCHECK(shadow_ < SHADOW_COUNT); - if (UseMaterialDesign()) { - // Harmony bubbles don't use arrows. - alignment_ = ALIGN_EDGE_TO_ANCHOR_EDGE; - arrow_paint_type_ = PAINT_NONE; - } else { - images_ = GetBorderImages(shadow_); - } + Init(); } BubbleBorder::~BubbleBorder() {} // static -gfx::Insets BubbleBorder::GetBorderAndShadowInsets() { +gfx::Insets BubbleBorder::GetBorderAndShadowInsets( + base::Optional<int> elevation) { + if (elevation.has_value()) { + return -gfx::ShadowValue::GetMargin(GetShadowValues(elevation)) + + gfx::Insets(kBorderThicknessDip); + } + constexpr gfx::Insets blur(kShadowBlur + kBorderThicknessDip); constexpr gfx::Insets offset(-kShadowVerticalOffset, 0, kShadowVerticalOffset, 0); return blur + offset; } +void BubbleBorder::SetCornerRadius(int corner_radius) { + corner_radius_ = corner_radius; + Init(); +} + void BubbleBorder::set_paint_arrow(ArrowPaintType value) { if (UseMaterialDesign()) return; @@ -208,7 +212,8 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, // TODO(estade): handle more anchor positions. if (UseMaterialDesign() && (arrow_ == TOP_RIGHT || arrow_ == TOP_LEFT || arrow_ == BOTTOM_CENTER || - arrow_ == LEFT_CENTER || arrow_ == RIGHT_CENTER)) { + arrow_ == TOP_CENTER || arrow_ == LEFT_CENTER || + arrow_ == RIGHT_CENTER)) { gfx::Rect contents_bounds(contents_size); // Apply the border part of the inset before calculating coordinates because // the border should align with the anchor's border. For the purposes of @@ -228,6 +233,8 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, anchor_rect.bottom_left() - contents_bounds.origin(); } else if (arrow_ == BOTTOM_CENTER) { contents_bounds += CenterTop(anchor_rect) - CenterBottom(contents_bounds); + } else if (arrow_ == TOP_CENTER) { + contents_bounds += CenterBottom(anchor_rect) - CenterTop(contents_bounds); } else if (arrow_ == LEFT_CENTER) { contents_bounds += RightCenter(anchor_rect) - LeftCenter(contents_bounds); } else if (arrow_ == RIGHT_CENTER) { @@ -303,7 +310,9 @@ int BubbleBorder::GetBorderThickness() const { } int BubbleBorder::GetBorderCornerRadius() const { - return UseMaterialDesign() ? 2 : images_->corner_radius; + if (UseMaterialDesign()) + return corner_radius_.value_or(kMaterialDesignCornerRadius); + return images_->corner_radius; } int BubbleBorder::GetArrowOffset(const gfx::Size& border_size) const { @@ -338,6 +347,16 @@ void BubbleBorder::SetBorderInteriorThickness(int border_interior_thickness) { images_->border_thickness = border_interior_thickness; } +void BubbleBorder::Init() { + if (UseMaterialDesign()) { + // Harmony bubbles don't use arrows. + alignment_ = ALIGN_EDGE_TO_ANCHOR_EDGE; + arrow_paint_type_ = PAINT_NONE; + } else { + images_ = GetBorderImages(shadow_); + } +} + void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { if (UseMaterialDesign()) return PaintMd(view, canvas); @@ -365,8 +384,11 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { } gfx::Insets BubbleBorder::GetInsets() const { - if (UseMaterialDesign()) - return (shadow_ == NO_ASSETS) ? gfx::Insets() : GetBorderAndShadowInsets(); + if (UseMaterialDesign()) { + return (shadow_ == NO_ASSETS) + ? gfx::Insets() + : GetBorderAndShadowInsets(md_shadow_elevation_); + } // The insets contain the stroke and shadow pixels outside the bubble fill. const int inset = GetBorderThickness(); @@ -388,14 +410,20 @@ gfx::Size BubbleBorder::GetMinimumSize() const { } // static -const cc::PaintFlags& BubbleBorder::GetBorderAndShadowFlags() { - // This object is always the same, so construct it once and cache. - static const base::NoDestructor<cc::PaintFlags> flags([] { - cc::PaintFlags f; - constexpr SkColor kBorderColor = SkColorSetA(SK_ColorBLACK, 0x26); - f.setColor(kBorderColor); - f.setAntiAlias(true); - +const gfx::ShadowValues& BubbleBorder::GetShadowValues( + base::Optional<int> elevation) { + // The shadows are always the same for any elevation, so construct them once + // and cache. + static base::NoDestructor<std::map<int, gfx::ShadowValues>> shadow_map; + if (shadow_map->find(elevation.value_or(-1)) != shadow_map->end()) + return shadow_map->find(elevation.value_or(-1))->second; + + gfx::ShadowValues shadows; + if (elevation.has_value()) { + DCHECK(elevation.value() >= 0); + shadows = gfx::ShadowValues( + gfx::ShadowValue::MakeMdShadowValues(elevation.value())); + } else { constexpr int kSmallShadowVerticalOffset = 2; constexpr int kSmallShadowBlur = 4; constexpr SkColor kSmallShadowColor = SkColorSetA(SK_ColorBLACK, 0x33); @@ -403,15 +431,38 @@ const cc::PaintFlags& BubbleBorder::GetBorderAndShadowFlags() { // gfx::ShadowValue counts blur pixels both inside and outside the shape, // whereas these blur values only describe the outside portion, hence they // must be doubled. - f.setLooper(gfx::CreateShadowDrawLooper({ + shadows = gfx::ShadowValues({ {gfx::Vector2d(0, kSmallShadowVerticalOffset), 2 * kSmallShadowBlur, kSmallShadowColor}, {gfx::Vector2d(0, kShadowVerticalOffset), 2 * kShadowBlur, kLargeShadowColor}, - })); - return f; - }()); - return *flags; + }); + } + + shadow_map->insert( + std::pair<int, gfx::ShadowValues>(elevation.value_or(-1), shadows)); + return shadow_map->find(elevation.value_or(-1))->second; +} + +// static +const cc::PaintFlags& BubbleBorder::GetBorderAndShadowFlags( + base::Optional<int> elevation) { + // The flags are always the same for any elevation, so construct them once and + // cache. + static base::NoDestructor<std::map<int, cc::PaintFlags>> flag_map; + + if (flag_map->find(elevation.value_or(-1)) != flag_map->end()) + return flag_map->find(elevation.value_or(-1))->second; + + cc::PaintFlags flags; + constexpr SkColor kBorderColor = SkColorSetA(SK_ColorBLACK, 0x26); + flags.setColor(kBorderColor); + flags.setAntiAlias(true); + flags.setLooper(gfx::CreateShadowDrawLooper(GetShadowValues(elevation))); + flag_map->insert( + std::pair<int, cc::PaintFlags>(elevation.value_or(-1), flags)); + + return flag_map->find(elevation.value_or(-1))->second; } gfx::Size BubbleBorder::GetSizeForContentsSize( @@ -547,7 +598,8 @@ void BubbleBorder::PaintMd(const View& view, gfx::Canvas* canvas) { canvas->sk_canvas()->clipRRect(r_rect, SkClipOp::kDifference, true /*doAntiAlias*/); - DrawBorderAndShadow(std::move(r_rect), &cc::PaintCanvas::drawRRect, canvas); + DrawBorderAndShadow(std::move(r_rect), &cc::PaintCanvas::drawRRect, canvas, + md_shadow_elevation_); } void BubbleBorder::PaintNoAssets(const View& view, gfx::Canvas* canvas) { @@ -561,6 +613,11 @@ internal::BorderImages* BubbleBorder::GetImagesForTest() const { return images_; } +bool BubbleBorder::UseMaterialDesign() const { + return ui::MaterialDesignController::IsSecondaryUiMaterial() || + corner_radius_.has_value(); +} + 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 46531ed02f6..400366fd808 100644 --- a/chromium/ui/views/bubble/bubble_border.h +++ b/chromium/ui/views/bubble/bubble_border.h @@ -13,6 +13,7 @@ #include "build/build_config.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia.h" +#include "ui/gfx/shadow_value.h" #include "ui/views/background.h" #include "ui/views/border.h" @@ -172,26 +173,34 @@ class VIEWS_EXPORT BubbleBorder : public Border { a : static_cast<Arrow>(a ^ BOTTOM); } - // Returns the insets required by a border and shadow. This is only used for - // MD bubbles. - static gfx::Insets GetBorderAndShadowInsets(); + // Returns the insets required by a border and shadow based on + // |shadow_elevation|. This is only used for MD bubbles. A null + // |shadow_elevation| will yield the default BubbleBorder MD insets. + static gfx::Insets GetBorderAndShadowInsets( + base::Optional<int> shadow_elevation = base::nullopt); - // Draws a border and shadow outside the |rect| on |canvas|, using |draw| as - // the draw function. Templated so as to accept either SkRect or SkRRect. + // Draws a border and shadow based on |shadow_elevation| outside the |rect| on + // |canvas|, using |draw| as the draw function. Templated so as to accept + // either SkRect or SkRRect. template <typename T> static void DrawBorderAndShadow( T rect, void (cc::PaintCanvas::*draw)(const T&, const cc::PaintFlags&), - gfx::Canvas* canvas) { + gfx::Canvas* canvas, + base::Optional<int> shadow_elevation = base::nullopt) { // Provide a 1 px border outside the bounds. const int kBorderStrokeThicknessPx = 1; const SkScalar one_pixel = SkFloatToScalar(kBorderStrokeThicknessPx / canvas->image_scale()); rect.outset(one_pixel, one_pixel); - (canvas->sk_canvas()->*draw)(rect, GetBorderAndShadowFlags()); + (canvas->sk_canvas()->*draw)(rect, + GetBorderAndShadowFlags(shadow_elevation)); } + // Set the corner radius, enables Material Design. + void SetCornerRadius(int radius); + // Get or set the arrow type. void set_arrow(Arrow arrow) { arrow_ = arrow; } Arrow arrow() const { return arrow_; } @@ -221,9 +230,17 @@ class VIEWS_EXPORT BubbleBorder : public Border { // location to place the arrow |offset| pixels from the perpendicular edge. void set_arrow_offset(int offset) { arrow_offset_ = offset; } - // Sets the way the arrow is actually painted. Default is PAINT_NORMAL. + // Sets the way the arrow is actually painted. Default is PAINT_NORMAL. void set_paint_arrow(ArrowPaintType value); + // Sets the shadow elevation for MD shadows. A null |shadow_elevation| will + // yield the default BubbleBorder MD shadow. + void set_md_shadow_elevation(int shadow_elevation) { + DCHECK(UseMaterialDesign()) << "Setting a non-default MD shadow elevation " + "requires that the BubbleBorder is using MD"; + md_shadow_elevation_ = shadow_elevation; + } + // Get the desired widget bounds (in screen coordinates) given the anchor rect // and bubble content size; calculated from shadow and arrow image dimensions. virtual gfx::Rect GetBounds(const gfx::Rect& anchor_rect, @@ -259,13 +276,25 @@ class VIEWS_EXPORT BubbleBorder : public Border { FRIEND_TEST_ALL_PREFIXES(BubbleBorderTest, GetBoundsOriginTest); FRIEND_TEST_ALL_PREFIXES(BubbleBorderTest, ShadowTypes); - // Returns the paint flags to use for painting the border and shadow. This is - // only used for MD bubbles. - static const cc::PaintFlags& GetBorderAndShadowFlags(); + // Returns the shadows based on |shadow_elevation| to use for painting the + // border and shadow, and for getting insets. This is only used for MD + // bubbles. A null |shadow_elevation| will yield the default BubbleBorder MD + // ShadowValues. + static const gfx::ShadowValues& GetShadowValues( + base::Optional<int> shadow_elevation = base::nullopt); + + // Returns the paint flags to use for painting the border and shadow based on + // |shadow_elevation|. This is only used for MD bubbles. A null + // |shadow_elevation| will yield the default BubbleBorder MD PaintFlags. + static const cc::PaintFlags& GetBorderAndShadowFlags( + base::Optional<int> shadow_elevation = base::nullopt); // The border and arrow stroke size used in image assets, in pixels. static const int kStroke; + // Initializes the MD or non-MD BubbleBorder. + void Init(); + gfx::Size GetSizeForContentsSize(const gfx::Size& contents_size) const; gfx::ImageSkia* GetArrowImage() const; gfx::Rect GetArrowRect(const gfx::Rect& bounds) const; @@ -287,8 +316,16 @@ class VIEWS_EXPORT BubbleBorder : public Border { internal::BorderImages* GetImagesForTest() const; + // Whether to use material design. + bool UseMaterialDesign() const; + Arrow arrow_; int arrow_offset_; + // Corner radius for the bubble border. If supplied the border will use + // material design. + base::Optional<int> corner_radius_; + // Elevation for the MD shadow. Requires material design. + base::Optional<SkColor> md_shadow_elevation_; ArrowPaintType arrow_paint_type_; BubbleAlignment alignment_; Shadow shadow_; diff --git a/chromium/ui/views/bubble/bubble_border_unittest.cc b/chromium/ui/views/bubble/bubble_border_unittest.cc index adb23135acf..87ca9bc7786 100644 --- a/chromium/ui/views/bubble/bubble_border_unittest.cc +++ b/chromium/ui/views/bubble/bubble_border_unittest.cc @@ -423,9 +423,8 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { : kAnchor.x() + kStrokeWidth - kBorderThickness, kTopHorizArrowY}, {BubbleBorder::TOP_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, - UseMd() ? kAnchor.CenterPoint().x() - : kAnchor.CenterPoint().x() - kArrowOffsetForHorizCenter, - kTopHorizArrowY + (UseMd() ? kInsets.top() - kStrokeWidth : 0)}, + kAnchor.CenterPoint().x() - kArrowOffsetForHorizCenter, + kTopHorizArrowY}, {BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, UseMd() ? kAnchor.CenterPoint().x() - kTotalSizeWithHorizArrow.width() : kAnchor.CenterPoint().x() + kArrowOffsetForNotCenter - diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.cc b/chromium/ui/views/bubble/bubble_dialog_delegate.cc index 9f766e72100..5856ea9b8b1 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.cc @@ -4,7 +4,6 @@ #include "ui/views/bubble/bubble_dialog_delegate.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "build/build_config.h" #include "ui/accessibility/ax_node_data.h" @@ -38,6 +37,10 @@ Widget* CreateBubbleWidget(BubbleDialogDelegateView* bubble) { bubble_params.delegate = bubble; bubble_params.opacity = Widget::InitParams::TRANSLUCENT_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->parent_window()) bubble_params.parent = bubble->parent_window(); else if (bubble->anchor_widget()) @@ -47,8 +50,13 @@ Widget* CreateBubbleWidget(BubbleDialogDelegateView* bubble) { : Widget::InitParams::ACTIVATABLE_NO; bubble->OnBeforeBubbleWidgetInit(&bubble_params, bubble_widget); bubble_widget->Init(bubble_params); +#if !defined(OS_MACOSX) + // On Mac, having a parent window creates a permanent stacking order, so + // there's no need to do this. Also, calling StackAbove() on Mac shows the + // bubble implicitly, for which the bubble is currently not ready. if (bubble_params.parent) bubble_widget->StackAbove(bubble_params.parent); +#endif return bubble_widget; } @@ -214,14 +222,15 @@ BubbleDialogDelegateView::BubbleDialogDelegateView() : BubbleDialogDelegateView(nullptr, BubbleBorder::TOP_LEFT) {} BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view, - BubbleBorder::Arrow arrow) + BubbleBorder::Arrow arrow, + BubbleBorder::Shadow shadow) : close_on_deactivate_(true), anchor_view_tracker_(std::make_unique<ViewTracker>()), anchor_widget_(nullptr), arrow_(arrow), mirror_arrow_in_rtl_( ViewsDelegate::GetInstance()->ShouldMirrorArrowsInRTL()), - shadow_(BubbleBorder::DIALOG_SHADOW), + shadow_(shadow), color_explicitly_set_(false), accept_events_(true), adjust_if_offscreen_(true), @@ -250,6 +259,13 @@ gfx::Rect BubbleDialogDelegateView::GetBubbleBounds() { adjust_if_offscreen_ && !anchor_minimized && has_anchor); } +ax::mojom::Role BubbleDialogDelegateView::GetAccessibleWindowRole() const { + // We return |ax::mojom::Role::kAlertDialog| which will make screen + // readers announce the contents of the bubble dialog as soon as it appears, + // as long as we also fire |ax::mojom::Event::kAlert|. + return ax::mojom::Role::kAlertDialog; +} + void BubbleDialogDelegateView::OnNativeThemeChanged( const ui::NativeTheme* theme) { UpdateColorsFromTheme(theme); @@ -325,9 +341,11 @@ void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget, // the bubble in its entirety rather than just its title and initially focused // view. See http://crbug.com/474622 for details. if (widget == GetWidget() && visible) { - if (GetAccessibleWindowRole() == ax::mojom::Role::kAlertDialog) + if (GetAccessibleWindowRole() == ax::mojom::Role::kAlert || + GetAccessibleWindowRole() == ax::mojom::Role::kAlertDialog) { widget->GetRootView()->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true); + } } } diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.h b/chromium/ui/views/bubble/bubble_dialog_delegate.h index ab5ca68d283..fa45ae39055 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate.h +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.h @@ -10,6 +10,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" #include "build/build_config.h" +#include "ui/accessibility/ax_enums.mojom.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" @@ -127,11 +128,20 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, protected: BubbleDialogDelegateView(); - BubbleDialogDelegateView(View* anchor_view, BubbleBorder::Arrow arrow); + // |shadow| usually doesn't need to be explicitly set, just uses the default + // argument. Unless on Mac when the bubble needs to use Views base shadow, + // override it with suitable bubble border type. + BubbleDialogDelegateView( + View* anchor_view, + BubbleBorder::Arrow arrow, + BubbleBorder::Shadow shadow = BubbleBorder::DIALOG_SHADOW); // Get bubble bounds from the anchor rect and client view's preferred size. virtual gfx::Rect GetBubbleBounds(); + // DialogDelegateView overrides: + ax::mojom::Role GetAccessibleWindowRole() const override; + // View overrides: void OnNativeThemeChanged(const ui::NativeTheme* theme) override; diff --git a/chromium/ui/views/bubble/info_bubble.cc b/chromium/ui/views/bubble/info_bubble.cc index b17c6db9872..65fc0411ece 100644 --- a/chromium/ui/views/bubble/info_bubble.cc +++ b/chromium/ui/views/bubble/info_bubble.cc @@ -11,6 +11,7 @@ #include "ui/views/bubble/bubble_frame_view.h" #include "ui/views/controls/label.h" #include "ui/views/layout/fill_layout.h" +#include "ui/views/layout/layout_provider.h" #include "ui/views/widget/widget.h" namespace views { @@ -20,10 +21,6 @@ namespace { // The visible width of bubble borders (differs from the actual width) in px. const int kBubbleBorderVisibleWidth = 1; -// The margin between the content of the error bubble and its border. -const int kInfoBubbleHorizontalMargin = 14; -const int kInfoBubbleVerticalMargin = 12; - } // namespace class InfoBubbleFrame : public BubbleFrameView { @@ -52,8 +49,8 @@ InfoBubble::InfoBubble(View* anchor, const base::string16& message) DCHECK(anchor_); SetAnchorView(anchor_); - set_margins( - gfx::Insets(kInfoBubbleVerticalMargin, kInfoBubbleHorizontalMargin)); + set_margins(LayoutProvider::Get()->GetInsetsMetric( + InsetsMetric::INSETS_TOOLTIP_BUBBLE)); set_can_activate(false); SetLayoutManager(std::make_unique<FillLayout>()); diff --git a/chromium/ui/views/bubble/tooltip_icon.cc b/chromium/ui/views/bubble/tooltip_icon.cc index de30b6dcc62..b25fd527510 100644 --- a/chromium/ui/views/bubble/tooltip_icon.cc +++ b/chromium/ui/views/bubble/tooltip_icon.cc @@ -4,7 +4,6 @@ #include "ui/views/bubble/tooltip_icon.h" -#include "base/memory/ptr_util.h" #include "base/timer/timer.h" #include "components/vector_icons/vector_icons.h" #include "ui/accessibility/ax_node_data.h" diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc index fe4ba9727f6..1a5f43ef3df 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.cc +++ b/chromium/ui/views/bubble/tray_bubble_view.cc @@ -150,7 +150,8 @@ TrayBubbleView::InitParams::InitParams(const InitParams& other) = default; TrayBubbleView::RerouteEventHandler::RerouteEventHandler( TrayBubbleView* tray_bubble_view) : tray_bubble_view_(tray_bubble_view) { - aura::Env::GetInstance()->PrependPreTargetHandler(this); + aura::Env::GetInstance()->AddPreTargetHandler( + this, ui::EventTarget::Priority::kSystem); } TrayBubbleView::RerouteEventHandler::~RerouteEventHandler() { @@ -210,7 +211,8 @@ TrayBubbleView::TrayBubbleView(const InitParams& init_params) preferred_width_(init_params.min_width), bubble_border_(new BubbleBorder( arrow(), - BubbleBorder::NO_ASSETS, + 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), @@ -221,6 +223,8 @@ TrayBubbleView::TrayBubbleView(const InitParams& init_params) bubble_border_->set_use_theme_background_color(!init_params.bg_color); bubble_border_->set_alignment(BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE); bubble_border_->set_paint_arrow(BubbleBorder::PAINT_NONE); + 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); @@ -313,6 +317,13 @@ 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(GetBubbleBounds()); @@ -320,9 +331,11 @@ void TrayBubbleView::SizeToContents() { void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params, Widget* bubble_widget) const { - // Apply a WM-provided shadow (see ui/wm/core/). - params->shadow_type = Widget::InitParams::SHADOW_TYPE_DROP; - params->shadow_elevation = wm::kShadowElevationActiveWindow; + 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) { diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h index aeff9a874a0..92f3ed00638 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.h +++ b/chromium/ui/views/bubble/tray_bubble_view.h @@ -9,6 +9,7 @@ #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.h" @@ -95,6 +96,8 @@ class VIEWS_EXPORT TrayBubbleView : public BubbleDialogDelegateView, 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); @@ -160,6 +163,7 @@ class VIEWS_EXPORT TrayBubbleView : public BubbleDialogDelegateView, protected: // Overridden from views::BubbleDialogDelegateView. int GetDialogButtons() const override; + ax::mojom::Role GetAccessibleWindowRole() const override; void SizeToContents() override; // Overridden from views::View. diff --git a/chromium/ui/views/cocoa/OWNERS b/chromium/ui/views/cocoa/OWNERS index 13c6f49f974..50a55e521ee 100644 --- a/chromium/ui/views/cocoa/OWNERS +++ b/chromium/ui/views/cocoa/OWNERS @@ -1 +1,2 @@ tapted@chromium.org +ellyjones@chromium.org diff --git a/chromium/ui/views/cocoa/bridged_content_view.h b/chromium/ui/views/cocoa/bridged_content_view.h index c04515dd324..bb53eab7765 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.h +++ b/chromium/ui/views/cocoa/bridged_content_view.h @@ -49,9 +49,6 @@ class View; // that OSX correctly blurs the background showing through. BOOL drawMenuBackgroundForBlur_; - // Whether dragging on the view moves the window. - BOOL mouseDownCanMoveWindow_; - // The cached window mask. Only used for non-rectangular windows on 10.9. base::scoped_nsobject<NSBezierPath> windowMask_; } @@ -60,11 +57,6 @@ class View; @property(assign, nonatomic) ui::TextInputClient* textInputClient; @property(assign, nonatomic) BOOL drawMenuBackgroundForBlur; -// Extends an atomic, readonly property on NSView to make it assignable. -// This usually returns YES if the view is transparent. We want to control it -// so that BridgedNativeWidget can dynamically enable dragging of the window. -@property(assign) BOOL mouseDownCanMoveWindow; - // Initialize the NSView -> views::View bridge. |viewToHost| must be non-NULL. - (id)initWithView:(views::View*)viewToHost; diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views/cocoa/bridged_content_view.mm index 5faee65a24f..c55fb600d20 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.mm +++ b/chromium/ui/views/cocoa/bridged_content_view.mm @@ -14,6 +14,7 @@ #include "ui/base/cocoa/cocoa_base_utils.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/dragdrop/os_exchange_data_provider_mac.h" +#include "ui/base/hit_test.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_edit_commands.h" #include "ui/base/ime/text_input_client.h" @@ -24,6 +25,7 @@ #import "ui/events/keycodes/keyboard_code_conversion_mac.h" #include "ui/gfx/canvas_paint_mac.h" #include "ui/gfx/decorated_text.h" +#import "ui/gfx/decorated_text_mac.h" #include "ui/gfx/geometry/rect.h" #import "ui/gfx/mac/coordinate_conversion.h" #include "ui/gfx/path.h" @@ -200,42 +202,6 @@ base::string16 AttributedSubstringForRangeHelper( return substring; } -NSAttributedString* GetAttributedString( - const gfx::DecoratedText& decorated_text) { - base::scoped_nsobject<NSMutableAttributedString> str( - [[NSMutableAttributedString alloc] - initWithString:base::SysUTF16ToNSString(decorated_text.text)]); - [str beginEditing]; - - NSValue* const line_style = - @(NSUnderlineStyleSingle | NSUnderlinePatternSolid); - - for (const auto& attribute : decorated_text.attributes) { - DCHECK(!attribute.range.is_reversed()); - DCHECK_LE(attribute.range.end(), [str length]); - - NSMutableDictionary* attrs = [NSMutableDictionary dictionary]; - NSRange range = attribute.range.ToNSRange(); - - if (attribute.font.GetNativeFont()) - attrs[NSFontAttributeName] = attribute.font.GetNativeFont(); - - // NSFont does not have underline as an attribute. Hence handle it - // separately. - const bool underline = attribute.font.GetStyle() & gfx::Font::UNDERLINE; - if (underline) - attrs[NSUnderlineStyleAttributeName] = line_style; - - if (attribute.strike) - attrs[NSStrikethroughStyleAttributeName] = line_style; - - [str setAttributes:attrs range:range]; - } - - [str endEditing]; - return str.autorelease(); -} - ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { if (action == @selector(undo:)) return ui::TextEditCommand::UNDO; @@ -309,7 +275,6 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { @synthesize hostedView = hostedView_; @synthesize textInputClient = textInputClient_; @synthesize drawMenuBackgroundForBlur = drawMenuBackgroundForBlur_; -@synthesize mouseDownCanMoveWindow = mouseDownCanMoveWindow_; - (id)initWithView:(views::View*)viewToHost { DCHECK(viewToHost); @@ -356,6 +321,16 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { [self removeTrackingArea:cursorTrackingArea_.get()]; } +// If the point is classified as HTCAPTION (background, draggable), return nil +// so that it can lead to a window drag or double-click in the title bar. +- (NSView*)hitTest:(NSPoint)point { + gfx::Point flippedPoint(point.x, NSHeight(self.superview.bounds) - point.y); + int component = hostedView_->GetWidget()->GetNonClientComponent(flippedPoint); + if (component == HTCAPTION) + return nil; + return [super hitTest:point]; +} + - (void)processCapturedMouseEvent:(NSEvent*)theEvent { if (!hostedView_) return; @@ -649,11 +624,16 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // NSView implementation. -// Always refuse first responder. Note this does not prevent the view becoming -// first responder via -[NSWindow makeFirstResponder:] when invoked during Init -// or by FocusManager. +// Refuse first responder, unless we are already first responder. Note this does +// not prevent the view becoming first responder via -[NSWindow +// makeFirstResponder:] when invoked during Init or by FocusManager. +// +// The condition is to work around an AppKit quirk. When a window is being +// ordered front, if its current first responder returns |NO| for this method, +// it resigns it if it can find another responder in the key loop that replies +// |YES|. - (BOOL)acceptsFirstResponder { - return NO; + return [[self window] firstResponder] == self; } - (BOOL)becomeFirstResponder { @@ -850,6 +830,11 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { [self handleKeyEvent:&event]; } +- (void)flagsChanged:(NSEvent*)theEvent { + ui::KeyEvent event(theEvent); + [self handleKeyEvent:&event]; +} + - (void)scrollWheel:(NSEvent*)theEvent { if (!hostedView_) return; @@ -904,7 +889,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { views::View::ConvertPointToTarget(hostedView_, target, &locationInTarget); gfx::DecoratedText decoratedWord; gfx::Point baselinePoint; - if (!wordLookupClient->GetDecoratedWordAtPoint( + if (!wordLookupClient->GetWordLookupDataAtPoint( locationInTarget, &decoratedWord, &baselinePoint)) { return; } @@ -913,7 +898,8 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { views::View::ConvertPointToTarget(target, hostedView_, &baselinePoint); NSPoint baselinePointAppKit = NSMakePoint( baselinePoint.x(), NSHeight([self frame]) - baselinePoint.y()); - [self showDefinitionForAttributedString:GetAttributedString(decoratedWord) + [self showDefinitionForAttributedString: + gfx::GetAttributedStringFromDecoratedText(decoratedWord) atPoint:baselinePointAppKit]; } @@ -1464,16 +1450,16 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { composition.text = base::SysNSStringToUTF16(text); composition.selection = gfx::Range(selectedRange); - // Add a black underline with a transparent background to the composition - // text. TODO(karandeepb): On Cocoa textfields, the target clause of the - // composition has a thick underlines. The composition text also has + // Add an underline with text color and a transparent background to the + // composition text. TODO(karandeepb): On Cocoa textfields, the target clause + // of the composition has a thick underlines. The composition text also has // discontinous underlines for different clauses. This is also supported in // the Chrome renderer. Add code to extract underlines from |text| once our // render text implementation supports thick underlines and discontinous // underlines for consecutive characters. See http://crbug.com/612675. composition.ime_text_spans.push_back( ui::ImeTextSpan(ui::ImeTextSpan::Type::kComposition, 0, [text length], - SK_ColorBLACK, false, SK_ColorTRANSPARENT)); + ui::ImeTextSpan::Thickness::kThin, SK_ColorTRANSPARENT)); textInputClient_->SetCompositionText(composition); hasUnhandledKeyDownEvent_ = NO; } diff --git a/chromium/ui/views/cocoa/bridged_native_widget.h b/chromium/ui/views/cocoa/bridged_native_widget.h index 64be7882515..9af5c2db7db 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.h +++ b/chromium/ui/views/cocoa/bridged_native_widget.h @@ -71,10 +71,6 @@ class VIEWS_EXPORT BridgedNativeWidget static gfx::Size GetWindowSizeForClientSize(NSWindow* window, const gfx::Size& size); - // Whether an event monitor should be used to intercept window drag events. - // Evalutes to |true| on macOS 10.10 and older. - static bool ShouldUseDragEventMonitor(); - // Creates one side of the bridge. |parent| must not be NULL. explicit BridgedNativeWidget(NativeWidgetMac* parent); ~BridgedNativeWidget() override; @@ -162,17 +158,6 @@ class VIEWS_EXPORT BridgedNativeWidget // Called by the NSWindowDelegate when the window becomes or resigns key. void OnWindowKeyStatusChangedTo(bool is_key); - // Returns true if the |event| should initiate a window drag. - bool ShouldDragWindow(NSEvent* event); - - // Called when the application receives a mouse-down, but before the event is - // processed by NSWindow. Returning true here will cause the event to be - // cancelled and reposted at the CGSessionEventTap level. This is used to - // determine whether a mouse-down should drag the window. Only called when - // ShouldUseDragEventMonitor() returns true. - // Virtual for testing. - virtual bool ShouldRepostPendingLeftMouseDown(NSEvent* event); - // Called by NativeWidgetMac when the window size constraints change. void OnSizeConstraintsChanged(); @@ -264,13 +249,6 @@ class VIEWS_EXPORT BridgedNativeWidget // Show the window using -[NSApp beginSheet:..], modal for the parent window. void ShowAsModalSheet(); - // Sets mouseDownCanMoveWindow on |bridged_view_| and triggers the NSWindow to - // update its draggable region. - void SetDraggable(bool draggable); - - // Called by |mouse_down_monitor_| to close a bubble. - void OnRightMouseDownWithBubble(NSEvent* event); - // Overridden from CocoaMouseCaptureDelegate: void PostCapturedEvent(NSEvent* event) override; void OnMouseCaptureLost() override; @@ -360,9 +338,6 @@ class VIEWS_EXPORT BridgedNativeWidget // the compositor arrives to avoid "blinking". bool initial_visibility_suppressed_ = false; - // Right mouse down monitor for bubble widget. - id mouse_down_monitor_; - AssociatedViews associated_views_; DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidget); diff --git a/chromium/ui/views/cocoa/bridged_native_widget.mm b/chromium/ui/views/cocoa/bridged_native_widget.mm index 3c6e9903d7d..64ea16b86ef 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget.mm @@ -12,7 +12,6 @@ #import "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #import "base/mac/sdk_forward_declarations.h" -#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "components/viz/common/features.h" @@ -126,15 +125,6 @@ using NSViewComparatorValue = id; using NSViewComparatorValue = __kindof NSView*; #endif -const CGFloat kMavericksMenuOpacity = 251.0 / 255.0; -const CGFloat kYosemiteMenuOpacity = 177.0 / 255.0; -const int kYosemiteMenuBlur = 80; - -// Margin at edge and corners of the window that trigger resizing. These match -// actual Cocoa resize margins. -const int kResizeAreaEdgeSize = 3; -const int kResizeAreaCornerSize = 12; - int kWindowPropertiesKey; float GetDeviceScaleFactorFromView(NSView* view) { @@ -178,141 +168,6 @@ gfx::Size GetClientSizeForWindowSize(NSWindow* window, return gfx::Size([window contentRectForFrameRect:frame_rect].size); } -// Determine whether a point is within the resize area at the edges and corners -// of a window. This is used to ensure that mouse downs which would resize the -// window are not reposted. As there's no way to determine this from Cocoa APIs, -// this should aim to match Cocoa behavior as closely as possible. -bool IsPointInResizeArea(NSPoint point, NSWindow* window) { - if (!([window styleMask] & NSResizableWindowMask)) - return false; - - bool can_resize_x = [window maxSize].width > [window minSize].width; - bool can_resize_y = [window maxSize].height > [window minSize].height; - NSSize window_size = [window frame].size; - - if (can_resize_x && (point.x < kResizeAreaEdgeSize || - point.x >= window_size.width - kResizeAreaEdgeSize)) - return true; - - if (can_resize_y && (point.y < kResizeAreaEdgeSize || - point.y >= window_size.height - kResizeAreaEdgeSize)) - return true; - - if (can_resize_x && can_resize_y && - (point.x < kResizeAreaCornerSize || - point.x >= window_size.width - kResizeAreaCornerSize) && - (point.y < kResizeAreaCornerSize || - point.y >= window_size.height - kResizeAreaCornerSize)) - return true; - - return false; -} - -// Routes the |ns_event| to the corresponding BridgedNativeWidget and queries -// whether the event should be reposted. -BOOL WindowWantsMouseDownReposted(NSEvent* ns_event) { - DCHECK(views::BridgedNativeWidget::ShouldUseDragEventMonitor()); - - views::BridgedNativeWidget* bridge = - views::NativeWidgetMac::GetBridgeForNativeWindow([ns_event window]); - return bridge && bridge->ShouldRepostPendingLeftMouseDown(ns_event); -} - -// Check if a mouse-down event should drag the window. If so, repost the event. -NSEvent* RepostEventIfHandledByWindow(NSEvent* ns_event) { - DCHECK(views::BridgedNativeWidget::ShouldUseDragEventMonitor()); - - enum RepostState { - // Nothing reposted: hit-test new mouse-downs to see if they need to be - // ignored and reposted after changing draggability. - NONE, - // Expecting the next event to be the reposted event: let it go through. - EXPECTING_REPOST, - // If, while reposting, another mousedown was received: when the reposted - // event is seen, ignore it. - REPOST_CANCELLED, - }; - - // Which repost we're expecting to receive. - static RepostState repost_state = NONE; - // The event number of the reposted event. This let's us track whether an - // event is actually the repost since user-generated events have increasing - // event numbers. This is only valid while |repost_state != NONE|. - static NSInteger reposted_event_number; - - NSInteger event_number = [ns_event eventNumber]; - - // The logic here is a bit convoluted because we want to mitigate race - // conditions if somehow a different mouse-down occurs between reposts. - // Specifically, we want to avoid: - // - BridgedNativeWidget's draggability getting out of sync (e.g. if it is - // draggable outside of a repost cycle), - // - any repost loop. - - if (repost_state == NONE) { - if (WindowWantsMouseDownReposted(ns_event)) { - repost_state = EXPECTING_REPOST; - reposted_event_number = event_number; - CGEventPost(kCGSessionEventTap, [ns_event CGEvent]); - return nil; - } - - return ns_event; - } - - if (repost_state == EXPECTING_REPOST) { - // Call through so that the window is made non-draggable again. - WindowWantsMouseDownReposted(ns_event); - - if (reposted_event_number == event_number) { - // Reposted event received. - repost_state = NONE; - return nil; - } - - // We were expecting a repost, but since this is a new mouse-down, cancel - // reposting and allow event to continue as usual. - repost_state = REPOST_CANCELLED; - return ns_event; - } - - DCHECK_EQ(REPOST_CANCELLED, repost_state); - if (reposted_event_number == event_number) { - // Reposting was cancelled, now that we've received the event, we don't - // expect to see it again. - repost_state = NONE; - return nil; - } - - return ns_event; -} - -// Support window caption/draggable regions. -// In AppKit, non-client regions are set by overriding -// -[NSView mouseDownCanMoveWindow]. NSApplication caches this area as views are -// installed and performs window moving when mouse-downs land in the area. -// In Views, non-client regions are determined via hit-tests when the event -// occurs. -// To bridge the two models, we monitor mouse-downs with -// +[NSEvent addLocalMonitorForEventsMatchingMask:handler:]. This receives -// events after window dragging is handled, so for mouse-downs that land on a -// draggable point, we cancel the event, make the window draggable and repost it -// at the CGSessionEventTap level so that window dragging will be handled again. -// On Mac OS > 10.10, we don't use an event monitor. Instead, we use [NSWindow -// performWindowDragWithEvent:]. See [NativeWidgetMacNSWindow sendEvent:]. -void SetupDragEventMonitor() { - DCHECK(views::BridgedNativeWidget::ShouldUseDragEventMonitor()); - static id monitor = nil; - if (monitor) - return; - - monitor = [NSEvent - addLocalMonitorForEventsMatchingMask:NSLeftMouseDownMask - handler:^NSEvent*(NSEvent* ns_event) { - return RepostEventIfHandledByWindow(ns_event); - }]; -} - // Returns a task runner for creating a ui::Compositor. This allows compositor // tasks to be funneled through ui::WindowResizeHelper's task runner to allow // resize operations to coordinate with frames provided by the GPU process. @@ -384,13 +239,6 @@ gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); } -// static -// TODO(karandeepb): Remove usage of drag event monitor once we stop supporting -// Mac OS 10.10. -bool BridgedNativeWidget::ShouldUseDragEventMonitor() { - return base::mac::IsAtMostOS10_10(); -} - BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) : native_widget_mac_(parent), focus_manager_(nullptr), @@ -399,11 +247,7 @@ BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) target_fullscreen_state_(false), in_fullscreen_transition_(false), window_visible_(false), - wants_to_be_visible_(false), - mouse_down_monitor_(nullptr) { - if (BridgedNativeWidget::ShouldUseDragEventMonitor()) - SetupDragEventMonitor(); - + wants_to_be_visible_(false) { DCHECK(parent); window_delegate_.reset( [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); @@ -448,17 +292,6 @@ void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window, name:NSControlTintDidChangeNotification object:nil]; - // Right-clicks outside a bubble should dismiss them, but that doesn't cause - // loss of focus on Mac, so add an event monitor to detect. - if (params.type == Widget::InitParams::TYPE_BUBBLE) { - mouse_down_monitor_ = [NSEvent - addLocalMonitorForEventsMatchingMask:NSRightMouseDownMask - handler:^NSEvent* (NSEvent* event) { - OnRightMouseDownWithBubble(event); - return event; - }]; - } - // Validate the window's initial state, otherwise the bridge's initial // tracking state will be incorrect. DCHECK(![window_ isVisible]); @@ -798,10 +631,6 @@ void BridgedNativeWidget::OnWindowWillClose() { parent_ = nullptr; } [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; - if (mouse_down_monitor_) { - [NSEvent removeMonitor:mouse_down_monitor_]; - mouse_down_monitor_ = nullptr; - } [show_animation_ stopAnimation]; // If set, calls OnShowAnimationComplete(). DCHECK(!show_animation_); @@ -976,7 +805,8 @@ void BridgedNativeWidget::OnBackingPropertiesChanged() { void BridgedNativeWidget::OnWindowKeyStatusChangedTo(bool is_key) { Widget* widget = native_widget_mac()->GetWidget(); - widget->OnNativeWidgetActivationChanged(is_key); + if (!widget->OnNativeWidgetActivationChanged(is_key)) + return; // The contentView is the BridgedContentView hosting the views::RootView. The // focus manager will already know if a native subview has focus. if ([window_ contentView] == [window_ firstResponder]) { @@ -993,56 +823,6 @@ void BridgedNativeWidget::OnWindowKeyStatusChangedTo(bool is_key) { } } -bool BridgedNativeWidget::ShouldDragWindow(NSEvent* event) { - if (!bridged_view_ || [event type] != NSLeftMouseDown) - return false; - - NSPoint location_in_window = [event locationInWindow]; - if (IsPointInResizeArea(location_in_window, window_)) - return false; - - gfx::Point point(location_in_window.x, - NSHeight([window_ frame]) - location_in_window.y); - - if (native_widget_mac()->GetWidget()->GetNonClientComponent(point) != - HTCAPTION) - return false; - - // Check that the point is not obscured by non-content NSViews. - for (NSView* subview : [[bridged_view_ superview] subviews]) { - if (subview == bridged_view_.get()) - continue; - - if (![subview mouseDownCanMoveWindow] && - NSPointInRect(location_in_window, [subview frame])) - return false; - } - - return true; -} - -bool BridgedNativeWidget::ShouldRepostPendingLeftMouseDown(NSEvent* event) { - DCHECK(BridgedNativeWidget::ShouldUseDragEventMonitor()); - DCHECK_EQ(NSLeftMouseDown, [event type]); - - if (!bridged_view_) - return false; - - if ([bridged_view_ mouseDownCanMoveWindow]) { - // This is a re-post, the movement has already started, so we can make the - // window non-draggable again. - SetDraggable(false); - return false; - } - - if (!ShouldDragWindow(event)) - return false; - - // Make the window draggable, then return true to repost the event. - SetDraggable(true); - return true; -} - void BridgedNativeWidget::OnSizeConstraintsChanged() { // Don't modify the size constraints or fullscreen collection behavior while // in fullscreen or during a transition. OnFullscreenTransitionComplete will @@ -1465,22 +1245,6 @@ void BridgedNativeWidget::AddCompositorSuperview() { [background_layer setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable]; - if (widget_type_ == Widget::InitParams::TYPE_MENU) { - // Giving the canvas opacity messes up subpixel font rendering, so use a - // solid background, but make the CALayer transparent. - if (base::mac::IsAtLeastOS10_10()) { - [background_layer setOpacity:kYosemiteMenuOpacity]; - CGSSetWindowBackgroundBlurRadius( - _CGSDefaultConnection(), [window_ windowNumber], kYosemiteMenuBlur); - // The blur effect does not occur with a fully transparent (or fully - // layer-backed) window. Setting a window background will use square - // corners, so ask the contentView to draw one instead. - [bridged_view_ setDrawMenuBackgroundForBlur:YES]; - } else { - [background_layer setOpacity:kMavericksMenuOpacity]; - } - } - // Set the layer first to create a layer-hosting view (not layer-backed). [compositor_superview_ setLayer:background_layer]; [compositor_superview_ setWantsLayer:YES]; @@ -1565,38 +1329,4 @@ NSMutableDictionary* BridgedNativeWidget::GetWindowProperties() const { return properties; } -void BridgedNativeWidget::SetDraggable(bool draggable) { - DCHECK(BridgedNativeWidget::ShouldUseDragEventMonitor()); - - [bridged_view_ setMouseDownCanMoveWindow:draggable]; - // AppKit will not update its cache of mouseDownCanMoveWindow unless something - // changes. Previously we tried adding an NSView and removing it, but for some - // reason it required reposting the mouse-down event, and didn't always work. - // Calling the below seems to be an effective solution. - [window_ setMovableByWindowBackground:NO]; - [window_ setMovableByWindowBackground:YES]; -} - -void BridgedNativeWidget::OnRightMouseDownWithBubble(NSEvent* event) { - NSWindow* target = [event window]; - if ([target isSheet]) - return; - - // Do not close the bubble if the event happened on a window with a higher - // level. For example, the content of a browser action bubble opens a - // calendar picker window with NSPopUpMenuWindowLevel, and a date selection - // closes the picker window, but it should not close the bubble. - if ([target level] > [window_ level]) - return; - - // If the event is in |window_|'s hierarchy, do not close the bubble. - while (target) { - if (target == window_.get()) - return; - target = [target parentWindow]; - } - - OnWindowKeyStatusChangedTo(false); -} - } // namespace views 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 224ea14d0f1..f02e1b94c08 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm @@ -192,40 +192,6 @@ TEST_F(BridgedNativeWidgetUITest, FullscreenRestore) { namespace { -// This is used to wait for reposted events to be seen. We can't just use -// RunPendingMessages() because CGEventPost might not be synchronous. -class HitTestBridgedNativeWidget : public BridgedNativeWidget { - public: - explicit HitTestBridgedNativeWidget(NativeWidgetMac* widget) - : BridgedNativeWidget(widget) {} - - // BridgedNativeWidget: - bool ShouldRepostPendingLeftMouseDown(NSEvent* event) override { - did_repost_ = BridgedNativeWidget::ShouldRepostPendingLeftMouseDown(event); - - if (run_loop_) - run_loop_->Quit(); - - return did_repost_; - } - - void WaitForShouldRepost() { - base::RunLoop run_loop; - run_loop_ = &run_loop; - run_loop.Run(); - run_loop_ = nullptr; - } - - bool IsDraggable() { return [ns_view() mouseDownCanMoveWindow]; } - bool did_repost() { return did_repost_; } - - private: - base::RunLoop* run_loop_ = nullptr; - bool did_repost_ = false; - - DISALLOW_COPY_AND_ASSIGN(HitTestBridgedNativeWidget); -}; - // This is used to return a customized result to NonClientHitTest. class HitTestNonClientFrameView : public NativeFrameView { public: @@ -290,12 +256,7 @@ class HitTestNativeWidgetMac : public NativeWidgetMac { HitTestNativeWidgetMac(internal::NativeWidgetDelegate* delegate, NativeFrameView* native_frame_view) : NativeWidgetMac(delegate), native_frame_view_(native_frame_view) { - NativeWidgetMac::bridge_.reset(new HitTestBridgedNativeWidget(this)); - } - - HitTestBridgedNativeWidget* bridge() { - return static_cast<HitTestBridgedNativeWidget*>( - NativeWidgetMac::bridge_.get()); + NativeWidgetMac::bridge_.reset(new BridgedNativeWidget(this)); } // internal::NativeWidgetPrivate: @@ -345,11 +306,6 @@ TEST_F(BridgedNativeWidgetUITest, DISABLED_HitTest) { // mouse events. [window setIgnoresMouseEvents:NO]; - HitTestBridgedNativeWidget* bridge = native_widget->bridge(); - - const bool using_drag_event_monitor = - BridgedNativeWidget::ShouldUseDragEventMonitor(); - // Dragging the window should work. frame_view->set_hit_test_result(HTCAPTION); { @@ -360,18 +316,8 @@ TEST_F(BridgedNativeWidgetUITest, DISABLED_HitTest) { initForNotification:NSWindowWillMoveNotification]); NSEvent* mouse_down = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( NSMakePoint(20, 20), window); - EXPECT_FALSE(bridge->IsDraggable()); CGEventPost(kCGSessionEventTap, [mouse_down CGEvent]); - if (using_drag_event_monitor) { - bridge->WaitForShouldRepost(); - EXPECT_TRUE(bridge->did_repost()); - EXPECT_TRUE(bridge->IsDraggable()); - bridge->WaitForShouldRepost(); - EXPECT_FALSE(bridge->did_repost()); - EXPECT_FALSE(bridge->IsDraggable()); - } else { - WaitForEvent(NSLeftMouseDownMask); - } + WaitForEvent(NSLeftMouseDownMask); base::scoped_nsobject<WindowedNSNotificationObserver> did_move_observer( [[WindowedNSNotificationObserver alloc] @@ -402,18 +348,8 @@ TEST_F(BridgedNativeWidgetUITest, DISABLED_HitTest) { initForNotification:NSWindowWillMoveNotification]); NSEvent* mouse_down = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( bottom_right_point, window); - EXPECT_FALSE(bridge->IsDraggable()); CGEventPost(kCGSessionEventTap, [mouse_down CGEvent]); - if (using_drag_event_monitor) { - bridge->WaitForShouldRepost(); - EXPECT_TRUE(bridge->did_repost()); - EXPECT_TRUE(bridge->IsDraggable()); - bridge->WaitForShouldRepost(); - EXPECT_FALSE(bridge->did_repost()); - EXPECT_FALSE(bridge->IsDraggable()); - } else { - WaitForEvent(NSLeftMouseDownMask); - } + WaitForEvent(NSLeftMouseDownMask); base::scoped_nsobject<WindowedNSNotificationObserver> did_move_observer( [[WindowedNSNotificationObserver alloc] @@ -449,7 +385,6 @@ TEST_F(BridgedNativeWidgetUITest, DISABLED_HitTest) { initForNotification:NSWindowWillMoveNotification]); NSEvent* mouse_down = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( bottom_right_point, window); - EXPECT_FALSE(bridge->IsDraggable()); CGEventPost(kCGSessionEventTap, [mouse_down CGEvent]); base::scoped_nsobject<WindowedNSNotificationObserver> did_resize_observer( @@ -463,14 +398,6 @@ TEST_F(BridgedNativeWidgetUITest, DISABLED_HitTest) { NSMakePoint(x + 408, y + 2), NSLeftMouseUp, 0); CGEventPost(kCGSessionEventTap, [mouse_up CGEvent]); - if (using_drag_event_monitor) { - // The only event observed by us is the original mouse-down. It should not - // be reposted. - bridge->WaitForShouldRepost(); - EXPECT_FALSE(bridge->did_repost()); - EXPECT_FALSE(bridge->IsDraggable()); - } - EXPECT_TRUE([did_resize_observer wait]); EXPECT_EQ(0, [will_move_observer notificationCount]); EXPECT_EQ(410, [window frame].size.width); diff --git a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm index a61d582b79c..0f44dc785d3 100644 --- a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm +++ b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm @@ -122,27 +122,6 @@ if ([commandDispatcher_ preSendEvent:event]) return; - // If a window drag event monitor is not used, query the BridgedNativeWidget - // to decide if a window drag should be performed. - // This conditional is equivalent to - // !views::BridgedNativeWidget::ShouldUseDragEventMonitor(), but it also - // supresses the -Wunguarded-availability warning. - if (@available(macOS 10.11, *)) { - views::BridgedNativeWidget* bridge = - views::NativeWidgetMac::GetBridgeForNativeWindow(self); - - if (bridge && bridge->ShouldDragWindow(event)) { - // Using performWindowDragWithEvent: does not generate a - // NSWindowWillMoveNotification. Hence post one. - [[NSNotificationCenter defaultCenter] - postNotificationName:NSWindowWillMoveNotification - object:self]; - - [self performWindowDragWithEvent:event]; - return; - } - } - NSEventType type = [event type]; if ((type != NSKeyDown && type != NSKeyUp) || ![self hasViewsMenuActive]) { [super sendEvent:event]; diff --git a/chromium/ui/views/color_chooser/color_chooser_view.cc b/chromium/ui/views/color_chooser/color_chooser_view.cc index f082282683b..d844b6a58f2 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.cc +++ b/chromium/ui/views/color_chooser/color_chooser_view.cc @@ -388,7 +388,7 @@ ColorChooserView::ColorChooserView(ColorChooserListener* listener, layout->StartRow(0, 0); textfield_ = new Textfield(); textfield_->set_controller(this); - textfield_->set_default_width_in_chars(kTextfieldLengthInChars); + textfield_->SetDefaultWidthInChars(kTextfieldLengthInChars); layout->AddView(textfield_); selected_color_patch_ = new SelectedColorPatchView(); layout->AddView(selected_color_patch_); diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc index 77c453f79d0..8a2e4dff278 100644 --- a/chromium/ui/views/controls/button/button.cc +++ b/chromium/ui/views/controls/button/button.cc @@ -444,6 +444,7 @@ void Button::OnBlur() { std::unique_ptr<InkDrop> Button::CreateInkDrop() { std::unique_ptr<views::InkDropImpl> ink_drop = CreateDefaultInkDropImpl(); ink_drop->SetShowHighlightOnFocus(true); + ink_drop->SetAutoHighlightModeForPlatform(); return std::move(ink_drop); } diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc index 8468460448f..ecdcda3d503 100644 --- a/chromium/ui/views/controls/button/checkbox.cc +++ b/chromium/ui/views/controls/button/checkbox.cc @@ -16,6 +16,7 @@ #include "ui/gfx/color_utils.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/accessibility/view_accessibility.h" #include "ui/views/animation/ink_drop_impl.h" #include "ui/views/animation/ink_drop_ripple.h" #include "ui/views/controls/button/label_button_border.h" @@ -76,6 +77,7 @@ const char Checkbox::kViewClassName[] = "Checkbox"; Checkbox::Checkbox(const base::string16& label, bool force_md) : LabelButton(NULL, label), checked_(false), + label_ax_id_(0), use_md_(force_md || ui::MaterialDesignController::IsSecondaryUiMaterial()) { SetHorizontalAlignment(gfx::ALIGN_LEFT); @@ -152,6 +154,18 @@ void Checkbox::SetMultiLine(bool multi_line) { label()->SetMultiLine(multi_line); } +void Checkbox::SetAssociatedLabel(View* labelling_view) { + DCHECK(labelling_view); + label_ax_id_ = labelling_view->GetViewAccessibility().GetUniqueId().Get(); + ui::AXNodeData node_data; + labelling_view->GetAccessibleNodeData(&node_data); + // TODO(aleventhal) automatically handle setting the name from the related + // label in view_accessibility and have it update the name if the text of the + // associated label changes. + SetAccessibleName( + node_data.GetString16Attribute(ax::mojom::StringAttribute::kName)); +} + // TODO(tetsui): Remove this method and |use_md_| when MD for secondary UI // becomes default and IsSecondaryUiMaterial() is tautology. bool Checkbox::UseMd() const { @@ -176,6 +190,10 @@ void Checkbox::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->SetDefaultActionVerb(ax::mojom::DefaultActionVerb::kCheck); } } + if (label_ax_id_) { + node_data->AddIntListAttribute(ax::mojom::IntListAttribute::kLabelledbyIds, + {label_ax_id_}); + } } void Checkbox::OnFocus() { diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h index f6605e8dd4c..ca8da3cb2af 100644 --- a/chromium/ui/views/controls/button/checkbox.h +++ b/chromium/ui/views/controls/button/checkbox.h @@ -39,6 +39,15 @@ class VIEWS_EXPORT Checkbox : public LabelButton { void SetMultiLine(bool multi_line); + // If the accessible name should be the same as the labelling view's text, + // use this. It will set the accessible label relationship and copy the + // accessible name from the labelling views's accessible name. Any view with + // an accessible name can be used, e.g. a Label, StyledLabel or Link. + void SetAssociatedLabel(View* labelling_view); + + // LabelButton: + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + protected: // Returns whether MD is enabled. Returns true if |force_md| in the // constructor or --secondary-ui-md flag is set. @@ -46,7 +55,6 @@ class VIEWS_EXPORT Checkbox : public LabelButton { // LabelButton: const char* GetClassName() const override; - void GetAccessibleNodeData(ui::AXNodeData* node_data) override; void OnFocus() override; void OnBlur() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; @@ -95,6 +103,9 @@ class VIEWS_EXPORT Checkbox : public LabelButton { // The images for each button node_data. gfx::ImageSkia images_[2][2][STATE_COUNT]; + // The unique id for the associated label's accessible object. + int32_t label_ax_id_; + bool use_md_; DISALLOW_COPY_AND_ASSIGN(Checkbox); diff --git a/chromium/ui/views/controls/button/checkbox_unittest.cc b/chromium/ui/views/controls/button/checkbox_unittest.cc new file mode 100644 index 00000000000..2984921c5ff --- /dev/null +++ b/chromium/ui/views/controls/button/checkbox_unittest.cc @@ -0,0 +1,65 @@ +// 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/controls/button/checkbox.h" + +#include "base/strings/utf_string_conversions.h" +#include "ui/accessibility/ax_enums.mojom.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/views/controls/button/checkbox.h" +#include "ui/views/controls/styled_label.h" +#include "ui/views/test/views_test_base.h" + +namespace views { + +class CheckboxTest : public ViewsTestBase { + public: + CheckboxTest() {} + ~CheckboxTest() override {} + + void SetUp() override { + ViewsTestBase::SetUp(); + + // Create a widget so that the Checkbox can query the hover state + // correctly. + widget_ = std::make_unique<Widget>(); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 650, 650); + widget_->Init(params); + widget_->Show(); + + checkbox_ = new Checkbox(base::string16()); + widget_->SetContentsView(checkbox_); + } + + void TearDown() override { + widget_.reset(); + ViewsTestBase::TearDown(); + } + + protected: + Checkbox* checkbox() { return checkbox_; } + + private: + std::unique_ptr<Widget> widget_; + Checkbox* checkbox_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(CheckboxTest); +}; + +TEST_F(CheckboxTest, AccessibilityTest) { + const base::string16 label_text = base::ASCIIToUTF16("Some label"); + StyledLabel label(label_text, nullptr); + checkbox()->SetAssociatedLabel(&label); + + ui::AXNodeData ax_data; + checkbox()->GetAccessibleNodeData(&ax_data); + + EXPECT_EQ(ax_data.GetString16Attribute(ax::mojom::StringAttribute::kName), + label_text); + EXPECT_EQ(ax_data.role, ax::mojom::Role::kCheckBox); +} + +} // namespace views diff --git a/chromium/ui/views/controls/button/image_button_factory.cc b/chromium/ui/views/controls/button/image_button_factory.cc index 6e7ddda2763..649002d85ad 100644 --- a/chromium/ui/views/controls/button/image_button_factory.cc +++ b/chromium/ui/views/controls/button/image_button_factory.cc @@ -1,7 +1,6 @@ // 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/views/controls/button/image_button_factory.h" #include "ui/gfx/color_utils.h" diff --git a/chromium/ui/views/controls/button/image_button_factory.h b/chromium/ui/views/controls/button/image_button_factory.h index d9659570d0a..2bb9a229bd4 100644 --- a/chromium/ui/views/controls/button/image_button_factory.h +++ b/chromium/ui/views/controls/button/image_button_factory.h @@ -6,6 +6,7 @@ #define UI_VIEWS_CONTROLS_BUTTON_IMAGE_BUTTON_FACTORY_H_ #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/color_palette.h" #include "ui/views/views_export.h" namespace gfx { @@ -28,7 +29,7 @@ VIEWS_EXPORT ImageButton* CreateVectorImageButton(ButtonListener* listener); VIEWS_EXPORT void SetImageFromVectorIcon( ImageButton* button, const gfx::VectorIcon& icon, - SkColor related_text_color = SK_ColorBLACK); + SkColor related_text_color = gfx::kGoogleGrey900); } // namespace views diff --git a/chromium/ui/views/controls/button/image_button_factory_unittest.cc b/chromium/ui/views/controls/button/image_button_factory_unittest.cc index c23149989a3..52728ff3516 100644 --- a/chromium/ui/views/controls/button/image_button_factory_unittest.cc +++ b/chromium/ui/views/controls/button/image_button_factory_unittest.cc @@ -5,6 +5,7 @@ #include "ui/views/controls/button/image_button_factory.h" #include "components/vector_icons/vector_icons.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/color_utils.h" #include "ui/views/animation/test/ink_drop_host_view_test_api.h" #include "ui/views/controls/button/button.h" @@ -31,9 +32,9 @@ TEST_F(ImageButtonFactoryTest, SetImageFromVectorIcon) { EXPECT_EQ(color_utils::DeriveDefaultIconColor(SK_ColorRED), button->GetInkDropBaseColor()); - // Default to black. + // Default to GoogleGrey900. SetImageFromVectorIcon(button, vector_icons::kClose16Icon); - EXPECT_EQ(color_utils::DeriveDefaultIconColor(SK_ColorBLACK), + EXPECT_EQ(color_utils::DeriveDefaultIconColor(gfx::kGoogleGrey900), button->GetInkDropBaseColor()); delete button; } diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc index 7cf98686b71..d56f4cb2f73 100644 --- a/chromium/ui/views/controls/button/label_button.cc +++ b/chromium/ui/views/controls/button/label_button.cc @@ -10,7 +10,6 @@ #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "ui/gfx/animation/throb_animation.h" #include "ui/gfx/canvas.h" diff --git a/chromium/ui/views/controls/button/label_button_border.cc b/chromium/ui/views/controls/button/label_button_border.cc index d79d8a473b0..7908d16fc50 100644 --- a/chromium/ui/views/controls/button/label_button_border.cc +++ b/chromium/ui/views/controls/button/label_button_border.cc @@ -15,6 +15,7 @@ #include "ui/native_theme/native_theme.h" #include "ui/views/border.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/layout/layout_provider.h" #include "ui/views/native_theme_delegate.h" #include "ui/views/resources/grit/views_resources.h" @@ -113,7 +114,8 @@ gfx::Insets LabelButtonAssetBorder::GetDefaultInsetsForStyle( if (style == Button::STYLE_BUTTON) { insets = gfx::Insets(8, 13); } else if (style == Button::STYLE_TEXTBUTTON) { - insets = gfx::Insets(5, 6); + insets = LayoutProvider::Get()->GetInsetsMetric( + InsetsMetric::INSETS_LABEL_BUTTON); } else { NOTREACHED(); } diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc index b708b83b434..60f6ca0ffb3 100644 --- a/chromium/ui/views/controls/button/md_text_button.cc +++ b/chromium/ui/views/controls/button/md_text_button.cc @@ -159,7 +159,7 @@ std::unique_ptr<views::InkDropHighlight> MdTextButton::CreateInkDropHighlight() return std::make_unique<InkDropHighlight>( gfx::RectF(GetLocalBounds()).CenterPoint(), base::WrapUnique(new BorderShadowLayerDelegate( - shadows, GetLocalBounds(), fill_color, kInkDropSmallCornerRadius))); + shadows, GetLocalBounds(), fill_color, corner_radius_))); } void MdTextButton::SetEnabledTextColors(SkColor color) { @@ -179,7 +179,8 @@ void MdTextButton::UpdateStyleToIndicateDefaultStatus() { MdTextButton::MdTextButton(ButtonListener* listener, int button_context) : LabelButton(listener, base::string16(), button_context), - is_prominent_(false) { + is_prominent_(false), + corner_radius_(kInkDropSmallCornerRadius) { SetInkDropMode(InkDropMode::ON); set_has_ink_drop_action_on_click(true); SetHorizontalAlignment(gfx::ALIGN_CENTER); @@ -330,7 +331,7 @@ void MdTextButton::UpdateColors() { DCHECK_EQ(SK_AlphaOPAQUE, static_cast<int>(SkColorGetA(bg_color))); SetBackground( CreateBackgroundFromPainter(Painter::CreateRoundRectWith1PxBorderPainter( - bg_color, stroke_color, kInkDropSmallCornerRadius))); + bg_color, stroke_color, corner_radius_))); SchedulePaint(); } diff --git a/chromium/ui/views/controls/button/md_text_button.h b/chromium/ui/views/controls/button/md_text_button.h index 27ba3c0cb97..25f65ce5e87 100644 --- a/chromium/ui/views/controls/button/md_text_button.h +++ b/chromium/ui/views/controls/button/md_text_button.h @@ -34,6 +34,10 @@ class VIEWS_EXPORT MdTextButton : public LabelButton { // See |bg_color_override_|. void SetBgColorOverride(const base::Optional<SkColor>& color); + // Override the default corner radius of the round rect used for the + // background and ink drop effects. + void set_corner_radius(float radius) { corner_radius_ = radius; } + // View: void OnPaintBackground(gfx::Canvas* canvas) override; @@ -64,6 +68,8 @@ class VIEWS_EXPORT MdTextButton : public LabelButton { // When set, this provides the background color. base::Optional<SkColor> bg_color_override_; + float corner_radius_; + DISALLOW_COPY_AND_ASSIGN(MdTextButton); }; diff --git a/chromium/ui/views/controls/button/menu_button_unittest.cc b/chromium/ui/views/controls/button/menu_button_unittest.cc index 4a898face54..e12bae6e2d9 100644 --- a/chromium/ui/views/controls/button/menu_button_unittest.cc +++ b/chromium/ui/views/controls/button/menu_button_unittest.cc @@ -579,7 +579,8 @@ TEST_F(MenuButtonTest, DraggableMenuButtonDoesNotActivateOnDrag) { TestDragDropClient drag_client; SetDragDropClient(GetContext(), &drag_client); - button()->PrependPreTargetHandler(&drag_client); + button()->AddPreTargetHandler(&drag_client, + ui::EventTarget::Priority::kSystem); generator()->DragMouseBy(10, 0); EXPECT_EQ(nullptr, menu_button_listener.last_source()); diff --git a/chromium/ui/views/controls/button/toggle_button_unittest.cc b/chromium/ui/views/controls/button/toggle_button_unittest.cc index ac6191418b0..2315f7935b7 100644 --- a/chromium/ui/views/controls/button/toggle_button_unittest.cc +++ b/chromium/ui/views/controls/button/toggle_button_unittest.cc @@ -5,7 +5,6 @@ #include "ui/views/controls/button/toggle_button.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event_utils.h" diff --git a/chromium/ui/views/controls/focus_ring.cc b/chromium/ui/views/controls/focus_ring.cc index a5a44bac724..1cf8348a308 100644 --- a/chromium/ui/views/controls/focus_ring.cc +++ b/chromium/ui/views/controls/focus_ring.cc @@ -5,21 +5,14 @@ #include "ui/views/controls/focus_ring.h" #include "ui/gfx/canvas.h" -#include "ui/views/controls/focusable_border.h" namespace views { namespace { -// The stroke width of the focus border in dp. +// The default stroke width of the focus border in dp. constexpr float kFocusHaloThicknessDp = 2.f; -// The focus indicator should hug the normal border, when present (as in the -// case of text buttons). Since it's drawn outside the parent view, we have to -// increase the rounding slightly. -constexpr float kFocusHaloCornerRadiusDp = - FocusableBorder::kCornerRadiusDp + kFocusHaloThicknessDp / 2.f; - FocusRing* GetFocusRing(View* parent) { for (int i = 0; i < parent->child_count(); ++i) { if (parent->child_at(i)->GetClassName() == FocusRing::kViewClassName) @@ -33,20 +26,38 @@ FocusRing* GetFocusRing(View* parent) { const char FocusRing::kViewClassName[] = "FocusRing"; // static -views::View* FocusRing::Install(View* parent, - ui::NativeTheme::ColorId override_color_id) { +FocusRing* FocusRing::Install(View* parent, + SkColor color, + float corner_radius) { FocusRing* ring = GetFocusRing(parent); if (!ring) { - ring = new FocusRing(); + ring = new FocusRing(color, corner_radius); parent->AddChildView(ring); + } else { + ring->color_ = color; + ring->corner_radius_ = corner_radius; } - ring->override_color_id_ = override_color_id; ring->Layout(); ring->SchedulePaint(); return ring; } // static +FocusRing* FocusRing::Install(View* parent, + ui::NativeTheme::ColorId override_color_id) { + SkColor ring_color = parent->GetNativeTheme()->GetSystemColor( + override_color_id == ui::NativeTheme::kColorId_NumColors + ? ui::NativeTheme::kColorId_FocusedBorderColor + : override_color_id); + FocusRing* ring = Install(parent, ring_color); + DCHECK(ring); + ring->override_color_id_ = override_color_id; + if (!ring->view_observer_.IsObserving(parent)) + ring->view_observer_.Add(parent); + return ring; +} + +// static void FocusRing::Uninstall(View* parent) { delete GetFocusRing(parent); } @@ -75,21 +86,27 @@ void FocusRing::Layout() { void FocusRing::OnPaint(gfx::Canvas* canvas) { cc::PaintFlags flags; flags.setAntiAlias(true); - flags.setColor( - SkColorSetA(GetNativeTheme()->GetSystemColor( - override_color_id_ != ui::NativeTheme::kColorId_NumColors - ? override_color_id_ - : ui::NativeTheme::kColorId_FocusedBorderColor), - 0x66)); + flags.setColor(SkColorSetA(color_, 0x66)); flags.setStyle(cc::PaintFlags::kStroke_Style); flags.setStrokeWidth(kFocusHaloThicknessDp); gfx::RectF rect(GetLocalBounds()); rect.Inset(gfx::InsetsF(kFocusHaloThicknessDp / 2.f)); - canvas->DrawRoundRect(rect, kFocusHaloCornerRadiusDp, flags); + // The focus indicator should hug the normal border, when present (as in the + // case of text buttons). Since it's drawn outside the parent view, increase + // the rounding slightly by adding half the ring thickness. + canvas->DrawRoundRect(rect, corner_radius_ + kFocusHaloThicknessDp / 2.f, + flags); +} + +void FocusRing::OnViewNativeThemeChanged(View* observed_view) { + if (override_color_id_) { + color_ = observed_view->GetNativeTheme()->GetSystemColor( + override_color_id_.value()); + } } -FocusRing::FocusRing() - : override_color_id_(ui::NativeTheme::kColorId_NumColors) { +FocusRing::FocusRing(SkColor color, float corner_radius) + : color_(color), corner_radius_(corner_radius), view_observer_(this) { InitFocusRing(this); } diff --git a/chromium/ui/views/controls/focus_ring.h b/chromium/ui/views/controls/focus_ring.h index f6ecc2a9e78..df1f1b330ca 100644 --- a/chromium/ui/views/controls/focus_ring.h +++ b/chromium/ui/views/controls/focus_ring.h @@ -5,24 +5,37 @@ #ifndef UI_VIEWS_CONTROLS_FOCUS_RING_H_ #define UI_VIEWS_CONTROLS_FOCUS_RING_H_ +#include "base/scoped_observer.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/controls/focusable_border.h" #include "ui/views/view.h" +#include "ui/views/view_observer.h" +#include "ui/views/views_export.h" namespace views { // FocusRing is a View that is designed to act as an indicator of focus for its // parent. It is a stand-alone view that paints to a layer which extends beyond // the bounds of its parent view. -class FocusRing : public View { +class VIEWS_EXPORT FocusRing : public View, public ViewObserver { public: static const char kViewClassName[]; // Create a FocusRing and adds it to |parent|, or updates the one that already - // exists. |override_color_id| will be used in place of the default coloration + // exists with the given |color| and |corner_radius|. + // TODO(crbug.com/831926): Prefer using the below Install() method - this one + // should eventually be removed. + static FocusRing* Install( + View* parent, + SkColor color, + float corner_radius = FocusableBorder::kCornerRadiusDp); + + // Similar to FocusRing::Install(View, SkColor, float), but + // |override_color_id| will be used in place of the default coloration // when provided. - static View* Install(View* parent, - ui::NativeTheme::ColorId override_color_id = - ui::NativeTheme::kColorId_NumColors); + static FocusRing* Install(View* parent, + ui::NativeTheme::ColorId override_color_id = + ui::NativeTheme::kColorId_NumColors); // Removes the FocusRing from |parent|. static void Uninstall(View* parent); @@ -35,12 +48,18 @@ class FocusRing : public View { void Layout() override; void OnPaint(gfx::Canvas* canvas) override; + // ViewObserver: + void OnViewNativeThemeChanged(View* observed_view) override; + protected: - FocusRing(); + FocusRing(SkColor color, float corner_radius); ~FocusRing() override; private: - ui::NativeTheme::ColorId override_color_id_; + SkColor color_; + float corner_radius_; + base::Optional<ui::NativeTheme::ColorId> override_color_id_; + ScopedObserver<View, FocusRing> view_observer_; DISALLOW_COPY_AND_ASSIGN(FocusRing); }; diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc index badae559e96..059a83652f7 100644 --- a/chromium/ui/views/controls/image_view.cc +++ b/chromium/ui/views/controls/image_view.cc @@ -29,8 +29,7 @@ void* GetBitmapPixels(const gfx::ImageSkia& img, float image_scale) { const char ImageView::kViewClassName[] = "ImageView"; ImageView::ImageView() - : image_size_set_(false), - horiz_alignment_(CENTER), + : horiz_alignment_(CENTER), vert_alignment_(CENTER), last_paint_scale_(0.f), last_painted_bitmap_pixels_(NULL) {} @@ -63,7 +62,6 @@ const gfx::ImageSkia& ImageView::GetImage() const { } void ImageView::SetImageSize(const gfx::Size& image_size) { - image_size_set_ = true; image_size_ = image_size; PreferredSizeChanged(); } @@ -74,7 +72,7 @@ gfx::Rect ImageView::GetImageBounds() const { } void ImageView::ResetImageSize() { - image_size_set_ = false; + image_size_.reset(); } bool ImageView::IsImageEqual(const gfx::ImageSkia& img) const { @@ -89,7 +87,7 @@ bool ImageView::IsImageEqual(const gfx::ImageSkia& img) const { } gfx::Size ImageView::GetImageSize() const { - return image_size_set_ ? image_size_ : image_.size(); + return image_size_.value_or(image_.size()); } gfx::Point ImageView::ComputeImageOrigin(const gfx::Size& image_size) const { diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h index 751bee97277..178c50e5087 100644 --- a/chromium/ui/views/controls/image_view.h +++ b/chromium/ui/views/controls/image_view.h @@ -6,6 +6,7 @@ #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" @@ -98,11 +99,8 @@ class VIEWS_EXPORT ImageView : public View { // properties. gfx::Point ComputeImageOrigin(const gfx::Size& image_size) const; - // Whether the image size is set. - bool image_size_set_; - // The actual image size. - gfx::Size image_size_; + base::Optional<gfx::Size> image_size_; // The underlying image. gfx::ImageSkia image_; diff --git a/chromium/ui/views/controls/image_view_unittest.cc b/chromium/ui/views/controls/image_view_unittest.cc index 417d342d445..28cbb21460a 100644 --- a/chromium/ui/views/controls/image_view_unittest.cc +++ b/chromium/ui/views/controls/image_view_unittest.cc @@ -5,7 +5,6 @@ #include "ui/views/controls/image_view.h" #include "base/i18n/rtl.h" -#include "base/memory/ptr_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc index 6b6d5f02b8a..b5baab3cbb1 100644 --- a/chromium/ui/views/controls/label.cc +++ b/chromium/ui/views/controls/label.cc @@ -14,7 +14,6 @@ #include "base/i18n/rtl.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_node_data.h" @@ -665,13 +664,21 @@ void Label::ShowContextMenuForView(View* source, MENU_ANCHOR_TOPLEFT, source_type); } -bool Label::GetDecoratedWordAtPoint(const gfx::Point& point, - gfx::DecoratedText* decorated_word, - gfx::Point* baseline_point) { +bool Label::GetWordLookupDataAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) { + gfx::RenderText* render_text = GetRenderTextForSelectionController(); + return render_text ? render_text->GetWordLookupDataAtPoint( + point, decorated_word, baseline_point) + : false; +} + +bool Label::GetWordLookupDataFromSelection(gfx::DecoratedText* decorated_text, + gfx::Point* baseline_point) { gfx::RenderText* render_text = GetRenderTextForSelectionController(); return render_text - ? render_text->GetDecoratedWordAtPoint(point, decorated_word, - baseline_point) + ? render_text->GetLookupDataForRange( + render_text->selection(), decorated_text, baseline_point) : false; } diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h index 635025a8250..1d35afeda78 100644 --- a/chromium/ui/views/controls/label.h +++ b/chromium/ui/views/controls/label.h @@ -268,9 +268,12 @@ class VIEWS_EXPORT Label : public View, ui::MenuSourceType source_type) override; // WordLookupClient overrides: - bool GetDecoratedWordAtPoint(const gfx::Point& point, - gfx::DecoratedText* decorated_word, - gfx::Point* baseline_point) override; + bool GetWordLookupDataAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) override; + + bool GetWordLookupDataFromSelection(gfx::DecoratedText* decorated_text, + gfx::Point* baseline_point) override; // SelectionControllerDelegate overrides: gfx::RenderText* GetRenderTextForSelectionController() override; diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc index 54654acf866..ec25ede20df 100644 --- a/chromium/ui/views/controls/label_unittest.cc +++ b/chromium/ui/views/controls/label_unittest.cc @@ -8,7 +8,6 @@ #include "base/command_line.h" #include "base/i18n/rtl.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" @@ -190,10 +189,10 @@ class LabelSelectionTest : public LabelTest { return widget()->GetFocusManager()->GetFocusedView(); } - void PerformMousePress(const gfx::Point& point, int extra_flags = 0) { + void PerformMousePress(const gfx::Point& point) { ui::MouseEvent pressed_event = ui::MouseEvent( ui::ET_MOUSE_PRESSED, point, point, ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON | extra_flags); + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); label()->OnMousePressed(pressed_event); } @@ -204,8 +203,8 @@ class LabelSelectionTest : public LabelTest { label()->OnMouseReleased(released_event); } - void PerformClick(const gfx::Point& point, int extra_flags = 0) { - PerformMousePress(point, extra_flags); + void PerformClick(const gfx::Point& point) { + PerformMousePress(point); PerformMouseRelease(point); } @@ -1088,7 +1087,7 @@ TEST_F(LabelSelectionTest, DoubleTripleClick) { EXPECT_TRUE(GetSelectedText().empty()); // Double clicking should select the word under cursor. - PerformClick(GetCursorPoint(0), ui::EF_IS_DOUBLE_CLICK); + PerformClick(GetCursorPoint(0)); EXPECT_STR_EQ("Label", GetSelectedText()); // Triple clicking should select all the text. @@ -1102,7 +1101,7 @@ TEST_F(LabelSelectionTest, DoubleTripleClick) { // Clicking at another location should clear the selection. PerformClick(GetCursorPoint(8)); EXPECT_TRUE(GetSelectedText().empty()); - PerformClick(GetCursorPoint(8), ui::EF_IS_DOUBLE_CLICK); + PerformClick(GetCursorPoint(8)); EXPECT_STR_EQ("double", GetSelectedText()); } @@ -1298,7 +1297,7 @@ TEST_F(LabelSelectionTest, MouseDragWord) { ASSERT_TRUE(label()->SetSelectable(true)); PerformClick(GetCursorPoint(8)); - PerformMousePress(GetCursorPoint(8), ui::EF_IS_DOUBLE_CLICK); + PerformMousePress(GetCursorPoint(8)); EXPECT_STR_EQ("drag", GetSelectedText()); PerformMouseDragTo(GetCursorPoint(0)); diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac.h b/chromium/ui/views/controls/menu/menu_closure_animation_mac.h new file mode 100644 index 00000000000..5c3b6d69aff --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.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_VIEWS_CONTROLS_MENU_MENU_CLOSURE_ANIMATION_MAC_H_ +#define UI_VIEWS_CONTROLS_MENU_MENU_CLOSURE_ANIMATION_MAC_H_ + +#include "base/macros.h" +#include "base/timer/timer.h" +#include "ui/gfx/animation/animation.h" +#include "ui/gfx/animation/animation_delegate.h" +#include "ui/views/views_export.h" + +namespace views { + +class MenuItemView; + +// This class implements the Mac menu closure animation: +// 1) For 100ms, the selected item is drawn as unselected +// 2) Then, for another 100ms, the selected item is drawn as selected +// 3) Then, and the window fades over 250ms to transparency +// Note that this class is owned by the involved MenuController, so if the menu +// is destructed early for any reason, this class will be destructed also, which +// 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. +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); + ~MenuClosureAnimationMac() override; + + // Start the animation. + void Start(); + + // Returns the MenuItemView this animation targets. + MenuItemView* item() { return item_; } + + // 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. + static void DisableAnimationsForTesting(); + + private: + enum class AnimationStep { + kStart, + kUnselected, + kSelected, + kFading, + kFinish, + }; + + static constexpr AnimationStep NextStepFor(AnimationStep step); + + void AdvanceAnimation(); + + // gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; + void AnimationEnded(const gfx::Animation* animation) override; + void AnimationCanceled(const gfx::Animation* animation) override; + + base::OnceClosure callback_; + base::OneShotTimer timer_; + std::unique_ptr<gfx::Animation> fade_animation_; + MenuItemView* item_; + AnimationStep step_; + + DISALLOW_COPY_AND_ASSIGN(MenuClosureAnimationMac); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_MENU_MENU_CLOSURE_ANIMATION_MAC_H_ diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm b/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm new file mode 100644 index 00000000000..2bec6d31087 --- /dev/null +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm @@ -0,0 +1,100 @@ +// 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/menu/menu_closure_animation_mac.h" + +#import <Cocoa/Cocoa.h> + +#include "base/logging.h" +#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/widget/widget.h" + +namespace { +static bool g_disable_animations_for_testing = false; +} + +namespace views { + +MenuClosureAnimationMac::MenuClosureAnimationMac(MenuItemView* item, + base::OnceClosure callback) + : callback_(std::move(callback)), + item_(item), + step_(AnimationStep::kStart) {} + +MenuClosureAnimationMac::~MenuClosureAnimationMac() {} + +void MenuClosureAnimationMac::Start() { + DCHECK_EQ(step_, AnimationStep::kStart); + if (g_disable_animations_for_testing) { + // Even when disabling animations, simulate the fact that the eventual + // accept callback will happen after a runloop cycle by skipping to the end + // of the animation. + step_ = AnimationStep::kFading; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&MenuClosureAnimationMac::AdvanceAnimation, + base::Unretained(this))); + return; + } + AdvanceAnimation(); +} + +// static +constexpr MenuClosureAnimationMac::AnimationStep +MenuClosureAnimationMac::NextStepFor( + MenuClosureAnimationMac::AnimationStep step) { + switch (step) { + case AnimationStep::kStart: + return AnimationStep::kUnselected; + case AnimationStep::kUnselected: + return AnimationStep::kSelected; + case AnimationStep::kSelected: + return AnimationStep::kFading; + case AnimationStep::kFading: + return AnimationStep::kFinish; + case AnimationStep::kFinish: + return AnimationStep::kFinish; + } +} + +void MenuClosureAnimationMac::AdvanceAnimation() { + step_ = NextStepFor(step_); + if (step_ == AnimationStep::kUnselected || + step_ == AnimationStep::kSelected) { + item_->SetForcedVisualSelection(step_ == AnimationStep::kSelected); + timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(80), + base::BindRepeating(&MenuClosureAnimationMac::AdvanceAnimation, + base::Unretained(this))); + } else if (step_ == AnimationStep::kFading) { + auto fade = std::make_unique<gfx::LinearAnimation>(this); + fade->SetDuration(base::TimeDelta::FromMilliseconds(200)); + fade_animation_ = std::move(fade); + fade_animation_->Start(); + } else if (step_ == AnimationStep::kFinish) { + std::move(callback_).Run(); + } +} + +// static +void MenuClosureAnimationMac::DisableAnimationsForTesting() { + g_disable_animations_for_testing = true; +} + +void MenuClosureAnimationMac::AnimationProgressed( + const gfx::Animation* animation) { + NSWindow* window = item_->GetWidget()->GetNativeWindow(); + [window setAlphaValue:animation->CurrentValueBetween(1.0, 0.0)]; +} + +void MenuClosureAnimationMac::AnimationEnded(const gfx::Animation* animation) { + AdvanceAnimation(); +} + +void MenuClosureAnimationMac::AnimationCanceled( + const gfx::Animation* animation) { + NOTREACHED(); +} + +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_config.cc b/chromium/ui/views/controls/menu/menu_config.cc index 001179c5198..62fbbca1d1d 100644 --- a/chromium/ui/views/controls/menu/menu_config.cc +++ b/chromium/ui/views/controls/menu/menu_config.cc @@ -7,7 +7,6 @@ #include "base/macros.h" #include "ui/views/controls/menu/menu_image_util.h" #include "ui/views/round_rect_painter.h" - namespace views { MenuConfig::MenuConfig() @@ -19,10 +18,16 @@ MenuConfig::MenuConfig() item_bottom_margin(3), item_no_icon_top_margin(4), item_no_icon_bottom_margin(4), + fixed_text_item_height(0), + fixed_menu_width(0), item_left_margin(10), + touchable_item_left_margin(16), label_to_arrow_padding(10), arrow_to_edge_padding(5), icon_to_label_padding(10), + touchable_icon_to_label_padding(22), + touchable_icon_size(20), + touchable_icon_color(SkColorSetA(SK_ColorBLACK, 0xDE)), check_width(kMenuCheckSize), check_height(kMenuCheckSize), arrow_width(kSubmenuArrowSize), @@ -43,7 +48,13 @@ MenuConfig::MenuConfig() icons_in_label(false), check_selected_combobox_item(false), show_delay(400), - corner_radius(0) { + corner_radius(0), + touchable_corner_radius(8), + touchable_anchor_offset(8), + touchable_menu_height(36), + touchable_menu_width(256), + touchable_menu_shadow_elevation(12), + vertical_touchable_menu_item_padding(8) { Init(); } diff --git a/chromium/ui/views/controls/menu/menu_config.h b/chromium/ui/views/controls/menu/menu_config.h index a4ecc47839b..3fbb794646a 100644 --- a/chromium/ui/views/controls/menu/menu_config.h +++ b/chromium/ui/views/controls/menu/menu_config.h @@ -44,9 +44,19 @@ struct VIEWS_EXPORT MenuConfig { int item_no_icon_top_margin; int item_no_icon_bottom_margin; + // Fixed dimensions used for entire items. If these are nonzero, they override + // the vertical margin constants given above - the item's text and icon are + // vertically centered within these heights. + int fixed_text_item_height; + int fixed_container_item_height; + int fixed_menu_width; + // Margins between the left of the item and the icon. int item_left_margin; + // Margins between the left of the touchable item and the icon. + int touchable_item_left_margin; + // Padding between the label and submenu arrow. int label_to_arrow_padding; @@ -56,6 +66,15 @@ struct VIEWS_EXPORT MenuConfig { // Padding between the icon and label. int icon_to_label_padding; + // Padding between the icon and label for touchable menu items. + int touchable_icon_to_label_padding; + + // The icon size used for icons in touchable menu items. + int touchable_icon_size; + + // The color used for icons in touchable menu items. + SkColor touchable_icon_color; + // The space reserved for the check. The actual size of the image may be // different. int check_width; @@ -121,6 +140,24 @@ struct VIEWS_EXPORT MenuConfig { // Radius of the rounded corners of the menu border. Must be >= 0. int corner_radius; + // Radius of the rounded corners of the touchable menu border + int touchable_corner_radius; + + // Anchor offset for touchable menus created by a touch event. + int touchable_anchor_offset; + + // Height of child MenuItemViews for touchable menus. + int touchable_menu_height; + + // Width of touchable menus. + int touchable_menu_width; + + // Shadow elevation of touchable menus. + int touchable_menu_shadow_elevation; + + // Vertical padding for touchable menus. + int vertical_touchable_menu_item_padding; + private: // Configures a MenuConfig as appropriate for the current platform. void Init(); diff --git a/chromium/ui/views/controls/menu/menu_config_mac.mm b/chromium/ui/views/controls/menu/menu_config_mac.mm index fc66f894436..e950582f43f 100644 --- a/chromium/ui/views/controls/menu/menu_config_mac.mm +++ b/chromium/ui/views/controls/menu/menu_config_mac.mm @@ -7,37 +7,44 @@ #import <AppKit/AppKit.h> #include "base/mac/mac_util.h" - -namespace views { +#include "ui/base/material_design/material_design_controller.h" namespace { -// The height of the menu separator in pixels. -const int kMenuSeparatorHeight = 2; -const int kMenuSeparatorHeightMavericks = 1; +void InitMaterialMenuConfig(views::MenuConfig* config) { + // These config parameters are from https://crbug.com/829347 and the spec + // images linked from that bug. + config->menu_vertical_border_size = 8; + config->menu_horizontal_border_size = 0; + config->submenu_horizontal_inset = 0; + config->fixed_text_item_height = 32; + config->fixed_container_item_height = 48; + config->fixed_menu_width = 320; + config->item_left_margin = 8; + config->label_to_arrow_padding = 0; + config->arrow_to_edge_padding = 16; + config->icon_to_label_padding = 8; + config->check_width = 16; + config->check_height = 16; + config->arrow_width = 8; + config->separator_height = 17; + config->separator_thickness = 1; + config->label_to_minor_text_padding = 8; + config->align_arrow_and_shortcut = true; + config->use_outer_border = false; + config->icons_in_label = true; + config->corner_radius = 8; +} } // namespace +namespace views { + void MenuConfig::Init() { font_list = gfx::FontList(gfx::Font([NSFont menuFontOfSize:0.0])); - menu_vertical_border_size = 4; - item_top_margin = item_no_icon_top_margin = 1; - item_bottom_margin = item_no_icon_bottom_margin = 1; - item_left_margin = 2; - arrow_to_edge_padding = 13; - icon_to_label_padding = 4; - check_width = 19; - check_height = 11; - separator_height = 13; - separator_upper_height = 7; - separator_lower_height = 6; - separator_thickness = base::mac::IsOS10_9() ? kMenuSeparatorHeightMavericks - : kMenuSeparatorHeight; - align_arrow_and_shortcut = true; - icons_in_label = true; check_selected_combobox_item = true; - corner_radius = 5; - use_outer_border = false; + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) + InitMaterialMenuConfig(this); } } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index 4feb2cd4b2e..46713d7170a 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -4,10 +4,12 @@ #include "ui/views/controls/menu/menu_controller.h" +#include <algorithm> +#include <utility> + #include "base/i18n/case_conversion.h" #include "base/i18n/rtl.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "build/build_config.h" @@ -164,7 +166,7 @@ View* GetNextFocusableView(View* ancestor, View* start_at, bool forward) { } #if defined(OS_WIN) || defined(OS_CHROMEOS) -// Determines the correct cooridinates and window to repost |event| to, if it is +// Determines the correct coordinates and window to repost |event| to, if it is // a mouse or touch event. static void RepostEventImpl(const ui::LocatedEvent* event, const gfx::Point& screen_loc, @@ -1145,6 +1147,13 @@ void MenuController::TurnOffMenuSelectionHoldForTest() { menu_selection_hold_time_ms = -1; } +void MenuController::OnMenuItemDestroying(MenuItemView* menu_item) { +#if defined(OS_MACOSX) + if (menu_closure_animation_ && menu_closure_animation_->item() == menu_item) + menu_closure_animation_.reset(); +#endif +} + void MenuController::SetSelection(MenuItemView* menu_item, int selection_types) { size_t paths_differ_at = 0; @@ -1470,8 +1479,25 @@ 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, + base::BindOnce(&MenuController::ReallyAccept, base::Unretained(this), + base::Unretained(item), event_flags)); + menu_closure_animation_->Start(); +#else + ReallyAccept(item, event_flags); +#endif +} + +void MenuController::ReallyAccept(MenuItemView* item, int event_flags) { DCHECK(IsBlockingRun()); result_ = item; +#if defined(OS_MACOSX) + // Reset the closure animation since it's now finished - this also unblocks + // input events for the menu. + menu_closure_animation_.reset(); +#endif if (item && !menu_stack_.empty() && !item->GetDelegate()->ShouldCloseAllMenusOnExecute(item->GetCommand())) { SetExitType(EXIT_OUTERMOST); @@ -1926,8 +1952,8 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, pref.set_width(std::max(pref.width(), state_.initial_bounds.width())); // Don't let the menu go too wide. - pref.set_width(std::min(pref.width(), - item->GetDelegate()->GetMaxWidthForMenu(item))); + pref.set_width( + std::min(pref.width(), item->GetDelegate()->GetMaxWidthForMenu(item))); if (!state_.monitor_bounds.IsEmpty()) pref.set_width(std::min(pref.width(), state_.monitor_bounds.width())); @@ -2011,7 +2037,7 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, state_.monitor_bounds.right()) x -= pref.width(); // Move the menu to the left of the button. else - x += state_.initial_bounds.width(); // Move the menu right. + x += state_.initial_bounds.width(); // Move the menu right. } else { // The menu should end with the same x coordinate as the owning // button. @@ -2019,7 +2045,7 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, state_.initial_bounds.x() - pref.width()) x = state_.initial_bounds.right(); // Move right of the button. else - x = state_.initial_bounds.x() - pref.width(); // Move left. + x = state_.initial_bounds.x() - pref.width(); // Move left. } } item->set_actual_menu_position(orientation); @@ -2141,6 +2167,12 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item, pref.set_width(std::min(pref.width(), item->GetDelegate()->GetMaxWidthForMenu(item))); + const MenuConfig& menu_config = MenuConfig::instance(); + // Shadow insets are built into MenuScrollView's preferred size so it must be + // compensated for when determining the bounds of touchable menus. + gfx::Insets shadow_insets = BubbleBorder::GetBorderAndShadowInsets( + menu_config.touchable_menu_shadow_elevation); + int x, y; if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE || state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) { @@ -2158,6 +2190,34 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item, } submenu->GetScrollViewContainer()->SetBubbleArrowOffset( pref.width() / 2 - x + x_old); + } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE) { + // Align the left edges of the menu and anchor, and the bottom of the menu + // with the top of the anchor. + x = owner_bounds.origin().x() - shadow_insets.left(); + y = owner_bounds.origin().y() - pref.height() + shadow_insets.bottom() - + menu_config.touchable_anchor_offset; + // Align the right of the container with the right of the app icon. + if (x + pref.width() > state_.monitor_bounds.width()) + x = owner_bounds.right() - pref.width() + shadow_insets.right(); + // Align the top of the menu with the bottom of the anchor. + if (y < 0) { + y = owner_bounds.bottom() - shadow_insets.top() + + menu_config.touchable_anchor_offset; + } + } else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT) { + // Align the right of the menu with the left of the anchor, and the top of + // the menu with the top of the anchor. + x = owner_bounds.origin().x() - pref.width() + shadow_insets.right() - + menu_config.touchable_anchor_offset; + y = owner_bounds.origin().y() - shadow_insets.top(); + // Align the left of the menu with the right of the anchor. + if (x < 0) { + x = owner_bounds.right() + shadow_insets.left() + + menu_config.touchable_anchor_offset; + } + // Align the bottom of the menu to the bottom of the anchor. + if (y + pref.height() > state_.monitor_bounds.height()) + y = owner_bounds.bottom() - pref.height() + shadow_insets.bottom(); } else { if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT) x = owner_bounds.right() - kBubbleTipSizeLeftRight; @@ -2316,7 +2376,7 @@ MenuController::SelectByCharDetails MenuController::FindChildForMnemonic( void MenuController::AcceptOrSelect(MenuItemView* parent, const SelectByCharDetails& details) { // This should only be invoked if there is a match. - DCHECK(details.first_match != -1); + DCHECK_NE(details.first_match, -1); DCHECK(parent->HasSubmenu()); SubmenuView* submenu = parent->GetSubmenu(); DCHECK(submenu); @@ -2710,4 +2770,12 @@ void MenuController::SetHotTrackedButton(Button* hot_button) { } } +bool MenuController::CanProcessInputEvents() const { +#if defined(OS_MACOSX) + return !menu_closure_animation_; +#else + return true; +#endif +} + } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h index 1a056eb2404..b2080448720 100644 --- a/chromium/ui/views/controls/menu/menu_controller.h +++ b/chromium/ui/views/controls/menu/menu_controller.h @@ -25,6 +25,10 @@ #include "ui/views/controls/menu/menu_delegate.h" #include "ui/views/widget/widget_observer.h" +#if defined(OS_MACOSX) +#include "ui/views/controls/menu/menu_closure_animation_mac.h" +#endif + namespace ui { class OSExchangeData; } @@ -138,6 +142,7 @@ class VIEWS_EXPORT MenuController base::TimeTicks closing_event_time() const { return closing_event_time_; } void set_is_combobox(bool is_combobox) { is_combobox_ = is_combobox; } + bool is_combobox() const { return is_combobox_; } // Various events, forwarded from the submenu. // @@ -198,6 +203,18 @@ class VIEWS_EXPORT MenuController // Only used for testing. static void TurnOffMenuSelectionHoldForTest(); + void set_use_touchable_layout(bool use_touchable_layout) { + use_touchable_layout_ = use_touchable_layout; + } + bool use_touchable_layout() const { return use_touchable_layout_; } + + // Notifies |this| that |menu_item| is being destroyed. + void OnMenuItemDestroying(MenuItemView* menu_item); + + // Returns whether this menu can handle input events right now. This method + // can return false while running animations. + bool CanProcessInputEvents() const; + private: friend class internal::MenuRunnerImpl; friend class test::MenuControllerTest; @@ -338,6 +355,7 @@ class VIEWS_EXPORT MenuController // Invoked when the user accepts the selected item. This is only used // when blocking. This schedules the loop to quit. void Accept(MenuItemView* item, int event_flags); + void ReallyAccept(MenuItemView* item, int event_flags); bool ShowSiblingMenu(SubmenuView* source, const gfx::Point& mouse_location); @@ -681,6 +699,9 @@ class VIEWS_EXPORT MenuController // Set to true if the menu item was selected by touch. bool item_selected_by_touch_ = false; + // Whether to use the touchable layout. + bool use_touchable_layout_ = false; + // During mouse event handling, this is the RootView to forward mouse events // to. We need this, because if we forward one event to it (e.g., mouse // pressed), subsequent events (like dragging) should also go to it, even if @@ -690,6 +711,10 @@ class VIEWS_EXPORT MenuController // A mask of the EventFlags for the mouse buttons currently pressed. int current_mouse_pressed_state_ = 0; +#if defined(OS_MACOSX) + std::unique_ptr<MenuClosureAnimationMac> menu_closure_animation_; +#endif + #if defined(USE_AURA) std::unique_ptr<MenuPreTargetHandler> menu_pre_target_handler_; #endif diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc index d8629d3a802..22fa8335af1 100644 --- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -464,6 +464,7 @@ class MenuControllerTest : public ViewsTestBase { void Accept(MenuItemView* item, int event_flags) { menu_controller_->Accept(item, event_flags); + views::test::WaitForMenuClosureAnimation(); } // Causes the |menu_controller_| to begin dragging. Use TestDragDropClient to @@ -943,6 +944,8 @@ TEST_F(MenuControllerTest, ChildButtonHotTrackedWhenNested) { // Tests that a menu opened asynchronously, will notify its // MenuControllerDelegate when Accept is called. TEST_F(MenuControllerTest, AsynchronousAccept) { + views::test::DisableMenuClosureAnimations(); + MenuController* controller = menu_controller(); controller->Run(owner(), nullptr, menu_item(), gfx::Rect(), MENU_ANCHOR_TOPLEFT, false, false); @@ -1254,6 +1257,7 @@ TEST_F(MenuControllerTest, AsynchronousRepostEventDeletesController) { // Tests that having the MenuController deleted during OnGestureEvent does not // cause a crash. ASAN bots should not detect use-after-free in MenuController. TEST_F(MenuControllerTest, AsynchronousGestureDeletesController) { + views::test::DisableMenuClosureAnimations(); MenuController* controller = menu_controller(); std::unique_ptr<TestMenuControllerDelegate> nested_delegate( new TestMenuControllerDelegate()); @@ -1277,6 +1281,7 @@ TEST_F(MenuControllerTest, AsynchronousGestureDeletesController) { // gesture event. The remainder of this test, and TearDown should not crash. DestroyMenuControllerOnMenuClosed(nested_delegate.get()); controller->OnGestureEvent(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 bcdd4af65ed..f1ff95b4748 100644 --- a/chromium/ui/views/controls/menu/menu_host.cc +++ b/chromium/ui/views/controls/menu/menu_host.cc @@ -116,7 +116,9 @@ void MenuHost::InitMenuHost(Widget* parent, const MenuController* menu_controller = submenu_->GetMenuItem()->GetMenuController(); const MenuConfig& menu_config = MenuConfig::instance(); - bool rounded_border = menu_controller && menu_config.corner_radius > 0; + bool rounded_border = + menu_controller && (menu_controller->use_touchable_layout() || + (menu_config.corner_radius > 0)); bool bubble_border = submenu_->GetScrollViewContainer() && submenu_->GetScrollViewContainer()->HasBubbleBorder(); params.shadow_type = bubble_border ? Widget::InitParams::SHADOW_TYPE_NONE diff --git a/chromium/ui/views/controls/menu/menu_host_root_view.cc b/chromium/ui/views/controls/menu/menu_host_root_view.cc index 362971fa8a4..bb72234feec 100644 --- a/chromium/ui/views/controls/menu/menu_host_root_view.cc +++ b/chromium/ui/views/controls/menu/menu_host_root_view.cc @@ -14,40 +14,41 @@ MenuHostRootView::MenuHostRootView(Widget* widget, SubmenuView* submenu) : internal::RootView(widget), submenu_(submenu) {} bool MenuHostRootView::OnMousePressed(const ui::MouseEvent& event) { - return GetMenuController() && - GetMenuController()->OnMousePressed(submenu_, event); + return GetMenuControllerForInputEvents() && + GetMenuControllerForInputEvents()->OnMousePressed(submenu_, event); } bool MenuHostRootView::OnMouseDragged(const ui::MouseEvent& event) { - return GetMenuController() && - GetMenuController()->OnMouseDragged(submenu_, event); + return GetMenuControllerForInputEvents() && + GetMenuControllerForInputEvents()->OnMouseDragged(submenu_, event); } void MenuHostRootView::OnMouseReleased(const ui::MouseEvent& event) { - if (GetMenuController()) - GetMenuController()->OnMouseReleased(submenu_, event); + if (GetMenuControllerForInputEvents()) + GetMenuControllerForInputEvents()->OnMouseReleased(submenu_, event); } void MenuHostRootView::OnMouseMoved(const ui::MouseEvent& event) { - if (GetMenuController()) - GetMenuController()->OnMouseMoved(submenu_, event); + if (GetMenuControllerForInputEvents()) + GetMenuControllerForInputEvents()->OnMouseMoved(submenu_, event); } bool MenuHostRootView::OnMouseWheel(const ui::MouseWheelEvent& event) { - return GetMenuController() && - GetMenuController()->OnMouseWheel(submenu_, event); + return GetMenuControllerForInputEvents() && + GetMenuControllerForInputEvents()->OnMouseWheel(submenu_, event); } View* MenuHostRootView::GetTooltipHandlerForPoint(const gfx::Point& point) { - return GetMenuController() - ? GetMenuController()->GetTooltipHandlerForPoint(submenu_, point) + return GetMenuControllerForInputEvents() + ? GetMenuControllerForInputEvents()->GetTooltipHandlerForPoint( + submenu_, point) : nullptr; } void MenuHostRootView::ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) { - if (GetMenuController()) - GetMenuController()->ViewHierarchyChanged(submenu_, details); + if (GetMenuControllerForInputEvents()) + GetMenuControllerForInputEvents()->ViewHierarchyChanged(submenu_, details); RootView::ViewHierarchyChanged(details); } @@ -87,4 +88,10 @@ MenuController* MenuHostRootView::GetMenuController() { return submenu_ ? submenu_->GetMenuItem()->GetMenuController() : NULL; } +MenuController* MenuHostRootView::GetMenuControllerForInputEvents() { + return GetMenuController() && GetMenuController()->CanProcessInputEvents() + ? GetMenuController() + : nullptr; +} + } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_host_root_view.h b/chromium/ui/views/controls/menu/menu_host_root_view.h index 62437f8b1e7..401e6e98db0 100644 --- a/chromium/ui/views/controls/menu/menu_host_root_view.h +++ b/chromium/ui/views/controls/menu/menu_host_root_view.h @@ -47,6 +47,7 @@ class MenuHostRootView : public internal::RootView { // Returns the MenuController for this MenuHostRootView. MenuController* GetMenuController(); + MenuController* GetMenuControllerForInputEvents(); // The SubmenuView we contain. SubmenuView* submenu_; diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 4cf7d61f7ef..257f71e6eaa 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -199,7 +199,9 @@ bool MenuItemView::IsBubble(MenuAnchorPosition anchor) { return anchor == MENU_ANCHOR_BUBBLE_LEFT || anchor == MENU_ANCHOR_BUBBLE_RIGHT || anchor == MENU_ANCHOR_BUBBLE_ABOVE || - anchor == MENU_ANCHOR_BUBBLE_BELOW; + anchor == MENU_ANCHOR_BUBBLE_BELOW || + anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE || + anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT; } // static @@ -591,6 +593,9 @@ void MenuItemView::Layout() { (icon_area_width_ - size.width()) / 2; if (config.icons_in_label || type_ == CHECKBOX || type_ == RADIO) x = label_start_; + if (GetMenuController() && GetMenuController()->use_touchable_layout()) + x = config.touchable_item_left_margin; + int y = (height() + GetTopMargin() - GetBottomMargin() - size.height()) / 2; icon_view_->SetPosition(gfx::Point(x, y)); @@ -598,13 +603,15 @@ void MenuItemView::Layout() { if (radio_check_image_view_) { int x = config.item_left_margin + left_icon_margin_; + if (GetMenuController() && GetMenuController()->use_touchable_layout()) + x = config.touchable_item_left_margin; int y = (height() + GetTopMargin() - GetBottomMargin() - kMenuCheckSize) / 2; radio_check_image_view_->SetBounds(x, y, kMenuCheckSize, kMenuCheckSize); } if (submenu_arrow_image_view_) { - int x = this->width() - config.arrow_width - config.arrow_to_edge_padding; + int x = width() - config.arrow_width - config.arrow_to_edge_padding; int y = (height() + GetTopMargin() - GetBottomMargin() - kSubmenuArrowSize) / 2; @@ -621,6 +628,11 @@ void MenuItemView::SetMargins(int top_margin, int bottom_margin) { invalidate_dimensions(); } +void MenuItemView::SetForcedVisualSelection(bool selected) { + forced_visual_selection_ = selected; + SchedulePaint(); +} + MenuItemView::MenuItemView(MenuItemView* parent, int command, MenuItemView::Type type) @@ -647,6 +659,8 @@ MenuItemView::MenuItemView(MenuItemView* parent, } MenuItemView::~MenuItemView() { + if (GetMenuController()) + GetMenuController()->OnMenuItemDestroying(this); delete submenu_; for (auto* item : removed_items_) delete item; @@ -676,6 +690,9 @@ void MenuItemView::UpdateMenuPartSizes() { padding = (has_icons_ || HasChecksOrRadioButtons()) ? config.icon_to_label_padding : 0; } + if (GetMenuController() && GetMenuController()->use_touchable_layout()) + padding = config.touchable_icon_to_label_padding; + label_start_ += padding; EmptyMenuMenuItem menu_item(this); @@ -815,6 +832,8 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { (mode == PB_NORMAL && IsSelected() && parent_menu_item_->GetSubmenu()->GetShowSelection(this) && (NonIconChildViewsCount() == 0)); + if (forced_visual_selection_.has_value()) + render_selection = *forced_visual_selection_; MenuDelegate *delegate = GetDelegate(); bool emphasized = @@ -841,6 +860,8 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { // Calculate some colors. SkColor fg_color = GetTextColor(false, render_selection, emphasized); SkColor icon_color = color_utils::DeriveDefaultIconColor(fg_color); + if (GetMenuController() && GetMenuController()->use_touchable_layout()) + icon_color = config.touchable_icon_color; // Render the check. if (type_ == CHECKBOX && delegate->IsItemChecked(GetCommand())) { @@ -870,12 +891,10 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { flags); if (!subtitle_.empty()) { canvas->DrawStringRectWithFlags( - subtitle_, - font_list, + subtitle_, font_list, GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_MenuItemSubtitleColor), - text_bounds + gfx::Vector2d(0, font_list.GetHeight()), - flags); + ui::NativeTheme::kColorId_MenuItemMinorTextColor), + text_bounds + gfx::Vector2d(0, font_list.GetHeight()), flags); } PaintMinorIconAndText(canvas, @@ -934,7 +953,7 @@ SkColor MenuItemView::GetTextColor(bool minor, bool render_selection, bool emphasized) const { ui::NativeTheme::ColorId color_id = - minor ? ui::NativeTheme::kColorId_MenuItemSubtitleColor + minor ? ui::NativeTheme::kColorId_MenuItemMinorTextColor : ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor; if (enabled()) { if (render_selection) @@ -1012,6 +1031,34 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { MenuItemDimensions dimensions; // Get the container height. dimensions.children_width = child_size.width(); + const MenuConfig& menu_config = MenuConfig::instance(); + + if (GetMenuController() && GetMenuController()->use_touchable_layout()) { + // MenuItemViews that use the touchable layout have fixed height and width. + dimensions.height = menu_config.touchable_menu_height; + dimensions.standard_width = menu_config.touchable_menu_width; + return dimensions; + } + + const gfx::FontList& font_list = GetFontList(); + base::string16 minor_text = GetMinorText(); + if (menu_config.fixed_text_item_height && + menu_config.fixed_container_item_height && menu_config.fixed_menu_width && + GetMenuController() && !GetMenuController()->is_combobox()) { + bool has_children = NonIconChildViewsCount() > 0; + dimensions.height = has_children ? menu_config.fixed_container_item_height + : menu_config.fixed_text_item_height; + dimensions.children_width = 0; + dimensions.minor_text_width = + minor_text.empty() ? 0 : gfx::GetStringWidth(minor_text, font_list); + int leave_for_minor = dimensions.minor_text_width + ? dimensions.minor_text_width + + menu_config.label_to_minor_text_padding + : 0; + dimensions.standard_width = menu_config.fixed_menu_width - leave_for_minor; + return dimensions; + } + dimensions.height = child_size.height(); // Adjust item content height if menu has both items with and without icons. // This way all menu items will have the same height. @@ -1025,9 +1072,6 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { if (IsContainer()) return dimensions; - // Determine the length of the label text. - const gfx::FontList& font_list = GetFontList(); - // Get Icon margin overrides for this particular item. const MenuDelegate* delegate = GetDelegate(); if (delegate) { @@ -1041,6 +1085,7 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { } int label_start = GetLabelStartForThisItem(); + // Determine the length of the label text. int string_width = gfx::GetStringWidth(title_, font_list); if (!subtitle_.empty()) { string_width = std::max(string_width, @@ -1050,7 +1095,6 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { dimensions.standard_width = string_width + label_start + item_right_margin_; // Determine the length of the right-side text. - base::string16 minor_text = GetMinorText(); dimensions.minor_text_width = minor_text.empty() ? 0 : gfx::GetStringWidth(minor_text, font_list); diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h index ab487377b9e..be24fb73cf7 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.h +++ b/chromium/ui/views/controls/menu/menu_item_view.h @@ -12,6 +12,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "base/strings/string16.h" #include "build/build_config.h" #include "ui/base/models/menu_separator_types.h" @@ -337,6 +338,11 @@ class VIEWS_EXPORT MenuItemView : public View { use_right_margin_ = use_right_margin; } + // Controls whether this menu has a forced visual selection state. This is + // used when animating item acceptance on Mac. Note that once this is set + // there's no way to unset it for this MenuItemView! + void SetForcedVisualSelection(bool selected); + protected: // Creates a MenuItemView. This is used by the various AddXXX methods. MenuItemView(MenuItemView* parent, int command, Type type); @@ -556,6 +562,9 @@ class VIEWS_EXPORT MenuItemView : public View { // The submenu indicator arrow icon in case the menu item has a Submenu. ImageView* submenu_arrow_image_view_; + // The forced visual selection state of this item, if any. + base::Optional<bool> forced_visual_selection_; + DISALLOW_COPY_AND_ASSIGN(MenuItemView); }; diff --git a/chromium/ui/views/controls/menu/menu_model_adapter.cc b/chromium/ui/views/controls/menu/menu_model_adapter.cc index 2a540157090..e1a2339bddb 100644 --- a/chromium/ui/views/controls/menu/menu_model_adapter.cc +++ b/chromium/ui/views/controls/menu/menu_model_adapter.cc @@ -61,59 +61,42 @@ MenuItemView* MenuModelAdapter::AddMenuItemFromModelAt(ui::MenuModel* model, MenuItemView* menu, int menu_index, int item_id) { - gfx::Image icon; - model->GetIconAt(model_index, &icon); - base::string16 label, sublabel, minor_text; - const gfx::VectorIcon* minor_icon = nullptr; - ui::MenuSeparatorType separator_style = ui::NORMAL_SEPARATOR; - MenuItemView::Type type; + base::Optional<MenuItemView::Type> type; ui::MenuModel::ItemType menu_type = model->GetTypeAt(model_index); - switch (menu_type) { case ui::MenuModel::TYPE_COMMAND: case ui::MenuModel::TYPE_BUTTON_ITEM: type = MenuItemView::NORMAL; - label = model->GetLabelAt(model_index); - sublabel = model->GetSublabelAt(model_index); - minor_text = model->GetMinorTextAt(model_index); - minor_icon = model->GetMinorIconAt(model_index); break; case ui::MenuModel::TYPE_CHECK: type = MenuItemView::CHECKBOX; - label = model->GetLabelAt(model_index); - sublabel = model->GetSublabelAt(model_index); - minor_text = model->GetMinorTextAt(model_index); - minor_icon = model->GetMinorIconAt(model_index); break; case ui::MenuModel::TYPE_RADIO: type = MenuItemView::RADIO; - label = model->GetLabelAt(model_index); - sublabel = model->GetSublabelAt(model_index); - minor_text = model->GetMinorTextAt(model_index); - minor_icon = model->GetMinorIconAt(model_index); break; case ui::MenuModel::TYPE_SEPARATOR: - icon = gfx::Image(); type = MenuItemView::SEPARATOR; - separator_style = model->GetSeparatorTypeAt(model_index); break; case ui::MenuModel::TYPE_SUBMENU: type = MenuItemView::SUBMENU; - label = model->GetLabelAt(model_index); - sublabel = model->GetSublabelAt(model_index); - minor_text = model->GetMinorTextAt(model_index); - minor_icon = model->GetMinorIconAt(model_index); - break; - default: - NOTREACHED(); - type = MenuItemView::NORMAL; break; } + if (*type == MenuItemView::SEPARATOR) { + return menu->AddMenuItemAt(menu_index, item_id, base::string16(), + base::string16(), base::string16(), nullptr, + gfx::ImageSkia(), *type, + model->GetSeparatorTypeAt(model_index)); + } + + gfx::Image icon; + model->GetIconAt(model_index, &icon); return menu->AddMenuItemAt( - menu_index, item_id, label, sublabel, minor_text, minor_icon, - icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(), type, - separator_style); + menu_index, item_id, model->GetLabelAt(model_index), + model->GetSublabelAt(model_index), model->GetMinorTextAt(model_index), + model->GetMinorIconAt(model_index), + icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(), *type, + ui::NORMAL_SEPARATOR); } // Static. diff --git a/chromium/ui/views/controls/menu/menu_pre_target_handler.cc b/chromium/ui/views/controls/menu/menu_pre_target_handler.cc index 68c3c94fb9e..9f9dd9448a4 100644 --- a/chromium/ui/views/controls/menu/menu_pre_target_handler.cc +++ b/chromium/ui/views/controls/menu/menu_pre_target_handler.cc @@ -23,7 +23,8 @@ aura::Window* GetOwnerRootWindow(views::Widget* owner) { MenuPreTargetHandler::MenuPreTargetHandler(MenuController* controller, Widget* owner) : controller_(controller), root_(GetOwnerRootWindow(owner)) { - aura::Env::GetInstanceDontCreate()->PrependPreTargetHandler(this); + aura::Env::GetInstanceDontCreate()->AddPreTargetHandler( + this, ui::EventTarget::Priority::kSystem); if (root_) { wm::GetActivationClient(root_)->AddObserver(this); root_->AddObserver(this); diff --git a/chromium/ui/views/controls/menu/menu_runner.h b/chromium/ui/views/controls/menu/menu_runner.h index 56a53d9dbd1..88b4bb43448 100644 --- a/chromium/ui/views/controls/menu/menu_runner.h +++ b/chromium/ui/views/controls/menu/menu_runner.h @@ -95,6 +95,9 @@ class VIEWS_EXPORT MenuRunner { // shelf uses the flag to continue dragging an item without lifting the // finger after the context menu of the item is opened. SEND_GESTURE_EVENTS_TO_OWNER = 1 << 7, + + // Whether to use the touchable layout for this context menu. + USE_TOUCHABLE_LAYOUT = 1 << 8, }; // Creates a new MenuRunner, which may use a native menu if available. diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.cc b/chromium/ui/views/controls/menu/menu_runner_impl.cc index 0004fe4a546..cd9ac6df3ff 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl.cc +++ b/chromium/ui/views/controls/menu/menu_runner_impl.cc @@ -128,6 +128,8 @@ void MenuRunnerImpl::RunMenuAt(Widget* parent, controller->set_is_combobox((run_types & MenuRunner::COMBOBOX) != 0); controller->set_send_gesture_events_to_owner( (run_types & MenuRunner::SEND_GESTURE_EVENTS_TO_OWNER) != 0); + controller->set_use_touchable_layout( + (run_types & MenuRunner::USE_TOUCHABLE_LAYOUT) != 0); controller_ = controller->AsWeakPtr(); menu_->set_controller(controller_.get()); menu_->PrepareForRun(owns_controller_, has_mnemonics, diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm index 274559a2daf..e68a76f83ed 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm +++ b/chromium/ui/views/controls/menu/menu_runner_impl_cocoa.mm @@ -20,10 +20,6 @@ namespace views { namespace internal { namespace { -// The menu run types that should show a native NSMenu rather than a toolkit- -// views menu. Only supported when the menu is backed by a ui::MenuModel. -const int kNativeRunTypes = MenuRunner::CONTEXT_MENU | MenuRunner::COMBOBOX; - const CGFloat kNativeCheckmarkWidth = 18; const CGFloat kNativeMenuItemHeight = 18; @@ -43,7 +39,9 @@ NSMenuItem* FirstCheckedItem(MenuControllerCocoa* menu_controller) { base::scoped_nsobject<NSView> CreateMenuAnchorView( NSWindow* window, const gfx::Rect& screen_bounds, - NSMenuItem* checked_item) { + NSMenuItem* checked_item, + CGFloat actual_menu_width, + MenuAnchorPosition position) { NSRect rect = gfx::ScreenRectToNSRect(screen_bounds); rect = [window convertRectFromScreen:rect]; rect = [[window contentView] convertRect:rect fromView:nil]; @@ -73,6 +71,13 @@ base::scoped_nsobject<NSView> CreateMenuAnchorView( } } + // When the actual menu width is larger than the anchor, right alignment + // should be respected. + if (actual_menu_width > rect.size.width && + position == views::MENU_ANCHOR_TOPRIGHT && !base::i18n::IsRTL()) { + int width_diff = actual_menu_width - rect.size.width; + rect.origin.x -= width_diff; + } // A plain NSView will anchor below rather than "over", so use an NSButton. base::scoped_nsobject<NSView> anchor_view( [[NSButton alloc] initWithFrame:rect]); @@ -118,11 +123,6 @@ MenuRunnerImplInterface* MenuRunnerImplInterface::Create( ui::MenuModel* menu_model, int32_t run_types, const base::Closure& on_menu_closed_callback) { - if ((run_types & kNativeRunTypes) != 0 && - (run_types & MenuRunner::IS_NESTED) == 0) { - return new MenuRunnerImplCocoa(menu_model, on_menu_closed_callback); - } - return new MenuRunnerImplAdapter(menu_model, on_menu_closed_callback); } @@ -159,7 +159,6 @@ void MenuRunnerImplCocoa::RunMenuAt(Widget* parent, const gfx::Rect& bounds, MenuAnchorPosition anchor, int32_t run_types) { - DCHECK(run_types & kNativeRunTypes); DCHECK(!IsRunning()); DCHECK(parent); closing_event_time_ = base::TimeTicks(); @@ -175,13 +174,14 @@ void MenuRunnerImplCocoa::RunMenuAt(Widget* parent, forView:parent->GetNativeView()]; } else if (run_types & MenuRunner::COMBOBOX) { NSMenuItem* checked_item = FirstCheckedItem(menu_controller_); - base::scoped_nsobject<NSView> anchor_view( - CreateMenuAnchorView(window, bounds, checked_item)); NSMenu* menu = [menu_controller_ menu]; + base::scoped_nsobject<NSView> anchor_view(CreateMenuAnchorView( + window, bounds, checked_item, menu.size.width, anchor)); [menu setMinimumWidth:bounds.width() + kNativeCheckmarkWidth]; [menu popUpMenuPositioningItem:checked_item atLocation:NSZeroPoint inView:anchor_view]; + [anchor_view removeFromSuperview]; } else { NOTREACHED(); diff --git a/chromium/ui/views/controls/menu/menu_runner_unittest.cc b/chromium/ui/views/controls/menu/menu_runner_unittest.cc index 1ad933bb7d0..79875e46f84 100644 --- a/chromium/ui/views/controls/menu/menu_runner_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_runner_unittest.cc @@ -9,7 +9,6 @@ #include <memory> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/ui_base_types.h" #include "ui/events/test/event_generator.h" @@ -163,6 +162,7 @@ TEST_F(MenuRunnerTest, LatinMnemonic) { if (IsMus()) return; + views::test::DisableMenuClosureAnimations(); InitMenuRunner(0); MenuRunner* runner = menu_runner(); runner->RunMenuAt(owner(), nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT, @@ -171,6 +171,7 @@ TEST_F(MenuRunnerTest, LatinMnemonic) { ui::test::EventGenerator generator(GetContext(), owner()->GetNativeWindow()); generator.PressKey(ui::VKEY_O, 0); + views::test::WaitForMenuClosureAnimation(); EXPECT_FALSE(runner->IsRunning()); TestMenuDelegate* delegate = menu_delegate(); EXPECT_EQ(1, delegate->execute_command_id()); @@ -186,6 +187,7 @@ TEST_F(MenuRunnerTest, NonLatinMnemonic) { if (IsMus()) return; + views::test::DisableMenuClosureAnimations(); InitMenuRunner(0); MenuRunner* runner = menu_runner(); runner->RunMenuAt(owner(), nullptr, gfx::Rect(), MENU_ANCHOR_TOPLEFT, @@ -195,6 +197,7 @@ TEST_F(MenuRunnerTest, NonLatinMnemonic) { ui::test::EventGenerator generator(GetContext(), owner()->GetNativeWindow()); ui::KeyEvent key_press(0x062f, ui::VKEY_N, 0); generator.Dispatch(&key_press); + views::test::WaitForMenuClosureAnimation(); EXPECT_FALSE(runner->IsRunning()); TestMenuDelegate* delegate = menu_delegate(); EXPECT_EQ(2, delegate->execute_command_id()); diff --git a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc index 01069fe8680..2e2e6b2aa26 100644 --- a/chromium/ui/views/controls/menu/menu_scroll_view_container.cc +++ b/chromium/ui/views/controls/menu/menu_scroll_view_container.cc @@ -5,7 +5,6 @@ #include "ui/views/controls/menu/menu_scroll_view_container.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "cc/paint/paint_flags.h" #include "third_party/skia/include/core/SkPath.h" #include "ui/accessibility/ax_node_data.h" @@ -25,7 +24,7 @@ namespace views { namespace { -static const int kBorderPaddingDueToRoundedCorners = 1; +static constexpr int kBorderPaddingDueToRoundedCorners = 1; // MenuScrollButton ------------------------------------------------------------ @@ -251,7 +250,10 @@ void MenuScrollViewContainer::OnPaintBackground(gfx::Canvas* canvas) { gfx::Rect bounds(0, 0, width(), height()); NativeTheme::ExtraParams extra; const MenuConfig& menu_config = MenuConfig::instance(); - extra.menu_background.corner_radius = menu_config.corner_radius; + extra.menu_background.corner_radius = + content_view_->GetMenuItem()->GetMenuController()->use_touchable_layout() + ? menu_config.touchable_corner_radius + : menu_config.corner_radius; GetNativeTheme()->Paint(canvas->sk_canvas(), NativeTheme::kMenuPopupBackground, NativeTheme::kNormal, bounds, extra); } @@ -300,9 +302,19 @@ void MenuScrollViewContainer::CreateDefaultBorder() { } void MenuScrollViewContainer::CreateBubbleBorder() { - bubble_border_ = new BubbleBorder(arrow_, - BubbleBorder::SMALL_SHADOW, - SK_ColorWHITE); + bubble_border_ = + new BubbleBorder(arrow_, BubbleBorder::SMALL_SHADOW, SK_ColorWHITE); + if (content_view_->GetMenuItem() + ->GetMenuController() + ->use_touchable_layout()) { + const MenuConfig& menu_config = MenuConfig::instance(); + bubble_border_->SetCornerRadius(menu_config.touchable_corner_radius); + bubble_border_->set_md_shadow_elevation( + menu_config.touchable_menu_shadow_elevation); + scroll_view_->GetContents()->SetBorder(CreateEmptyBorder( + gfx::Insets(menu_config.vertical_touchable_menu_item_padding, 0))); + } + SetBorder(std::unique_ptr<Border>(bubble_border_)); SetBackground(std::make_unique<BubbleBackground>(bubble_border_)); } @@ -318,6 +330,9 @@ BubbleBorder::Arrow MenuScrollViewContainer::BubbleBorderTypeFromAnchor( return BubbleBorder::BOTTOM_CENTER; case MENU_ANCHOR_BUBBLE_BELOW: return BubbleBorder::TOP_CENTER; + case MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE: + case MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT: + return BubbleBorder::FLOAT; default: return BubbleBorder::NONE; } diff --git a/chromium/ui/views/controls/menu/menu_types.h b/chromium/ui/views/controls/menu/menu_types.h index 2f9922261f5..d95ce0b33c0 100644 --- a/chromium/ui/views/controls/menu/menu_types.h +++ b/chromium/ui/views/controls/menu/menu_types.h @@ -21,7 +21,9 @@ enum MenuAnchorPosition { MENU_ANCHOR_BUBBLE_LEFT, MENU_ANCHOR_BUBBLE_RIGHT, MENU_ANCHOR_BUBBLE_ABOVE, - MENU_ANCHOR_BUBBLE_BELOW + MENU_ANCHOR_BUBBLE_BELOW, + MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE, + MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT }; } // namespace views diff --git a/chromium/ui/views/controls/menu/native_menu_win.cc b/chromium/ui/views/controls/menu/native_menu_win.cc index a9d8f72b428..3f38a24897c 100644 --- a/chromium/ui/views/controls/menu/native_menu_win.cc +++ b/chromium/ui/views/controls/menu/native_menu_win.cc @@ -5,7 +5,6 @@ #include "ui/views/controls/menu/native_menu_win.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc index b8926dedbdc..9b244c9e9ea 100644 --- a/chromium/ui/views/controls/menu/submenu_view.cc +++ b/chromium/ui/views/controls/menu/submenu_view.cc @@ -24,10 +24,10 @@ namespace { // Height of the drop indicator. This should be an even number. -const int kDropIndicatorHeight = 2; +constexpr int kDropIndicatorHeight = 2; // Color of the drop indicator. -const SkColor kDropIndicatorColor = SK_ColorBLACK; +constexpr SkColor kDropIndicatorColor = SK_ColorBLACK; } // namespace @@ -69,7 +69,7 @@ bool SubmenuView::HasVisibleChildren() { return false; } -int SubmenuView::GetMenuItemCount() { +int SubmenuView::GetMenuItemCount() const { int count = 0; for (int i = 0; i < child_count(); ++i) { if (child_at(i)->id() == MenuItemView::kMenuItemViewID) @@ -144,6 +144,8 @@ gfx::Size SubmenuView::CalculatePreferredSize() const { int max_complex_width = 0; // The max. width of items which contain a label and maybe an accelerator. int max_simple_width = 0; + // The minimum width of touchable items. + int touchable_minimum_width = 0; // We perform the size calculation in two passes. In the first pass, we // calculate the width of the menu. In the second, we calculate the height @@ -163,6 +165,7 @@ gfx::Size SubmenuView::CalculatePreferredSize() const { std::max(max_minor_text_width_, dimensions.minor_text_width); max_complex_width = std::max(max_complex_width, dimensions.standard_width + dimensions.children_width); + touchable_minimum_width = dimensions.standard_width; } else { max_complex_width = std::max(max_complex_width, child->GetPreferredSize().width()); @@ -178,6 +181,10 @@ gfx::Size SubmenuView::CalculatePreferredSize() const { insets.width(), minimum_preferred_width_ - 2 * insets.width())); + if (GetMenuItem()->GetMenuController() && + GetMenuItem()->GetMenuController()->use_touchable_layout()) + width = std::max(touchable_minimum_width, width); + // Then, the height for that width. int height = 0; int menu_item_width = width - insets.width(); diff --git a/chromium/ui/views/controls/menu/submenu_view.h b/chromium/ui/views/controls/menu/submenu_view.h index c17c94449e8..9ba69331264 100644 --- a/chromium/ui/views/controls/menu/submenu_view.h +++ b/chromium/ui/views/controls/menu/submenu_view.h @@ -56,7 +56,7 @@ class VIEWS_EXPORT SubmenuView : public View, // Returns the number of child views that are MenuItemViews. // MenuItemViews are identified by ID. - int GetMenuItemCount(); + int GetMenuItemCount() const; // Returns the MenuItemView at the specified index. MenuItemView* GetMenuItemAt(int index); diff --git a/chromium/ui/views/controls/native/native_view_host.cc b/chromium/ui/views/controls/native/native_view_host.cc index df116d35b8a..f15b82824d5 100644 --- a/chromium/ui/views/controls/native/native_view_host.cc +++ b/chromium/ui/views/controls/native/native_view_host.cc @@ -48,11 +48,6 @@ void NativeViewHost::Detach() { Detach(false); } -void NativeViewHost::SetPreferredSize(const gfx::Size& size) { - preferred_size_ = size; - PreferredSizeChanged(); -} - bool NativeViewHost::SetCornerRadius(int corner_radius) { return native_wrapper_->SetCornerRadius(corner_radius); } @@ -73,10 +68,6 @@ void NativeViewHost::NativeViewDestroyed() { //////////////////////////////////////////////////////////////////////////////// // NativeViewHost, View overrides: -gfx::Size NativeViewHost::CalculatePreferredSize() const { - return preferred_size_; -} - void NativeViewHost::Layout() { if (!native_view_ || !native_wrapper_.get()) return; diff --git a/chromium/ui/views/controls/native/native_view_host.h b/chromium/ui/views/controls/native/native_view_host.h index e1cd204d047..c81cb7443a5 100644 --- a/chromium/ui/views/controls/native/native_view_host.h +++ b/chromium/ui/views/controls/native/native_view_host.h @@ -47,9 +47,6 @@ class VIEWS_EXPORT NativeViewHost : public View { // detached before calling this function, and this has no effect in that case. void Detach(); - // Sets a preferred size for the native view attached to this View. - void SetPreferredSize(const gfx::Size& size); - // Sets the corner radius for clipping gfx::NativeView. Returns true on // success or false if the platform doesn't support the operation. // NB: This does not interact nicely with fast_resize. @@ -86,7 +83,6 @@ class VIEWS_EXPORT NativeViewHost : public View { void NativeViewDestroyed(); // Overridden from View: - gfx::Size CalculatePreferredSize() const override; void Layout() override; void OnPaint(gfx::Canvas* canvas) override; void VisibilityChanged(View* starting_from, bool is_visible) override; @@ -120,9 +116,6 @@ class VIEWS_EXPORT NativeViewHost : public View { // attached gfx::NativeView. std::unique_ptr<NativeViewHostWrapper> native_wrapper_; - // The preferred size of this View - gfx::Size preferred_size_; - // The actual size of the NativeView, or an empty size if no scaling of the // NativeView should occur. gfx::Size native_view_size_; 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 fb04f31f2e4..420b005a433 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura.cc @@ -10,6 +10,7 @@ #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" +#include "ui/aura/window_occlusion_tracker.h" #include "ui/base/cursor/cursor.h" #include "ui/base/hit_test.h" #include "ui/compositor/paint_recorder.h" @@ -106,6 +107,11 @@ 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; + clipping_window_delegate_->set_native_view(NULL); RemoveClippingWindow(); if (!destroyed) { @@ -143,17 +149,12 @@ void NativeViewHostAura::RemovedFromWidget() { } bool NativeViewHostAura::SetCornerRadius(int corner_radius) { -#if defined(OS_WIN) - // Layer masks don't work on Windows. See crbug.com/713359 - return false; -#else mask_ = views::Painter::CreatePaintedLayer( views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK, corner_radius)); mask_->layer()->SetFillsBoundsOpaquely(false); InstallMask(); return true; -#endif } void NativeViewHostAura::InstallClip(int x, int y, int w, int h) { diff --git a/chromium/ui/views/controls/progress_bar_unittest.cc b/chromium/ui/views/controls/progress_bar_unittest.cc index 6eddafe318b..09bb6d807bd 100644 --- a/chromium/ui/views/controls/progress_bar_unittest.cc +++ b/chromium/ui/views/controls/progress_bar_unittest.cc @@ -9,10 +9,13 @@ #include "ui/accessibility/ax_node_data.h" #include "ui/gfx/color_utils.h" #include "ui/native_theme/native_theme.h" +#include "ui/views/test/views_test_base.h" namespace views { -TEST(ProgressBarTest, Accessibility) { +using ProgressBarTest = ViewsTestBase; + +TEST_F(ProgressBarTest, Accessibility) { ProgressBar bar; bar.SetValue(0.62); @@ -26,7 +29,7 @@ TEST(ProgressBarTest, Accessibility) { } // Test that default colors can be overridden. Used by Chromecast. -TEST(ProgressBarTest, OverrideDefaultColors) { +TEST_F(ProgressBarTest, OverrideDefaultColors) { ProgressBar bar; EXPECT_NE(SK_ColorRED, bar.GetForegroundColor()); EXPECT_NE(SK_ColorGREEN, bar.GetBackgroundColor()); diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index 31fd358fa1b..06a8b1625c7 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -254,13 +254,22 @@ void ScrollView::SetContents(View* a_view) { // Protect against clients passing a contents view that has its own Layer. DCHECK(!a_view->layer()); if (ScrollsWithLayers()) { - if (!a_view->background() && GetBackgroundColor() != SK_ColorTRANSPARENT) { - a_view->SetBackground(CreateSolidBackground(GetBackgroundColor())); + bool fills_opaquely = true; + if (!a_view->background()) { + // Contents views may not be aware they need to fill their entire bounds - + // play it safe here to avoid graphical glitches + // (https://crbug.com/826472). If there's no solid background, mark the + // view as not filling its bounds opaquely. + if (GetBackgroundColor() != SK_ColorTRANSPARENT) + a_view->SetBackground(CreateSolidBackground(GetBackgroundColor())); + else + fills_opaquely = false; } a_view->SetPaintToLayer(); a_view->layer()->SetDidScrollCallback( base::Bind(&ScrollView::OnLayerScrolled, base::Unretained(this))); a_view->layer()->SetScrollable(contents_viewport_->bounds().size()); + a_view->layer()->SetFillsBoundsOpaquely(fills_opaquely); } SetHeaderOrContents(contents_viewport_, a_view, &contents_); } diff --git a/chromium/ui/views/controls/scroll_view_unittest.cc b/chromium/ui/views/controls/scroll_view_unittest.cc index d57fbb058e0..6b818c25420 100644 --- a/chromium/ui/views/controls/scroll_view_unittest.cc +++ b/chromium/ui/views/controls/scroll_view_unittest.cc @@ -5,7 +5,6 @@ #include "ui/views/controls/scroll_view.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/test/test_timeouts.h" #include "base/threading/thread_task_runner_handle.h" @@ -134,12 +133,12 @@ class CustomView : public View { DISALLOW_COPY_AND_ASSIGN(CustomView); }; -void CheckScrollbarVisibility(const ScrollView& scroll_view, +void CheckScrollbarVisibility(const ScrollView* scroll_view, ScrollBarOrientation orientation, bool should_be_visible) { const ScrollBar* scrollbar = orientation == HORIZONTAL - ? scroll_view.horizontal_scroll_bar() - : scroll_view.vertical_scroll_bar(); + ? scroll_view->horizontal_scroll_bar() + : scroll_view->vertical_scroll_bar(); if (should_be_visible) { ASSERT_TRUE(scrollbar); EXPECT_TRUE(scrollbar->visible()); @@ -179,11 +178,16 @@ class ScrollViewTest : public ViewsTestBase { public: ScrollViewTest() {} + void SetUp() override { + ViewsTestBase::SetUp(); + scroll_view_ = std::make_unique<ScrollView>(); + } + View* InstallContents() { const gfx::Rect default_outer_bounds(0, 0, 100, 100); View* contents = new View; - scroll_view_.SetContents(contents); - scroll_view_.SetBoundsRect(default_outer_bounds); + scroll_view_->SetContents(contents); + scroll_view_->SetBoundsRect(default_outer_bounds); return contents; } @@ -208,14 +212,14 @@ class ScrollViewTest : public ViewsTestBase { protected: #endif int VerticalScrollBarWidth() { - return scroll_view_.vertical_scroll_bar()->GetThickness(); + return scroll_view_->vertical_scroll_bar()->GetThickness(); } int HorizontalScrollBarHeight() { - return scroll_view_.horizontal_scroll_bar()->GetThickness(); + return scroll_view_->horizontal_scroll_bar()->GetThickness(); } - ScrollView scroll_view_; + std::unique_ptr<ScrollView> scroll_view_; private: DISALLOW_COPY_AND_ASSIGN(ScrollViewTest); @@ -332,7 +336,7 @@ const int WidgetScrollViewTest::kDefaultWidth; // Verifies the viewport is sized to fit the available space. TEST_F(ScrollViewTest, ViewportSizedToFit) { View* contents = InstallContents(); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ("0,0 100x100", contents->parent()->bounds().ToString()); } @@ -340,9 +344,9 @@ TEST_F(ScrollViewTest, ViewportSizedToFit) { // bounded scroll view. TEST_F(ScrollViewTest, BoundedViewportSizedToFit) { View* contents = InstallContents(); - scroll_view_.ClipHeightTo(100, 200); - scroll_view_.SetBorder(CreateSolidBorder(2, 0)); - scroll_view_.Layout(); + scroll_view_->ClipHeightTo(100, 200); + scroll_view_->SetBorder(CreateSolidBorder(2, 0)); + scroll_view_->Layout(); EXPECT_EQ("2,2 96x96", contents->parent()->bounds().ToString()); // Make sure the width of |contents| is set properly not to overflow the @@ -355,11 +359,11 @@ TEST_F(ScrollViewTest, BoundedViewportSizedToFit) { TEST_F(ScrollViewTest, VerticalScrollbarDoesNotAppearUnnecessarily) { const gfx::Rect default_outer_bounds(0, 0, 100, 100); View* contents = new VerticalResizingView; - scroll_view_.SetContents(contents); - scroll_view_.SetBoundsRect(default_outer_bounds); - scroll_view_.Layout(); - EXPECT_FALSE(scroll_view_.vertical_scroll_bar()->visible()); - EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible()); + scroll_view_->SetContents(contents); + scroll_view_->SetBoundsRect(default_outer_bounds); + scroll_view_->Layout(); + EXPECT_FALSE(scroll_view_->vertical_scroll_bar()->visible()); + EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible()); } // Verifies the scrollbars are added as necessary. @@ -369,54 +373,54 @@ TEST_F(ScrollViewTest, ScrollBars) { // Size the contents such that vertical scrollbar is needed. contents->SetBounds(0, 0, 50, 400); - scroll_view_.Layout(); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(), + scroll_view_->Layout(); + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(), contents->parent()->width()); EXPECT_EQ(100, contents->parent()->height()); - CheckScrollbarVisibility(scroll_view_, VERTICAL, true); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false); - EXPECT_TRUE(!scroll_view_.horizontal_scroll_bar() || - !scroll_view_.horizontal_scroll_bar()->visible()); - ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible()); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false); + EXPECT_TRUE(!scroll_view_->horizontal_scroll_bar() || + !scroll_view_->horizontal_scroll_bar()->visible()); + ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible()); // Size the contents such that horizontal scrollbar is needed. contents->SetBounds(0, 0, 400, 50); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(100, contents->parent()->width()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(), + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(), contents->parent()->height()); - CheckScrollbarVisibility(scroll_view_, VERTICAL, false); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, false); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true); // Both horizontal and vertical. contents->SetBounds(0, 0, 300, 400); - scroll_view_.Layout(); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(), + scroll_view_->Layout(); + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(), contents->parent()->width()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(), + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(), contents->parent()->height()); - CheckScrollbarVisibility(scroll_view_, VERTICAL, true); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true); // Add a border, test vertical scrollbar. const int kTopPadding = 1; const int kLeftPadding = 2; const int kBottomPadding = 3; const int kRightPadding = 4; - scroll_view_.SetBorder(CreateEmptyBorder(kTopPadding, kLeftPadding, - kBottomPadding, kRightPadding)); + scroll_view_->SetBorder(CreateEmptyBorder(kTopPadding, kLeftPadding, + kBottomPadding, kRightPadding)); contents->SetBounds(0, 0, 50, 400); - scroll_view_.Layout(); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth() - kLeftPadding - + scroll_view_->Layout(); + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth() - kLeftPadding - kRightPadding, contents->parent()->width()); EXPECT_EQ(100 - kTopPadding - kBottomPadding, contents->parent()->height()); - EXPECT_TRUE(!scroll_view_.horizontal_scroll_bar() || - !scroll_view_.horizontal_scroll_bar()->visible()); - ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible()); - gfx::Rect bounds = scroll_view_.vertical_scroll_bar()->bounds(); + EXPECT_TRUE(!scroll_view_->horizontal_scroll_bar() || + !scroll_view_->horizontal_scroll_bar()->visible()); + ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible()); + gfx::Rect bounds = scroll_view_->vertical_scroll_bar()->bounds(); EXPECT_EQ(100 - VerticalScrollBarWidth() - kRightPadding, bounds.x()); EXPECT_EQ(100 - kRightPadding, bounds.right()); EXPECT_EQ(kTopPadding, bounds.y()); @@ -424,16 +428,16 @@ TEST_F(ScrollViewTest, ScrollBars) { // Horizontal with border. contents->SetBounds(0, 0, 400, 50); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(100 - kLeftPadding - kRightPadding, contents->parent()->width()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - kTopPadding - + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - kTopPadding - kBottomPadding, contents->parent()->height()); - ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible()); - EXPECT_TRUE(!scroll_view_.vertical_scroll_bar() || - !scroll_view_.vertical_scroll_bar()->visible()); - bounds = scroll_view_.horizontal_scroll_bar()->bounds(); + ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible()); + EXPECT_TRUE(!scroll_view_->vertical_scroll_bar() || + !scroll_view_->vertical_scroll_bar()->visible()); + bounds = scroll_view_->horizontal_scroll_bar()->bounds(); EXPECT_EQ(kLeftPadding, bounds.x()); EXPECT_EQ(100 - kRightPadding, bounds.right()); EXPECT_EQ(100 - kBottomPadding - HorizontalScrollBarHeight(), bounds.y()); @@ -441,26 +445,26 @@ TEST_F(ScrollViewTest, ScrollBars) { // Both horizontal and vertical with border. contents->SetBounds(0, 0, 300, 400); - scroll_view_.Layout(); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth() - kLeftPadding - + scroll_view_->Layout(); + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth() - kLeftPadding - kRightPadding, contents->parent()->width()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - kTopPadding - + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - kTopPadding - kBottomPadding, contents->parent()->height()); - bounds = scroll_view_.horizontal_scroll_bar()->bounds(); + bounds = scroll_view_->horizontal_scroll_bar()->bounds(); // Check horiz. - ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible()); - bounds = scroll_view_.horizontal_scroll_bar()->bounds(); + ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible()); + bounds = scroll_view_->horizontal_scroll_bar()->bounds(); EXPECT_EQ(kLeftPadding, bounds.x()); EXPECT_EQ(100 - kRightPadding - VerticalScrollBarWidth(), bounds.right()); EXPECT_EQ(100 - kBottomPadding - HorizontalScrollBarHeight(), bounds.y()); EXPECT_EQ(100 - kBottomPadding, bounds.bottom()); // Check vert. - ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible()); - bounds = scroll_view_.vertical_scroll_bar()->bounds(); + ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible()); + bounds = scroll_view_->vertical_scroll_bar()->bounds(); EXPECT_EQ(100 - VerticalScrollBarWidth() - kRightPadding, bounds.x()); EXPECT_EQ(100 - kRightPadding, bounds.right()); EXPECT_EQ(kTopPadding, bounds.y()); @@ -471,11 +475,11 @@ TEST_F(ScrollViewTest, ScrollBars) { // Assertions around adding a header. TEST_F(ScrollViewTest, Header) { CustomView* header = new CustomView; - scroll_view_.SetHeader(header); + scroll_view_->SetHeader(header); View* header_parent = header->parent(); View* contents = InstallContents(); - scroll_view_.Layout(); + scroll_view_->Layout(); // |header|s preferred size is empty, which should result in all space going // to contents. EXPECT_EQ("0,0 100x0", header->parent()->bounds().ToString()); @@ -502,7 +506,7 @@ TEST_F(ScrollViewTest, Header) { EXPECT_EQ("0,0 0x0", contents->bounds().ToString()); // Remove the header. - scroll_view_.SetHeader(NULL); + scroll_view_->SetHeader(NULL); // SetHeader(NULL) deletes header. header = NULL; EXPECT_EQ("0,0 100x0", header_parent->bounds().ToString()); @@ -512,111 +516,111 @@ TEST_F(ScrollViewTest, Header) { // Verifies the scrollbars are added as necessary when a header is present. TEST_F(ScrollViewTest, ScrollBarsWithHeader) { CustomView* header = new CustomView; - scroll_view_.SetHeader(header); + scroll_view_->SetHeader(header); View* contents = InstallContents(); header->SetPreferredSize(gfx::Size(10, 20)); // Size the contents such that vertical scrollbar is needed. contents->SetBounds(0, 0, 50, 400); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(0, contents->parent()->x()); EXPECT_EQ(20, contents->parent()->y()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(), + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(), contents->parent()->width()); EXPECT_EQ(80, contents->parent()->height()); EXPECT_EQ(0, header->parent()->x()); EXPECT_EQ(0, header->parent()->y()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(), + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(), header->parent()->width()); EXPECT_EQ(20, header->parent()->height()); - EXPECT_TRUE(!scroll_view_.horizontal_scroll_bar() || - !scroll_view_.horizontal_scroll_bar()->visible()); - ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible()); + EXPECT_TRUE(!scroll_view_->horizontal_scroll_bar() || + !scroll_view_->horizontal_scroll_bar()->visible()); + ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible()); // Make sure the vertical scrollbar overlaps the header for traditional // scrollbars and doesn't overlap the header for overlay scrollbars. const int expected_scrollbar_y = - scroll_view_.vertical_scroll_bar()->OverlapsContent() + scroll_view_->vertical_scroll_bar()->OverlapsContent() ? header->bounds().bottom() : header->y(); - EXPECT_EQ(expected_scrollbar_y, scroll_view_.vertical_scroll_bar()->y()); + EXPECT_EQ(expected_scrollbar_y, scroll_view_->vertical_scroll_bar()->y()); EXPECT_EQ(header->y(), contents->y()); // Size the contents such that horizontal scrollbar is needed. contents->SetBounds(0, 0, 400, 50); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(0, contents->parent()->x()); EXPECT_EQ(20, contents->parent()->y()); EXPECT_EQ(100, contents->parent()->width()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - 20, + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - 20, contents->parent()->height()); EXPECT_EQ(0, header->parent()->x()); EXPECT_EQ(0, header->parent()->y()); EXPECT_EQ(100, header->parent()->width()); EXPECT_EQ(20, header->parent()->height()); - ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible()); - EXPECT_TRUE(!scroll_view_.vertical_scroll_bar() || - !scroll_view_.vertical_scroll_bar()->visible()); + ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible()); + EXPECT_TRUE(!scroll_view_->vertical_scroll_bar() || + !scroll_view_->vertical_scroll_bar()->visible()); // Both horizontal and vertical. contents->SetBounds(0, 0, 300, 400); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(0, contents->parent()->x()); EXPECT_EQ(20, contents->parent()->y()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(), + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(), contents->parent()->width()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight() - 20, + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight() - 20, contents->parent()->height()); EXPECT_EQ(0, header->parent()->x()); EXPECT_EQ(0, header->parent()->y()); - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutWidth(), + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutWidth(), header->parent()->width()); EXPECT_EQ(20, header->parent()->height()); - ASSERT_TRUE(scroll_view_.horizontal_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.horizontal_scroll_bar()->visible()); - ASSERT_TRUE(scroll_view_.vertical_scroll_bar() != NULL); - EXPECT_TRUE(scroll_view_.vertical_scroll_bar()->visible()); + ASSERT_TRUE(scroll_view_->horizontal_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->horizontal_scroll_bar()->visible()); + ASSERT_TRUE(scroll_view_->vertical_scroll_bar() != NULL); + EXPECT_TRUE(scroll_view_->vertical_scroll_bar()->visible()); } // Verifies the header scrolls horizontally with the content. TEST_F(ScrollViewTest, HeaderScrollsWithContent) { - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); CustomView* contents = new CustomView; - scroll_view_.SetContents(contents); + scroll_view_->SetContents(contents); contents->SetPreferredSize(gfx::Size(500, 500)); CustomView* header = new CustomView; - scroll_view_.SetHeader(header); + scroll_view_->SetHeader(header); header->SetPreferredSize(gfx::Size(500, 20)); - scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); + scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString()); EXPECT_EQ("0,0", header->origin().ToString()); // Scroll the horizontal scrollbar. - ASSERT_TRUE(scroll_view_.horizontal_scroll_bar()); - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 1); + ASSERT_TRUE(scroll_view_->horizontal_scroll_bar()); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 1); EXPECT_EQ("-1,0", test_api.IntegralViewOffset().ToString()); EXPECT_EQ("-1,0", header->origin().ToString()); // Scrolling the vertical scrollbar shouldn't effect the header. - ASSERT_TRUE(scroll_view_.vertical_scroll_bar()); - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 1); + ASSERT_TRUE(scroll_view_->vertical_scroll_bar()); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 1); EXPECT_EQ("-1,-1", test_api.IntegralViewOffset().ToString()); EXPECT_EQ("-1,0", header->origin().ToString()); } // Verifies ScrollRectToVisible() on the child works. TEST_F(ScrollViewTest, ScrollRectToVisible) { - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); CustomView* contents = new CustomView; - scroll_view_.SetContents(contents); + scroll_view_->SetContents(contents); contents->SetPreferredSize(gfx::Size(500, 1000)); - scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); - scroll_view_.Layout(); + scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); + scroll_view_->Layout(); EXPECT_EQ("0,0", test_api.IntegralViewOffset().ToString()); // Scroll to y=405 height=10, this should make the y position of the content @@ -625,7 +629,7 @@ TEST_F(ScrollViewTest, ScrollRectToVisible) { const int viewport_height = test_api.contents_viewport()->height(); // Expect there to be a horizontal scrollbar, making the viewport shorter. - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(), viewport_height); + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(), viewport_height); gfx::ScrollOffset offset = test_api.CurrentOffset(); EXPECT_EQ(415 - viewport_height, offset.y()); @@ -637,17 +641,17 @@ TEST_F(ScrollViewTest, ScrollRectToVisible) { // Verifies that child scrolls into view when it's focused. TEST_F(ScrollViewTest, ScrollChildToVisibleOnFocus) { - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); CustomView* contents = new CustomView; - scroll_view_.SetContents(contents); + scroll_view_->SetContents(contents); contents->SetPreferredSize(gfx::Size(500, 1000)); FixedView* child = new FixedView; child->SetPreferredSize(gfx::Size(10, 10)); child->SetPosition(gfx::Point(0, 405)); contents->AddChildView(child); - scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 100)); - scroll_view_.Layout(); + scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100)); + scroll_view_->Layout(); EXPECT_EQ(gfx::Point(), test_api.IntegralViewOffset()); // Set focus to the child control. This should cause the control to scroll to @@ -657,7 +661,7 @@ TEST_F(ScrollViewTest, ScrollChildToVisibleOnFocus) { const int viewport_height = test_api.contents_viewport()->height(); // Expect there to be a horizontal scrollbar, making the viewport shorter. - EXPECT_EQ(100 - scroll_view_.GetScrollBarLayoutHeight(), viewport_height); + EXPECT_EQ(100 - scroll_view_->GetScrollBarLayoutHeight(), viewport_height); gfx::ScrollOffset offset = test_api.CurrentOffset(); EXPECT_EQ(415 - viewport_height, offset.y()); @@ -666,138 +670,138 @@ TEST_F(ScrollViewTest, ScrollChildToVisibleOnFocus) { // Verifies ClipHeightTo() uses the height of the content when it is between the // minimum and maximum height values. TEST_F(ScrollViewTest, ClipHeightToNormalContentHeight) { - scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight); + scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight); const int kNormalContentHeight = 75; - scroll_view_.SetContents( + scroll_view_->SetContents( new views::StaticSizedView(gfx::Size(kWidth, kNormalContentHeight))); EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), - scroll_view_.GetPreferredSize()); + scroll_view_->GetPreferredSize()); - scroll_view_.SizeToPreferredSize(); - scroll_view_.Layout(); + scroll_view_->SizeToPreferredSize(); + scroll_view_->Layout(); EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), - scroll_view_.contents()->size()); - EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), scroll_view_.size()); + scroll_view_->contents()->size()); + EXPECT_EQ(gfx::Size(kWidth, kNormalContentHeight), scroll_view_->size()); } // Verifies ClipHeightTo() uses the minimum height when the content is shorter // than the minimum height value. TEST_F(ScrollViewTest, ClipHeightToShortContentHeight) { - scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight); + scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight); const int kShortContentHeight = 10; View* contents = new views::StaticSizedView(gfx::Size(kWidth, kShortContentHeight)); - scroll_view_.SetContents(contents); + scroll_view_->SetContents(contents); - EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_.GetPreferredSize()); + EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_->GetPreferredSize()); - scroll_view_.SizeToPreferredSize(); - scroll_view_.Layout(); + scroll_view_->SizeToPreferredSize(); + scroll_view_->Layout(); // Layered scrolling requires the contents to fill the viewport. if (contents->layer()) { - EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_.contents()->size()); + EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_->contents()->size()); } else { EXPECT_EQ(gfx::Size(kWidth, kShortContentHeight), - scroll_view_.contents()->size()); + scroll_view_->contents()->size()); } - EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_.size()); + EXPECT_EQ(gfx::Size(kWidth, kMinHeight), scroll_view_->size()); } // Verifies ClipHeightTo() uses the maximum height when the content is longer // thamn the maximum height value. TEST_F(ScrollViewTest, ClipHeightToTallContentHeight) { - scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight); + scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight); const int kTallContentHeight = 1000; - scroll_view_.SetContents( + scroll_view_->SetContents( new views::StaticSizedView(gfx::Size(kWidth, kTallContentHeight))); - EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_.GetPreferredSize()); + EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_->GetPreferredSize()); - scroll_view_.SizeToPreferredSize(); - scroll_view_.Layout(); + scroll_view_->SizeToPreferredSize(); + scroll_view_->Layout(); // The width may be less than kWidth if the scroll bar takes up some width. - EXPECT_GE(kWidth, scroll_view_.contents()->width()); - EXPECT_EQ(kTallContentHeight, scroll_view_.contents()->height()); - EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_.size()); + EXPECT_GE(kWidth, scroll_view_->contents()->width()); + EXPECT_EQ(kTallContentHeight, scroll_view_->contents()->height()); + EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_->size()); } // Verifies that when ClipHeightTo() produces a scrollbar, it reduces the width // of the inner content of the ScrollView. TEST_F(ScrollViewTest, ClipHeightToScrollbarUsesWidth) { - scroll_view_.ClipHeightTo(kMinHeight, kMaxHeight); + scroll_view_->ClipHeightTo(kMinHeight, kMaxHeight); // Create a view that will be much taller than it is wide. - scroll_view_.SetContents(new views::ProportionallySizedView(1000)); + scroll_view_->SetContents(new views::ProportionallySizedView(1000)); // Without any width, it will default to 0,0 but be overridden by min height. - scroll_view_.SizeToPreferredSize(); - EXPECT_EQ(gfx::Size(0, kMinHeight), scroll_view_.GetPreferredSize()); + scroll_view_->SizeToPreferredSize(); + EXPECT_EQ(gfx::Size(0, kMinHeight), scroll_view_->GetPreferredSize()); - gfx::Size new_size(kWidth, scroll_view_.GetHeightForWidth(kWidth)); - scroll_view_.SetSize(new_size); - scroll_view_.Layout(); + gfx::Size new_size(kWidth, scroll_view_->GetHeightForWidth(kWidth)); + scroll_view_->SetSize(new_size); + scroll_view_->Layout(); - int expected_width = kWidth - scroll_view_.GetScrollBarLayoutWidth(); - EXPECT_EQ(scroll_view_.contents()->size().width(), expected_width); - EXPECT_EQ(scroll_view_.contents()->size().height(), 1000 * expected_width); - EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_.size()); + int expected_width = kWidth - scroll_view_->GetScrollBarLayoutWidth(); + EXPECT_EQ(scroll_view_->contents()->size().width(), expected_width); + EXPECT_EQ(scroll_view_->contents()->size().height(), 1000 * expected_width); + EXPECT_EQ(gfx::Size(kWidth, kMaxHeight), scroll_view_->size()); } TEST_F(ScrollViewTest, CornerViewVisibility) { View* contents = InstallContents(); - View* corner_view = ScrollViewTestApi(&scroll_view_).corner_view(); + View* corner_view = ScrollViewTestApi(scroll_view_.get()).corner_view(); contents->SetBounds(0, 0, 200, 200); - scroll_view_.Layout(); + scroll_view_->Layout(); // Corner view should not exist if using overlay scrollbars. - if (scroll_view_.vertical_scroll_bar()->OverlapsContent()) { + if (scroll_view_->vertical_scroll_bar()->OverlapsContent()) { EXPECT_FALSE(corner_view->parent()); return; } // Corner view should be visible when both scrollbars are visible. - EXPECT_EQ(&scroll_view_, corner_view->parent()); + EXPECT_EQ(scroll_view_.get(), corner_view->parent()); EXPECT_TRUE(corner_view->visible()); // Corner view should be aligned to the scrollbars. - EXPECT_EQ(scroll_view_.vertical_scroll_bar()->x(), corner_view->x()); - EXPECT_EQ(scroll_view_.horizontal_scroll_bar()->y(), corner_view->y()); - EXPECT_EQ(scroll_view_.GetScrollBarLayoutWidth(), corner_view->width()); - EXPECT_EQ(scroll_view_.GetScrollBarLayoutHeight(), corner_view->height()); + EXPECT_EQ(scroll_view_->vertical_scroll_bar()->x(), corner_view->x()); + EXPECT_EQ(scroll_view_->horizontal_scroll_bar()->y(), corner_view->y()); + EXPECT_EQ(scroll_view_->GetScrollBarLayoutWidth(), corner_view->width()); + EXPECT_EQ(scroll_view_->GetScrollBarLayoutHeight(), corner_view->height()); // Corner view should be removed when only the vertical scrollbar is visible. contents->SetBounds(0, 0, 50, 200); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_FALSE(corner_view->parent()); // ... or when only the horizontal scrollbar is visible. contents->SetBounds(0, 0, 200, 50); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_FALSE(corner_view->parent()); // ... or when no scrollbar is visible. contents->SetBounds(0, 0, 50, 50); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_FALSE(corner_view->parent()); // Corner view should reappear when both scrollbars reappear. contents->SetBounds(0, 0, 200, 200); - scroll_view_.Layout(); - EXPECT_EQ(&scroll_view_, corner_view->parent()); + scroll_view_->Layout(); + EXPECT_EQ(scroll_view_.get(), corner_view->parent()); EXPECT_TRUE(corner_view->visible()); } TEST_F(ScrollViewTest, ChildWithLayerTest) { View* contents = InstallContents(); - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); if (test_api.contents_viewport()->layer()) return; @@ -812,7 +816,7 @@ TEST_F(ScrollViewTest, ChildWithLayerTest) { EXPECT_TRUE(test_api.contents_viewport()->layer()->fills_bounds_opaquely()); // Setting a transparent color should make fills opaquely false. - scroll_view_.SetBackgroundColor(SK_ColorTRANSPARENT); + scroll_view_->SetBackgroundColor(SK_ColorTRANSPARENT); EXPECT_FALSE(test_api.contents_viewport()->layer()->fills_bounds_opaquely()); child->DestroyLayer(); @@ -829,12 +833,12 @@ TEST_F(ScrollViewTest, ChildWithLayerTest) { // is added to the ScrollView's viewport. TEST_F(ScrollViewTest, DontCreateLayerOnViewportIfLayerOnScrollViewCreated) { View* contents = InstallContents(); - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); if (test_api.contents_viewport()->layer()) return; - scroll_view_.SetPaintToLayer(); + scroll_view_->SetPaintToLayer(); View* child = new View(); contents->AddChildView(child); @@ -853,35 +857,35 @@ TEST_F(ScrollViewTest, CocoaOverlayScrollBars) { // Size the contents such that vertical scrollbar is needed. // Since it is overlaid, the ViewPort size should match the ScrollView. contents->SetBounds(0, 0, 50, 400); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(100, contents->parent()->width()); EXPECT_EQ(100, contents->parent()->height()); - EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutWidth()); - CheckScrollbarVisibility(scroll_view_, VERTICAL, true); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false); + EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutWidth()); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false); // Size the contents such that horizontal scrollbar is needed. contents->SetBounds(0, 0, 400, 50); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(100, contents->parent()->width()); EXPECT_EQ(100, contents->parent()->height()); - EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutHeight()); - CheckScrollbarVisibility(scroll_view_, VERTICAL, false); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true); + EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutHeight()); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, false); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true); // Both horizontal and vertical scrollbars. contents->SetBounds(0, 0, 300, 400); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(100, contents->parent()->width()); EXPECT_EQ(100, contents->parent()->height()); - EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutWidth()); - EXPECT_EQ(0, scroll_view_.GetScrollBarLayoutHeight()); - CheckScrollbarVisibility(scroll_view_, VERTICAL, true); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true); + EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutWidth()); + EXPECT_EQ(0, scroll_view_->GetScrollBarLayoutHeight()); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true); // Make sure the horizontal and vertical scrollbars don't overlap each other. - gfx::Rect vert_bounds = scroll_view_.vertical_scroll_bar()->bounds(); - gfx::Rect horiz_bounds = scroll_view_.horizontal_scroll_bar()->bounds(); + gfx::Rect vert_bounds = scroll_view_->vertical_scroll_bar()->bounds(); + gfx::Rect horiz_bounds = scroll_view_->horizontal_scroll_bar()->bounds(); EXPECT_EQ(vert_bounds.x(), horiz_bounds.right()); EXPECT_EQ(horiz_bounds.y(), vert_bounds.bottom()); @@ -982,11 +986,11 @@ TEST_F(WidgetScrollViewTest, ScrollersOnRest) { // Test that increasing the size of the viewport "below" scrolled content causes // the content to scroll up so that it still fills the viewport. TEST_F(ScrollViewTest, ConstrainScrollToBounds) { - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); View* contents = InstallContents(); contents->SetBoundsRect(gfx::Rect(0, 0, 300, 300)); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(gfx::ScrollOffset(), test_api.CurrentOffset()); @@ -996,49 +1000,49 @@ TEST_F(ScrollViewTest, ConstrainScrollToBounds) { EXPECT_NE(gfx::ScrollOffset(), fully_scrolled); // Making the viewport 55 pixels taller should scroll up the same amount. - scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 100, 155)); - scroll_view_.Layout(); + scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 155)); + scroll_view_->Layout(); EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y()); EXPECT_EQ(fully_scrolled.x(), test_api.CurrentOffset().x()); // And 77 pixels wider should scroll left. Also make it short again: the y- // offset from the last change should remain. - scroll_view_.SetBoundsRect(gfx::Rect(0, 0, 177, 100)); - scroll_view_.Layout(); + scroll_view_->SetBoundsRect(gfx::Rect(0, 0, 177, 100)); + scroll_view_->Layout(); EXPECT_EQ(fully_scrolled.y() - 55, test_api.CurrentOffset().y()); EXPECT_EQ(fully_scrolled.x() - 77, test_api.CurrentOffset().x()); } // Calling Layout on ScrollView should not reset the scroll location. TEST_F(ScrollViewTest, ContentScrollNotResetOnLayout) { - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); CustomView* contents = new CustomView; contents->SetPreferredSize(gfx::Size(300, 300)); - scroll_view_.SetContents(contents); - scroll_view_.ClipHeightTo(0, 150); - scroll_view_.SizeToPreferredSize(); + scroll_view_->SetContents(contents); + scroll_view_->ClipHeightTo(0, 150); + scroll_view_->SizeToPreferredSize(); // ScrollView preferred width matches that of |contents|, with the height // capped at the value we clipped to. - EXPECT_EQ(gfx::Size(300, 150), scroll_view_.size()); + EXPECT_EQ(gfx::Size(300, 150), scroll_view_->size()); // Scroll down. - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 25); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), 25); EXPECT_EQ(25, test_api.CurrentOffset().y()); // Call Layout; no change to scroll position. - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(25, test_api.CurrentOffset().y()); // Change contents of |contents|, call Layout; still no change to scroll // position. contents->SetPreferredSize(gfx::Size(300, 500)); contents->InvalidateLayout(); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(25, test_api.CurrentOffset().y()); // Change |contents| to be shorter than the ScrollView's clipped height. // This /will/ change the scroll location due to ConstrainScrollToBounds. contents->SetPreferredSize(gfx::Size(300, 50)); - scroll_view_.Layout(); + scroll_view_->Layout(); EXPECT_EQ(0, test_api.CurrentOffset().y()); } @@ -1046,16 +1050,16 @@ TEST_F(ScrollViewTest, ContentScrollNotResetOnLayout) { TEST_F(ScrollViewTest, VerticalOverflowIndicators) { const int kWidth = 100; - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); // Set up with vertical scrollbar. FixedView* contents = new FixedView; contents->SetPreferredSize(gfx::Size(kWidth, kMaxHeight * 5)); - scroll_view_.SetContents(contents); - scroll_view_.ClipHeightTo(0, kMaxHeight); + scroll_view_->SetContents(contents); + scroll_view_->ClipHeightTo(0, kMaxHeight); // Make sure the size is set such that no horizontal scrollbar gets shown. - scroll_view_.SetSize( + scroll_view_->SetSize( gfx::Size(kWidth + test_api.GetBaseScrollBar(VERTICAL)->GetThickness(), kMaxHeight)); @@ -1064,8 +1068,8 @@ TEST_F(ScrollViewTest, VerticalOverflowIndicators) { // The vertical scroll bar should be visible and the horizontal scroll bar // should not. - CheckScrollbarVisibility(scroll_view_, VERTICAL, true); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false); // The overflow indicator on the bottom should be visible. EXPECT_TRUE(test_api.more_content_bottom()->visible()); @@ -1079,7 +1083,7 @@ TEST_F(ScrollViewTest, VerticalOverflowIndicators) { // Now scroll the view to someplace in the middle of the scrollable region. int offset = kMaxHeight * 2; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset()); // At this point, both overflow indicators on the top and bottom should be @@ -1093,7 +1097,7 @@ TEST_F(ScrollViewTest, VerticalOverflowIndicators) { // Finally scroll the view to end of the scrollable region. offset = kMaxHeight * 4; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset()); // The overflow indicator on the bottom should not be visible. @@ -1111,15 +1115,15 @@ TEST_F(ScrollViewTest, HorizontalOverflowIndicators) { const int kWidth = 100; const int kHeight = 100; - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); // Set up with horizontal scrollbar. FixedView* contents = new FixedView; contents->SetPreferredSize(gfx::Size(kWidth * 5, kHeight)); - scroll_view_.SetContents(contents); + scroll_view_->SetContents(contents); // Make sure the size is set such that no vertical scrollbar gets shown. - scroll_view_.SetSize(gfx::Size( + scroll_view_->SetSize(gfx::Size( kWidth, kHeight + test_api.GetBaseScrollBar(HORIZONTAL)->GetThickness())); contents->SetBounds(0, 0, kWidth * 5, kHeight); @@ -1129,8 +1133,8 @@ TEST_F(ScrollViewTest, HorizontalOverflowIndicators) { // The horizontal scroll bar should be visible and the vertical scroll bar // should not. - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true); - CheckScrollbarVisibility(scroll_view_, VERTICAL, false); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, false); // The overflow indicator on the right should be visible. EXPECT_TRUE(test_api.more_content_right()->visible()); @@ -1144,7 +1148,7 @@ TEST_F(ScrollViewTest, HorizontalOverflowIndicators) { // Now scroll the view to someplace in the middle of the scrollable region. int offset = kWidth * 2; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset); EXPECT_EQ(gfx::ScrollOffset(offset, 0), test_api.CurrentOffset()); // At this point, both overflow indicators on the left and right should be @@ -1158,7 +1162,7 @@ TEST_F(ScrollViewTest, HorizontalOverflowIndicators) { // Finally scroll the view to end of the scrollable region. offset = kWidth * 4; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), offset); EXPECT_EQ(gfx::ScrollOffset(offset, 0), test_api.CurrentOffset()); // The overflow indicator on the right should not be visible. @@ -1176,22 +1180,22 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { const int kWidth = 100; const int kHeight = 100; - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); // Set up with both horizontal and vertical scrollbars. FixedView* contents = new FixedView; contents->SetPreferredSize(gfx::Size(kWidth * 5, kHeight * 5)); - scroll_view_.SetContents(contents); + scroll_view_->SetContents(contents); // Make sure the size is set such that both scrollbars are shown. - scroll_view_.SetSize(gfx::Size(kWidth, kHeight)); + scroll_view_->SetSize(gfx::Size(kWidth, kHeight)); // Make sure the initial origin is 0,0 EXPECT_EQ(gfx::ScrollOffset(0, 0), test_api.CurrentOffset()); // The horizontal and vertical scroll bars should be visible. - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, true); - CheckScrollbarVisibility(scroll_view_, VERTICAL, true); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, true); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true); // The overflow indicators on the right and bottom should not be visible since // they are against the scrollbars. @@ -1205,8 +1209,8 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { // Now scroll the view to someplace in the middle of the horizontal scrollable // region. int offset_x = kWidth * 2; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), - offset_x); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), + offset_x); EXPECT_EQ(gfx::ScrollOffset(offset_x, 0), test_api.CurrentOffset()); // Since there is a vertical scrollbar only the overflow indicator on the left @@ -1220,8 +1224,8 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { // Next, scroll the view to end of the scrollable region. offset_x = kWidth * 4; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), - offset_x); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), + offset_x); EXPECT_EQ(gfx::ScrollOffset(offset_x, 0), test_api.CurrentOffset()); // The overflow indicator on the right should still not be visible. @@ -1237,7 +1241,7 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { EXPECT_FALSE(test_api.more_content_bottom()->visible()); // Return the view back to the horizontal origin. - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 0); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), 0); EXPECT_EQ(gfx::ScrollOffset(0, 0), test_api.CurrentOffset()); // The overflow indicators on the right and bottom should not be visible since @@ -1253,7 +1257,7 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { // Now scroll the view to somplace in the middle of the vertical scrollable // region. int offset_y = kHeight * 2; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y); EXPECT_EQ(gfx::ScrollOffset(0, offset_y), test_api.CurrentOffset()); // Similar to the above, since there is a horizontal scrollbar only the @@ -1268,7 +1272,7 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { // Finally, for the vertical test scroll the region all the way to the end. offset_y = kHeight * 4; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset_y); EXPECT_EQ(gfx::ScrollOffset(0, offset_y), test_api.CurrentOffset()); // The overflow indicator on the bottom should still not be visible. @@ -1286,8 +1290,8 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { // Back to the horizontal. Scroll all the way to the end in the horizontal // direction. offset_x = kWidth * 4; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), - offset_x); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(HORIZONTAL), + offset_x); EXPECT_EQ(gfx::ScrollOffset(offset_x, offset_y), test_api.CurrentOffset()); // The overflow indicator on the bottom and right should still not be visible. @@ -1302,19 +1306,19 @@ TEST_F(ScrollViewTest, HorizontalVerticalOverflowIndicators) { TEST_F(ScrollViewTest, VerticalWithHeaderOverflowIndicators) { const int kWidth = 100; - ScrollViewTestApi test_api(&scroll_view_); + ScrollViewTestApi test_api(scroll_view_.get()); // Set up with vertical scrollbar and a header. FixedView* contents = new FixedView; CustomView* header = new CustomView; contents->SetPreferredSize(gfx::Size(kWidth, kMaxHeight * 5)); - scroll_view_.SetContents(contents); + scroll_view_->SetContents(contents); header->SetPreferredSize(gfx::Size(10, 20)); - scroll_view_.SetHeader(header); - scroll_view_.ClipHeightTo(0, kMaxHeight + header->height()); + scroll_view_->SetHeader(header); + scroll_view_->ClipHeightTo(0, kMaxHeight + header->height()); // Make sure the size is set such that no horizontal scrollbar gets shown. - scroll_view_.SetSize( + scroll_view_->SetSize( gfx::Size(kWidth + test_api.GetBaseScrollBar(VERTICAL)->GetThickness(), kMaxHeight + header->height())); @@ -1323,8 +1327,8 @@ TEST_F(ScrollViewTest, VerticalWithHeaderOverflowIndicators) { // The vertical scroll bar should be visible and the horizontal scroll bar // should not. - CheckScrollbarVisibility(scroll_view_, VERTICAL, true); - CheckScrollbarVisibility(scroll_view_, HORIZONTAL, false); + CheckScrollbarVisibility(scroll_view_.get(), VERTICAL, true); + CheckScrollbarVisibility(scroll_view_.get(), HORIZONTAL, false); // The overflow indicator on the bottom should be visible. EXPECT_TRUE(test_api.more_content_bottom()->visible()); @@ -1338,7 +1342,7 @@ TEST_F(ScrollViewTest, VerticalWithHeaderOverflowIndicators) { // Now scroll the view to someplace in the middle of the scrollable region. int offset = kMaxHeight * 2; - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset()); // At this point, only the overflow indicator on the bottom should be visible @@ -1353,7 +1357,7 @@ TEST_F(ScrollViewTest, VerticalWithHeaderOverflowIndicators) { // Finally scroll the view to end of the scrollable region. offset = test_api.GetBaseScrollBar(VERTICAL)->GetMaxPosition(); - scroll_view_.ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); + scroll_view_->ScrollToPosition(test_api.GetBaseScrollBar(VERTICAL), offset); EXPECT_EQ(gfx::ScrollOffset(0, offset), test_api.CurrentOffset()); // The overflow indicator on the bottom should not be visible now. diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc index 1515c68c296..5e7982dcdb9 100644 --- a/chromium/ui/views/controls/styled_label.cc +++ b/chromium/ui/views/controls/styled_label.cc @@ -13,6 +13,7 @@ #include "base/i18n/rtl.h" #include "base/strings/string_util.h" +#include "ui/accessibility/ax_node_data.h" #include "ui/gfx/font_list.h" #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" @@ -225,6 +226,15 @@ gfx::Insets StyledLabel::GetInsets() const { return insets; } +void StyledLabel::GetAccessibleNodeData(ui::AXNodeData* node_data) { + if (text_context_ == style::CONTEXT_DIALOG_TITLE) + node_data->role = ax::mojom::Role::kTitleBar; + else + node_data->role = ax::mojom::Role::kStaticText; + + node_data->SetName(text()); +} + gfx::Size StyledLabel::CalculatePreferredSize() const { return calculated_size_; } diff --git a/chromium/ui/views/controls/styled_label.h b/chromium/ui/views/controls/styled_label.h index 6dc8e734bdc..3268ad50e2c 100644 --- a/chromium/ui/views/controls/styled_label.h +++ b/chromium/ui/views/controls/styled_label.h @@ -127,6 +127,7 @@ 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; void Layout() override; diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc index 7c44a024a40..bc33e72aca4 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -286,9 +286,7 @@ void Tab::OnPaint(gfx::Canvas* canvas) { void Tab::GetAccessibleNodeData(ui::AXNodeData* data) { data->role = ax::mojom::Role::kTab; data->SetName(title()->text()); - data->AddState(ax::mojom::State::kSelectable); - if (selected()) - data->AddState(ax::mojom::State::kSelected); + data->AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, selected()); } bool Tab::HandleAccessibleAction(const ui::AXActionData& action_data) { diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc index 02bb3cf82c8..f499e202ee4 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc @@ -32,7 +32,10 @@ base::string16 DefaultTabTitle() { class TabbedPaneTest : public ViewsTestBase { public: - TabbedPaneTest() { + TabbedPaneTest() = default; + + void SetUp() override { + ViewsTestBase::SetUp(); tabbed_pane_ = std::make_unique<TabbedPane>(); tabbed_pane_->set_owned_by_client(); } @@ -226,8 +229,8 @@ TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) { EXPECT_EQ(ax::mojom::Role::kTab, data.role); EXPECT_EQ(DefaultTabTitle(), data.GetString16Attribute(ax::mojom::StringAttribute::kName)); - EXPECT_TRUE(data.HasState(ax::mojom::State::kSelectable)); - EXPECT_EQ(i == 0, data.HasState(ax::mojom::State::kSelected)); + EXPECT_EQ(i == 0, + data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)); } ui::AXActionData action; diff --git a/chromium/ui/views/controls/table/table_header.cc b/chromium/ui/views/controls/table/table_header.cc index dd72a413579..8c5b20fdfe8 100644 --- a/chromium/ui/views/controls/table/table_header.cc +++ b/chromium/ui/views/controls/table/table_header.cc @@ -242,7 +242,7 @@ bool TableHeader::StartResize(const ui::LocatedEvent& event) { resize_details_.reset(new ColumnResizeDetails); resize_details_->column_index = index; resize_details_->initial_x = event.root_location().x(); - resize_details_->initial_width = table_->visible_columns()[index].width; + resize_details_->initial_width = table_->GetVisibleColumn(index).width; return true; } @@ -253,9 +253,15 @@ void TableHeader::ContinueResize(const ui::LocatedEvent& event) { const int scale = base::i18n::IsRTL() ? -1 : 1; const int delta = scale * (event.root_location().x() - resize_details_->initial_x); + const TableView::VisibleColumn& column = + table_->GetVisibleColumn(resize_details_->column_index); + const int needed_for_title = + gfx::GetStringWidth(column.column.title, font_list_) + + 2 * kHorizontalPadding; table_->SetVisibleColumnWidth( resize_details_->column_index, - std::max(kMinColumnWidth, resize_details_->initial_width + delta)); + std::max({kMinColumnWidth, needed_for_title, + resize_details_->initial_width + delta})); } void TableHeader::ToggleSortOrder(const ui::LocatedEvent& event) { @@ -264,7 +270,7 @@ void TableHeader::ToggleSortOrder(const ui::LocatedEvent& event) { const int x = GetMirroredXInView(event.x()); const int index = GetClosestVisibleColumnIndex(table_, x); - const TableView::VisibleColumn& column(table_->visible_columns()[index]); + const TableView::VisibleColumn& column(table_->GetVisibleColumn(index)); if (x >= column.x && x < column.x + column.width && event.y() >= 0 && event.y() < height()) table_->ToggleSortOrder(index); @@ -277,7 +283,7 @@ int TableHeader::GetResizeColumn(int x) const { const int index = GetClosestVisibleColumnIndex(table_, x); DCHECK_NE(-1, index); - const TableView::VisibleColumn& column(table_->visible_columns()[index]); + const TableView::VisibleColumn& column(table_->GetVisibleColumn(index)); if (index > 0 && x >= column.x - kResizePadding && x <= column.x + kResizePadding) { return index - 1; diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc index a6c8234c5cb..d7f37cf103f 100644 --- a/chromium/ui/views/controls/table/table_view.cc +++ b/chromium/ui/views/controls/table/table_view.cc @@ -287,6 +287,11 @@ bool TableView::HasColumn(int id) const { return false; } +const TableView::VisibleColumn& TableView::GetVisibleColumn(int index) { + DCHECK(index >= 0 && index < static_cast<int>(visible_columns_.size())); + return visible_columns_[index]; +} + void TableView::SetVisibleColumnWidth(int index, int width) { DCHECK(index >= 0 && index < static_cast<int>(visible_columns_.size())); if (visible_columns_[index].width == width) @@ -458,9 +463,9 @@ void TableView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ax::mojom::Role::kRow; node_data->AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, selection_model_.active()); - if (selection_model_.IsSelected(selection_model_.active())) { - node_data->AddState(ax::mojom::State::kSelected); - } + node_data->AddBoolAttribute( + ax::mojom::BoolAttribute::kSelected, + selection_model_.IsSelected(selection_model_.active())); // Generate accessible name from column headers and selected cell text. std::vector<base::string16> name_parts; diff --git a/chromium/ui/views/controls/table/table_view.h b/chromium/ui/views/controls/table/table_view.h index b62e21ce5ef..eca7e3ed55f 100644 --- a/chromium/ui/views/controls/table/table_view.h +++ b/chromium/ui/views/controls/table/table_view.h @@ -139,6 +139,8 @@ class VIEWS_EXPORT TableView return visible_columns_; } + const VisibleColumn& GetVisibleColumn(int index); + // Sets the width of the column. |index| is in terms of |visible_columns_|. void SetVisibleColumnWidth(int index, int width); diff --git a/chromium/ui/views/controls/table/table_view_unittest.cc b/chromium/ui/views/controls/table/table_view_unittest.cc index e5f895ecfd9..88df9b24fd8 100644 --- a/chromium/ui/views/controls/table/table_view_unittest.cc +++ b/chromium/ui/views/controls/table/table_view_unittest.cc @@ -12,6 +12,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" +#include "ui/gfx/text_utils.h" #include "ui/views/controls/table/table_grouper.h" #include "ui/views/controls/table/table_header.h" #include "ui/views/controls/table/table_view_observer.h" @@ -46,6 +47,8 @@ class TableViewTestHelper { table_->SetSelectionModel(new_selection); } + const gfx::FontList& font_list() { return table_->font_list_; } + private: TableView* table_; @@ -197,7 +200,7 @@ std::string GetRowsInViewOrderAsString(TableView* table) { // Format row |i| like this: "[value1, value2, value3]" result += "["; for (size_t j = 0; j < table->visible_columns().size(); ++j) { - const ui::TableColumn& column = table->visible_columns()[j].column; + const ui::TableColumn& column = table->GetVisibleColumn(j).column; if (j != 0) result += ", "; // Comma between each value in the row. @@ -209,6 +212,27 @@ std::string GetRowsInViewOrderAsString(TableView* table) { return result; } +bool PressLeftMouseAt(views::View* target, const gfx::Point& point) { + const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, point, point, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + return target->OnMousePressed(pressed); +} + +void ReleaseLeftMouseAt(views::View* target, const gfx::Point& point) { + const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + target->OnMouseReleased(release); +} + +bool DragLeftMouseTo(views::View* target, const gfx::Point& point) { + const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, point, point, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + 0); + return target->OnMouseDragged(dragged); +} + } // namespace class TableViewTest : public ViewsTestBase { @@ -324,7 +348,7 @@ TEST_F(TableViewTest, ColumnVisibility) { // Hide the first column. table_->SetColumnVisibility(0, false); ASSERT_EQ(1u, helper_->visible_col_count()); - EXPECT_EQ(1, table_->visible_columns()[0].column.id); + EXPECT_EQ(1, table_->GetVisibleColumn(0).column.id); EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds())); // Hide the second column. @@ -334,40 +358,33 @@ TEST_F(TableViewTest, ColumnVisibility) { // Show the second column. table_->SetColumnVisibility(1, true); ASSERT_EQ(1u, helper_->visible_col_count()); - EXPECT_EQ(1, table_->visible_columns()[0].column.id); + EXPECT_EQ(1, table_->GetVisibleColumn(0).column.id); EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds())); // Show the first column. table_->SetColumnVisibility(0, true); ASSERT_EQ(2u, helper_->visible_col_count()); - EXPECT_EQ(1, table_->visible_columns()[0].column.id); - EXPECT_EQ(0, table_->visible_columns()[1].column.id); + EXPECT_EQ(1, table_->GetVisibleColumn(0).column.id); + EXPECT_EQ(0, table_->GetVisibleColumn(1).column.id); EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds())); } // Verifies resizing a column works. TEST_F(TableViewTest, Resize) { - const int x = table_->visible_columns()[0].width; + const int x = table_->GetVisibleColumn(0).width; EXPECT_NE(0, x); // Drag the mouse 1 pixel to the left. - const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0), - gfx::Point(x, 0), ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON); - helper_->header()->OnMousePressed(pressed); - const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0), - gfx::Point(x - 1, 0), ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, 0); - helper_->header()->OnMouseDragged(dragged); + PressLeftMouseAt(helper_->header(), gfx::Point(x, 0)); + DragLeftMouseTo(helper_->header(), gfx::Point(x - 1, 0)); // This should shrink the first column and pull the second column in. - EXPECT_EQ(x - 1, table_->visible_columns()[0].width); - EXPECT_EQ(x - 1, table_->visible_columns()[1].x); + EXPECT_EQ(x - 1, table_->GetVisibleColumn(0).width); + EXPECT_EQ(x - 1, table_->GetVisibleColumn(1).x); } // Verifies resizing a column works with a gesture. TEST_F(TableViewTest, ResizeViaGesture) { - const int x = table_->visible_columns()[0].width; + const int x = table_->GetVisibleColumn(0).width; EXPECT_NE(0, x); // Drag the mouse 1 pixel to the left. ui::GestureEvent scroll_begin( @@ -380,8 +397,27 @@ TEST_F(TableViewTest, ResizeViaGesture) { helper_->header()->OnGestureEvent(&scroll_update); // This should shrink the first column and pull the second column in. - EXPECT_EQ(x - 1, table_->visible_columns()[0].width); - EXPECT_EQ(x - 1, table_->visible_columns()[1].x); + EXPECT_EQ(x - 1, table_->GetVisibleColumn(0).width); + EXPECT_EQ(x - 1, table_->GetVisibleColumn(1).x); +} + +// Verifies resizing a column won't reduce the column width below the width of +// the column's title text. +TEST_F(TableViewTest, ResizeHonorsMinimum) { + TableViewTestHelper helper(table_); + const int x = table_->GetVisibleColumn(0).width; + EXPECT_NE(0, x); + + PressLeftMouseAt(helper_->header(), gfx::Point(x, 0)); + DragLeftMouseTo(helper_->header(), gfx::Point(20, 0)); + + int title_width = gfx::GetStringWidth( + table_->GetVisibleColumn(0).column.title, helper.font_list()); + EXPECT_LT(title_width, table_->GetVisibleColumn(0).width); + + int old_width = table_->GetVisibleColumn(0).width; + DragLeftMouseTo(helper_->header(), gfx::Point(old_width + 10, 0)); + EXPECT_EQ(old_width + 10, table_->GetVisibleColumn(0).width); } // Assertions for table sorting. @@ -519,20 +555,12 @@ TEST_F(TableViewTest, Sort) { TEST_F(TableViewTest, SortOnMouse) { EXPECT_TRUE(table_->sort_descriptors().empty()); - const int x = table_->visible_columns()[0].width / 2; + const int x = table_->GetVisibleColumn(0).width / 2; EXPECT_NE(0, x); // Press and release the mouse. - const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0), - gfx::Point(x, 0), ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON); // The header must return true, else it won't normally get the release. - EXPECT_TRUE(helper_->header()->OnMousePressed(pressed)); - const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(x, 0), - gfx::Point(x, 0), ui::EventTimeForNow(), - ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON); - helper_->header()->OnMouseReleased(release); + EXPECT_TRUE(PressLeftMouseAt(helper_->header(), gfx::Point(x, 0))); + ReleaseLeftMouseAt(helper_->header(), gfx::Point(x, 0)); ASSERT_EQ(1u, table_->sort_descriptors().size()); EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index d02b648714c..6cd683a7ee9 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -8,7 +8,6 @@ #include <utility> #include "base/command_line.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -58,7 +57,7 @@ #if defined(OS_WIN) #include "base/win/win_util.h" -#include "ui/base/win/osk_display_manager.h" +#include "ui/base/ime/win/osk_display_manager.h" #endif #if defined(OS_LINUX) && !defined(OS_CHROMEOS) @@ -75,6 +74,10 @@ #include "ui/wm/core/ime_util_chromeos.h" #endif +#if defined(OS_MACOSX) +#include "ui/base/cocoa/secure_password_input.h" +#endif + namespace views { namespace { @@ -262,6 +265,7 @@ Textfield::Textfield() scheduled_text_edit_command_(ui::TextEditCommand::INVALID_COMMAND), read_only_(false), default_width_in_chars_(0), + minimum_width_in_chars_(-1), use_default_text_color_(true), use_default_background_color_(true), use_default_selection_text_color_(true), @@ -319,9 +323,16 @@ Textfield::~Textfield() { } } -void Textfield::SetAssociatedLabel(Label* label) { - label_ax_id_ = label->GetViewAccessibility().GetUniqueId().Get(); - accessible_name_ = label->text(); +void Textfield::SetAssociatedLabel(View* labelling_view) { + DCHECK(labelling_view); + label_ax_id_ = labelling_view->GetViewAccessibility().GetUniqueId().Get(); + ui::AXNodeData node_data; + labelling_view->GetAccessibleNodeData(&node_data); + // TODO(aleventhal) automatically handle setting the name from the related + // label in view_accessibility and have it update the name if the text of the + // associated label changes. + SetAccessibleName( + node_data.GetString16Attribute(ax::mojom::StringAttribute::kName)); } void Textfield::SetReadOnly(bool read_only) { @@ -502,6 +513,16 @@ void Textfield::SetFontList(const gfx::FontList& font_list) { PreferredSizeChanged(); } +void Textfield::SetDefaultWidthInChars(int default_width) { + DCHECK_GE(default_width, 0); + default_width_in_chars_ = default_width; +} + +void Textfield::SetMinimumWidthInChars(int minimum_width) { + DCHECK_GE(minimum_width, -1); + minimum_width_in_chars_ = minimum_width; +} + base::string16 Textfield::GetPlaceholderText() const { return placeholder_text_; } @@ -515,7 +536,8 @@ void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment) { } void Textfield::ShowImeIfNeeded() { - if (enabled() && !read_only()) + // GetInputMethod() may return nullptr in tests. + if (enabled() && !read_only() && GetInputMethod()) GetInputMethod()->ShowImeIfNeeded(); } @@ -601,14 +623,24 @@ int Textfield::GetBaseline() const { } gfx::Size Textfield::CalculatePreferredSize() const { - const gfx::Insets& insets = GetInsets(); + DCHECK_GE(default_width_in_chars_, minimum_width_in_chars_); return gfx::Size( GetFontList().GetExpectedTextWidth(default_width_in_chars_) + - insets.width(), + GetInsets().width(), LayoutProvider::GetControlHeightForFont(style::CONTEXT_TEXTFIELD, GetTextStyle(), GetFontList())); } +gfx::Size Textfield::GetMinimumSize() const { + DCHECK_LE(minimum_width_in_chars_, default_width_in_chars_); + gfx::Size minimum_size = View::GetMinimumSize(); + if (minimum_width_in_chars_ >= 0) + minimum_size.set_width( + GetFontList().GetExpectedTextWidth(minimum_width_in_chars_) + + GetInsets().width()); + return minimum_size; +} + const char* Textfield::GetClassName() const { return kViewClassName; } @@ -1042,6 +1074,11 @@ void Textfield::OnPaint(gfx::Canvas* canvas) { } void Textfield::OnFocus() { +#if defined(OS_MACOSX) + if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) + password_input_enabler_.reset(new ui::ScopedPasswordInputEnabler()); +#endif // defined(OS_MACOSX) + GetRenderText()->set_focused(true); if (ShouldShowCursor()) { UpdateCursorViewPosition(); @@ -1085,6 +1122,10 @@ void Textfield::OnBlur() { FocusRing::Uninstall(this); SchedulePaint(); View::OnBlur(); + +#if defined(OS_MACOSX) + password_input_enabler_.reset(); +#endif // defined(OS_MACOSX) } gfx::Point Textfield::GetKeyboardContextMenuLocation() { @@ -1183,11 +1224,18 @@ bool Textfield::CanStartDragForView(View* sender, //////////////////////////////////////////////////////////////////////////////// // Textfield, WordLookupClient overrides: -bool Textfield::GetDecoratedWordAtPoint(const gfx::Point& point, - gfx::DecoratedText* decorated_word, - gfx::Point* baseline_point) { - return GetRenderText()->GetDecoratedWordAtPoint(point, decorated_word, - baseline_point); +bool Textfield::GetWordLookupDataAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) { + return GetRenderText()->GetWordLookupDataAtPoint(point, decorated_word, + baseline_point); +} + +bool Textfield::GetWordLookupDataFromSelection( + gfx::DecoratedText* decorated_text, + gfx::Point* baseline_point) { + return GetRenderText()->GetLookupDataForRange(GetRenderText()->selection(), + decorated_text, baseline_point); } //////////////////////////////////////////////////////////////////////////////// @@ -1284,10 +1332,20 @@ void Textfield::DestroyTouchSelection() { // Textfield, ui::SimpleMenuModel::Delegate overrides: bool Textfield::IsCommandIdChecked(int command_id) const { + if (text_services_context_menu_ && + text_services_context_menu_->SupportsCommand(command_id)) { + return text_services_context_menu_->IsCommandIdChecked(command_id); + } + return true; } bool Textfield::IsCommandIdEnabled(int command_id) const { + if (text_services_context_menu_ && + text_services_context_menu_->SupportsCommand(command_id)) { + return text_services_context_menu_->IsCommandIdEnabled(command_id); + } + return Textfield::IsTextEditCommandEnabled( GetTextEditCommandFromMenuCommand(command_id, HasSelection())); } @@ -1315,12 +1373,27 @@ bool Textfield::GetAcceleratorForCommandId(int command_id, *accelerator = ui::Accelerator(ui::VKEY_A, kPlatformModifier); 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; } } void Textfield::ExecuteCommand(int command_id, int event_flags) { + if (text_services_context_menu_ && + text_services_context_menu_->SupportsCommand(command_id)) { + text_services_context_menu_->ExecuteCommand(command_id); + return; + } + Textfield::ExecuteTextEditCommand( GetTextEditCommandFromMenuCommand(command_id, HasSelection())); } @@ -2080,7 +2153,17 @@ void Textfield::OnCaretBoundsChanged() { GetInputMethod()->OnCaretBoundsChanged(this); if (touch_selection_controller_) touch_selection_controller_->SelectionChanged(); - NotifyAccessibilityEvent(ax::mojom::Event::kTextSelectionChanged, true); + +#if defined(OS_MACOSX) + // On Mac, the context menu contains a look up item which displays the + // selected text. As such, the menu needs to be updated if the selection has + // changed. + context_menu_contents_.reset(); +#endif + + // Screen reader users don't expect notifications about unfocused textfields. + if (HasFocus()) + NotifyAccessibilityEvent(ax::mojom::Event::kTextSelectionChanged, true); } void Textfield::OnBeforeUserAction() { diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h index db30abdd006..9c4b28d689f 100644 --- a/chromium/ui/views/controls/textfield/textfield.h +++ b/chromium/ui/views/controls/textfield/textfield.h @@ -16,6 +16,7 @@ #include "base/memory/weak_ptr.h" #include "base/strings/string16.h" #include "base/time/time.h" +#include "build/build_config.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/text_input_type.h" @@ -38,6 +39,12 @@ namespace base { class TimeDelta; } +#if defined(OS_MACOSX) +namespace ui { +class ScopedPasswordInputEnabler; +} +#endif // defined(OS_MACOSX) + namespace views { class Label; @@ -83,8 +90,9 @@ class VIEWS_EXPORT Textfield : public View, // features. The flags is the bit map of ui::TextInputFlags. void SetTextInputFlags(int flags); - // Gets the text for the Textfield. Call sites should take care to not reveal - // the text for a password textfield. + // Gets the text for the Textfield. + // NOTE: Call sites should take care to not reveal the text for a password + // textfield. const base::string16& text() const { return model_->text(); } // Sets the text currently displayed in the Textfield. This doesn't @@ -102,8 +110,9 @@ class VIEWS_EXPORT Textfield : public View, // changes. void InsertOrReplaceText(const base::string16& new_text); - // Returns the text that is currently selected. Call sites should take care to - // not reveal the text for a password textfield. + // Returns the text that is currently selected. + // NOTE: Call sites should take care to not reveal the text for a password + // textfield. base::string16 GetSelectedText() const; // Select the entire text range. If |reversed| is true, the range will end at @@ -154,9 +163,10 @@ class VIEWS_EXPORT Textfield : public View, void SetFontList(const gfx::FontList& font_list); // Sets the default width of the text control. See default_width_in_chars_. - void set_default_width_in_chars(int default_width) { - default_width_in_chars_ = default_width; - } + void SetDefaultWidthInChars(int default_width); + + // Sets the minimum width of the text control. See minimum_width_in_chars_. + void SetMinimumWidthInChars(int minimum_width); // Sets the text to display when empty. void set_placeholder_text(const base::string16& text) { @@ -224,10 +234,11 @@ class VIEWS_EXPORT Textfield : public View, // label, use SetAssociatedLabel() instead. void SetAccessibleName(const base::string16& name); - // If the accessible name should be the same as the label text, use this. It - // will set both the accessible label relationship and the accessible name - // from the contents of the label. - void SetAssociatedLabel(Label* label); + // If the accessible name should be the same as the labelling view's text, + // use this. It will set the accessible label relationship and copy the + // accessible name from the labelling views's accessible name. Any view with + // an accessible name can be used, typically a Label, StyledLabel or Link. + void SetAssociatedLabel(View* labelling_view); // Set extra spacing placed between glyphs; used for obscured text styling. void SetGlyphSpacing(int spacing); @@ -235,6 +246,7 @@ class VIEWS_EXPORT Textfield : public View, // View overrides: int GetBaseline() const override; gfx::Size CalculatePreferredSize() const override; + gfx::Size GetMinimumSize() const override; const char* GetClassName() const override; void SetBorder(std::unique_ptr<Border> b) override; gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; @@ -287,9 +299,11 @@ class VIEWS_EXPORT Textfield : public View, const gfx::Point& p) override; // WordLookupClient overrides: - bool GetDecoratedWordAtPoint(const gfx::Point& point, - gfx::DecoratedText* decorated_word, - gfx::Point* baseline_point) override; + bool GetWordLookupDataAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) override; + bool GetWordLookupDataFromSelection(gfx::DecoratedText* decorated_text, + gfx::Point* baseline_point) override; // SelectionControllerDelegate overrides: bool HasTextBeingDragged() const override; @@ -483,9 +497,16 @@ class VIEWS_EXPORT Textfield : public View, bool read_only_; // The default number of average characters for the width of this text field. - // This will be reported as the "desired size". Defaults to 0. + // This will be reported as the "desired size". Must be set to >= + // minimum_width_in_chars_. Defaults to 0. int default_width_in_chars_; + // The minimum allowed width of this text field in average characters. This + // will be reported as the minimum size. Must be set to <= + // default_width_in_chars_. Setting this to -1 will cause GetMinimumSize() to + // return View::GetMinimumSize(). Defaults to -1. + int minimum_width_in_chars_; + // Flags indicating whether various system colors should be used, and if not, // what overriding color values should be used instead. bool use_default_text_color_; @@ -575,6 +596,11 @@ class VIEWS_EXPORT Textfield : public View, // View containing the text cursor. View cursor_view_; +#if defined(OS_MACOSX) + // Used to track active password input sessions. + std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_; +#endif // defined(OS_MACOSX) + // 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 e06e12df73e..7b06af51838 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.cc +++ b/chromium/ui/views/controls/textfield/textfield_model.cc @@ -8,7 +8,6 @@ #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -16,6 +15,7 @@ #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/gfx/range/range.h" #include "ui/gfx/utf16_indexing.h" +#include "ui/views/style/platform_style.h" namespace views { @@ -258,7 +258,7 @@ namespace { gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) { for (size_t i = 0; i < composition.ime_text_spans.size(); ++i) { const ui::ImeTextSpan& underline = composition.ime_text_spans[i]; - if (underline.thick) + if (underline.thickness == ui::ImeTextSpan::Thickness::kThick) return gfx::Range(underline.start_offset, underline.end_offset); } return gfx::Range::InvalidRange(); @@ -406,10 +406,8 @@ bool TextfieldModel::Backspace(bool add_to_kill_buffer) { } size_t cursor_position = GetCursorPosition(); if (cursor_position > 0) { - // Delete one code point, which may be two UTF-16 words. - size_t previous_grapheme_index = - gfx::UTF16OffsetToIndex(text(), cursor_position, -1); - gfx::Range range_to_delete(cursor_position, previous_grapheme_index); + gfx::Range range_to_delete( + PlatformStyle::RangeToDeleteBackwards(text(), cursor_position)); if (add_to_kill_buffer) SetKillBuffer(GetTextFromRange(range_to_delete)); ExecuteAndRecordDelete(range_to_delete, true); @@ -658,9 +656,10 @@ void TextfieldModel::SetCompositionText( base::string16 new_text = text(); render_text_->SetText(new_text.insert(cursor, composition.text)); composition_range_ = gfx::Range(cursor, cursor + composition.text.length()); - // Don't render transparent IME spans. + // Don't render IME spans with thickness "kNone". if (composition.ime_text_spans.size() > 0 && - composition.ime_text_spans[0].underline_color != 0) + composition.ime_text_spans[0].thickness != + ui::ImeTextSpan::Thickness::kNone) render_text_->SetCompositionRange(composition_range_); else render_text_->SetCompositionRange(gfx::Range::InvalidRange()); diff --git a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc index d35aba0f62a..c35b12260bf 100644 --- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc @@ -233,6 +233,38 @@ TEST_F(TextfieldModelTest, EditString_ComplexScript) { EXPECT_TRUE(model.Backspace()); EXPECT_EQ(base::WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9"), model.text()); + + // Halfwidth katakana ダ: + // "HALFWIDTH KATAKANA LETTER TA" + "HALFWIDTH KATAKANA VOICED SOUND MARK" + // ("ABC" prefix as sanity check that the entire string isn't deleted). + model.SetText(base::WideToUTF16(L"ABC\xFF80\xFF9E")); + MoveCursorTo(model, model.text().length()); + model.Backspace(); +#if defined(OS_MACOSX) + // On Mac, the entire cluster should be deleted to match + // NSTextField behavior. + EXPECT_EQ(base::WideToUTF16(L"ABC"), model.text()); + EXPECT_EQ(3U, model.GetCursorPosition()); +#else + EXPECT_EQ(base::WideToUTF16(L"ABC\xFF80"), model.text()); + EXPECT_EQ(4U, model.GetCursorPosition()); +#endif + + // Emoji with Fitzpatrick modifier: + // 'BOY' + 'EMOJI MODIFIER FITZPATRICK TYPE-5' + model.SetText(base::WideToUTF16(L"\U0001F466\U0001F3FE")); + MoveCursorTo(model, model.text().length()); + model.Backspace(); +#if defined(OS_MACOSX) + // On Mac, the entire emoji should be deleted to match NSTextField + // behavior. + EXPECT_EQ(base::WideToUTF16(L""), model.text()); + EXPECT_EQ(0U, model.GetCursorPosition()); +#else + // https://crbug.com/829040 + EXPECT_EQ(base::WideToUTF16(L"\U0001F466"), model.text()); + EXPECT_EQ(2U, model.GetCursorPosition()); +#endif } TEST_F(TextfieldModelTest, EmptyString) { @@ -973,7 +1005,8 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { ui::CompositionText composition; composition.text = base::ASCIIToUTF16("678"); - composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 3, 0, false)); + composition.ime_text_spans.push_back( + ui::ImeTextSpan(0, 3, ui::ImeTextSpan::Thickness::kThin)); // Cursor should be at the end of composition when characters are just typed. composition.selection = gfx::Range(3, 3); @@ -988,8 +1021,10 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { // Restart composition with targeting "67" in "678". composition.selection = gfx::Range(1, 3); composition.ime_text_spans.clear(); - composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 2, 0, true)); - composition.ime_text_spans.push_back(ui::ImeTextSpan(2, 3, 0, false)); + composition.ime_text_spans.push_back( + ui::ImeTextSpan(0, 2, ui::ImeTextSpan::Thickness::kThick)); + composition.ime_text_spans.push_back( + ui::ImeTextSpan(2, 3, ui::ImeTextSpan::Thickness::kThin)); model.SetCompositionText(composition); EXPECT_TRUE(model.HasCompositionText()); EXPECT_TRUE(model.HasSelection()); @@ -1581,7 +1616,8 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { ui::CompositionText composition; composition.text = base::ASCIIToUTF16("abc"); - composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 3, 0, false)); + composition.ime_text_spans.push_back( + ui::ImeTextSpan(0, 3, ui::ImeTextSpan::Thickness::kThin)); composition.selection = gfx::Range(2, 3); model.SetText(base::ASCIIToUTF16("ABCDE")); diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index 5cfb980672f..775c61fd31d 100644 --- a/chromium/ui/views/controls/textfield/textfield_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -19,6 +19,7 @@ #include "base/strings/string16.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "ui/accessibility/ax_node_data.h" #include "ui/aura/window.h" @@ -30,6 +31,8 @@ #include "ui/base/ime/input_method_factory.h" #include "ui/base/ime/text_edit_commands.h" #include "ui/base/ime/text_input_client.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/ui_base_features.h" #include "ui/base/ui_base_switches.h" #include "ui/base/ui_base_switches_util.h" #include "ui/events/event.h" @@ -67,6 +70,10 @@ #include "ui/wm/core/ime_util_chromeos.h" #endif +#if defined(OS_MACOSX) +#include "ui/base/cocoa/secure_password_input.h" +#endif + using base::ASCIIToUTF16; using base::UTF8ToUTF16; using base::WideToUTF16; @@ -83,8 +90,6 @@ class MockInputMethod : public ui::InputMethodBase { ~MockInputMethod() override; // Overridden from InputMethod: - bool OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) override; ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* key) override; void OnTextInputTypeChanged(const ui::TextInputClient* client) override; void OnCaretBoundsChanged(const ui::TextInputClient* client) override {} @@ -144,13 +149,6 @@ MockInputMethod::MockInputMethod() MockInputMethod::~MockInputMethod() { } -bool MockInputMethod::OnUntranslatedIMEMessage(const base::NativeEvent& event, - NativeEventResult* result) { - if (result) - *result = NativeEventResult(); - return false; -} - ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key) { // On Mac, emulate InputMethodMac behavior for character events. Composition // still needs to be mocked, since it's not possible to generate test events @@ -286,6 +284,15 @@ class TestTextfield : public views::Textfield { event_flags_ = 0; } + void OnAccessibilityEvent(ax::mojom::Event event_type) override { + if (event_type == ax::mojom::Event::kTextSelectionChanged) + ++accessibility_selection_fired_count_; + } + + int GetAccessibilitySelectionFiredCount() { + return accessibility_selection_fired_count_; + } + private: // views::View override: void OnKeyEvent(ui::KeyEvent* event) override { @@ -310,6 +317,7 @@ class TestTextfield : public views::Textfield { bool key_handled_ = false; bool key_received_ = false; int event_flags_ = 0; + int accessibility_selection_fired_count_ = 0; base::WeakPtrFactory<TestTextfield> weak_ptr_factory_{this}; @@ -688,21 +696,37 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { const bool is_all_selected = !text.empty() && textfield_->GetSelectedRange().length() == text.length(); - EXPECT_EQ(can_undo, menu->IsEnabledAt(0 /* UNDO */)); - EXPECT_TRUE(menu->IsEnabledAt(1 /* Separator */)); - EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(2 /* CUT */)); - EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(3 /* COPY */)); + int menu_index = 0; +#if defined(OS_MACOSX) + // On Mac, the Look Up item should appear at the top of the menu if the + // textfield has a selection. + if (textfield_has_selection) { + EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* LOOK UP */)); + EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */)); + } + EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* EMOJI */)); + EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */)); +#endif + + EXPECT_EQ(can_undo, menu->IsEnabledAt(menu_index++ /* UNDO */)); + EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */)); + EXPECT_EQ(textfield_has_selection, + menu->IsEnabledAt(menu_index++ /* CUT */)); + EXPECT_EQ(textfield_has_selection, + menu->IsEnabledAt(menu_index++ /* COPY */)); EXPECT_NE(GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE).empty(), - menu->IsEnabledAt(4 /* PASTE */)); - EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(5 /* DELETE */)); - EXPECT_TRUE(menu->IsEnabledAt(6 /* Separator */)); - EXPECT_EQ(!is_all_selected, menu->IsEnabledAt(7 /* SELECT ALL */)); + menu->IsEnabledAt(menu_index++ /* PASTE */)); + EXPECT_EQ(textfield_has_selection, + menu->IsEnabledAt(menu_index++ /* DELETE */)); + EXPECT_TRUE(menu->IsEnabledAt(menu_index++ /* Separator */)); + EXPECT_EQ(!is_all_selected, + menu->IsEnabledAt(menu_index++ /* SELECT ALL */)); } - void PressMouseButton(ui::EventFlags mouse_button_flags, int extra_flags) { + void PressMouseButton(ui::EventFlags mouse_button_flags) { ui::MouseEvent press(ui::ET_MOUSE_PRESSED, mouse_position_, mouse_position_, ui::EventTimeForNow(), mouse_button_flags, - mouse_button_flags | extra_flags); + mouse_button_flags); textfield_->OnMousePressed(press); } @@ -713,21 +737,21 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { textfield_->OnMouseReleased(release); } - void PressLeftMouseButton(int extra_flags = 0) { - PressMouseButton(ui::EF_LEFT_MOUSE_BUTTON, extra_flags); + void PressLeftMouseButton() { + PressMouseButton(ui::EF_LEFT_MOUSE_BUTTON); } void ReleaseLeftMouseButton() { ReleaseMouseButton(ui::EF_LEFT_MOUSE_BUTTON); } - void ClickLeftMouseButton(int extra_flags = 0) { - PressLeftMouseButton(extra_flags); + void ClickLeftMouseButton() { + PressLeftMouseButton(); ReleaseLeftMouseButton(); } void ClickRightMouseButton() { - PressMouseButton(ui::EF_RIGHT_MOUSE_BUTTON, 0); + PressMouseButton(ui::EF_RIGHT_MOUSE_BUTTON); ReleaseMouseButton(ui::EF_RIGHT_MOUSE_BUTTON); } @@ -1516,6 +1540,9 @@ TEST_F(TextfieldTest, FocusTraversalTest) { TEST_F(TextfieldTest, ContextMenuDisplayTest) { InitTextfield(); + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kEnableEmojiContextMenu); + EXPECT_TRUE(textfield_->context_menu_controller()); textfield_->SetText(ASCIIToUTF16("hello world")); ui::Clipboard::GetForCurrentThread()->Clear(ui::CLIPBOARD_TYPE_COPY_PASTE); @@ -1545,7 +1572,7 @@ TEST_F(TextfieldTest, DoubleAndTripleClickTest) { MoveMouseTo(gfx::Point(0, GetCursorYForTesting())); ClickLeftMouseButton(); EXPECT_TRUE(textfield_->GetSelectedText().empty()); - ClickLeftMouseButton(ui::EF_IS_DOUBLE_CLICK); + ClickLeftMouseButton(); EXPECT_STR_EQ("hello", textfield_->GetSelectedText()); // Test for triple click. @@ -3436,6 +3463,93 @@ TEST_F(TextfieldTest, TextServicesContextMenuTextDirectionTest) { EXPECT_TRUE(test_api_->IsTextDirectionCheckedInContextMenu( base::i18n::TextDirection::RIGHT_TO_LEFT)); } + +// Tests to see if the look up item is updated when the textfield's selected +// text has changed. +TEST_F(TextfieldTest, LookUpItemUpdate) { + InitTextfield(); + EXPECT_TRUE(textfield_->context_menu_controller()); + + const base::string16 kTextOne = ASCIIToUTF16("crake"); + textfield_->SetText(kTextOne); + textfield_->SelectAll(false); + + ui::MenuModel* context_menu = GetContextMenuModel(); + EXPECT_TRUE(context_menu); + EXPECT_GT(context_menu->GetItemCount(), 0); + EXPECT_EQ(context_menu->GetLabelAt(0), + l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, kTextOne)); + + const base::string16 kTextTwo = ASCIIToUTF16("rail"); + textfield_->SetText(kTextTwo); + textfield_->SelectAll(false); + + context_menu = GetContextMenuModel(); + EXPECT_TRUE(context_menu); + EXPECT_GT(context_menu->GetItemCount(), 0); + EXPECT_EQ(context_menu->GetLabelAt(0), + l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, kTextTwo)); +} + +// Tests to see if the look up item is hidden for password fields. +TEST_F(TextfieldTest, LookUpPassword) { + InitTextfield(); + textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + + const base::string16 kText = ASCIIToUTF16("Willie Wagtail"); + + textfield_->SetText(kText); + textfield_->SelectAll(false); + + ui::MenuModel* context_menu = GetContextMenuModel(); + EXPECT_TRUE(context_menu); + EXPECT_GT(context_menu->GetItemCount(), 0); + EXPECT_NE(context_menu->GetCommandIdAt(0), IDS_CONTENT_CONTEXT_LOOK_UP); + EXPECT_NE(context_menu->GetLabelAt(0), + l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, kText)); +} + +TEST_F(TextfieldTest, SecurePasswordInput) { + InitTextfield(); + ASSERT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled()); + + // Shouldn't enable secure input if it's not a password textfield. + textfield_->OnFocus(); + EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled()); + + textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD); + + // Single matched calls immediately update IsPasswordInputEnabled(). + textfield_->OnFocus(); + EXPECT_TRUE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled()); + + textfield_->OnBlur(); + EXPECT_FALSE(ui::ScopedPasswordInputEnabler::IsPasswordInputEnabled()); +} #endif // defined(OS_MACOSX) +TEST_F(TextfieldTest, AccessibilitySelectionEvents) { + const std::string& kText = "abcdef"; + InitTextfield(); + textfield_->SetText(ASCIIToUTF16(kText)); + EXPECT_TRUE(textfield_->HasFocus()); + int previous_selection_fired_count = + textfield_->GetAccessibilitySelectionFiredCount(); + textfield_->SelectAll(false); + EXPECT_LT(previous_selection_fired_count, + textfield_->GetAccessibilitySelectionFiredCount()); + previous_selection_fired_count = + textfield_->GetAccessibilitySelectionFiredCount(); + + // No selection event when textfield blurred, even though text is + // deselected. + widget_->GetFocusManager()->ClearFocus(); + EXPECT_FALSE(textfield_->HasFocus()); + textfield_->ClearSelection(); + EXPECT_FALSE(textfield_->HasSelection()); + // Has not changed. + EXPECT_EQ(previous_selection_fired_count, + textfield_->GetAccessibilitySelectionFiredCount()); +} + } // namespace views diff --git a/chromium/ui/views/controls/tree/tree_view.cc b/chromium/ui/views/controls/tree/tree_view.cc index f5709270c99..4b8d78b239a 100644 --- a/chromium/ui/views/controls/tree/tree_view.cc +++ b/chromium/ui/views/controls/tree/tree_view.cc @@ -7,7 +7,6 @@ #include <algorithm> #include "base/i18n/rtl.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "build/build_config.h" #include "components/vector_icons/vector_icons.h" @@ -427,8 +426,16 @@ void TreeView::ShowContextMenu(const gfx::Point& p, void TreeView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ax::mojom::Role::kTree; node_data->SetRestriction(ax::mojom::Restriction::kReadOnly); - if (!selected_node_) + // TODO(aleventhal): The tree view accessibility implementation is misusing + // the name field. It should really be using selection events for the + // currently selected item. The name field should be for for the label + // if there is one, otherwise something that would work in place of a label. + // See http://crbug.com/811277. + + if (!selected_node_) { + node_data->SetNameExplicitlyEmpty(); return; + } // Get selected item info. node_data->role = ax::mojom::Role::kTreeItem; diff --git a/chromium/ui/views/controls/tree/tree_view_unittest.cc b/chromium/ui/views/controls/tree/tree_view_unittest.cc index 02d3ef1c246..fa4c7bf7b0c 100644 --- a/chromium/ui/views/controls/tree/tree_view_unittest.cc +++ b/chromium/ui/views/controls/tree/tree_view_unittest.cc @@ -7,7 +7,6 @@ #include <string> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/models/tree_node_model.h" 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 2388b27653b..bf411354261 100644 --- a/chromium/ui/views/controls/views_text_services_context_menu.h +++ b/chromium/ui/views/controls/views_text_services_context_menu.h @@ -34,6 +34,14 @@ class ViewsTextServicesContextMenu { VIEWS_EXPORT static bool IsTextDirectionCheckedForTesting( ViewsTextServicesContextMenu* menu, base::i18n::TextDirection direction); + + // Returns true if the given |command_id| is handled by the menu. + virtual bool SupportsCommand(int command_id) const = 0; + + // Methods associated with SimpleMenuModel::Delegate. + virtual bool IsCommandIdChecked(int command_id) const = 0; + virtual bool IsCommandIdEnabled(int command_id) const = 0; + virtual void ExecuteCommand(int command_id) = 0; }; } // namespace views diff --git a/chromium/ui/views/controls/views_text_services_context_menu_mac.mm b/chromium/ui/views/controls/views_text_services_context_menu_mac.mm index be96d8ac4ee..0c4cd260bed 100644 --- a/chromium/ui/views/controls/views_text_services_context_menu_mac.mm +++ b/chromium/ui/views/controls/views_text_services_context_menu_mac.mm @@ -4,10 +4,21 @@ #include "ui/views/controls/views_text_services_context_menu.h" -#include "base/memory/ptr_util.h" +#import <Cocoa/Cocoa.h> + +#include "base/feature_list.h" #include "ui/base/cocoa/text_services_context_menu.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/base/models/simple_menu_model.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/ui_base_features.h" +#include "ui/gfx/decorated_text.h" +#import "ui/gfx/decorated_text_mac.h" +#include "ui/resources/grit/ui_resources.h" +#include "ui/strings/grit/ui_strings.h" #include "ui/views/controls/textfield/textfield.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" namespace views { @@ -16,21 +27,78 @@ namespace { // This class serves as a bridge to TextServicesContextMenu to add and handle // text service items in the context menu. The items include Speech, Look Up // and BiDi. -// TODO (spqchan): Add Look Up and BiDi. class ViewsTextServicesContextMenuMac : public ViewsTextServicesContextMenu, public ui::TextServicesContextMenu::Delegate { public: ViewsTextServicesContextMenuMac(ui::SimpleMenuModel* menu, Textfield* client) : text_services_menu_(this), client_(client) { + // The index to use when inserting items into the menu. + int index = 0; + + base::string16 text = GetSelectedText(); + if (!text.empty()) { + menu->InsertItemAt( + index++, IDS_CONTENT_CONTEXT_LOOK_UP, + l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_LOOK_UP, text)); + menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR); + } + if (base::FeatureList::IsEnabled(features::kEnableEmojiContextMenu)) { + menu->InsertItemWithStringIdAt(index++, IDS_CONTENT_CONTEXT_EMOJI, + IDS_CONTENT_CONTEXT_EMOJI); + menu->InsertSeparatorAt(index++, ui::NORMAL_SEPARATOR); + } text_services_menu_.AppendToContextMenu(menu); text_services_menu_.AppendEditableItems(menu); } ~ViewsTextServicesContextMenuMac() override {} + // ViewsTextServicesContextMenu: + bool SupportsCommand(int command_id) const override { + return text_services_menu_.SupportsCommand(command_id) || + command_id == IDS_CONTENT_CONTEXT_EMOJI || + command_id == IDS_CONTENT_CONTEXT_LOOK_UP; + } + + bool IsCommandIdChecked(int command_id) const override { + DCHECK_EQ(IDS_CONTENT_CONTEXT_LOOK_UP, command_id); + return false; + } + + bool IsCommandIdEnabled(int command_id) const override { + if (text_services_menu_.SupportsCommand(command_id)) + return text_services_menu_.IsCommandIdEnabled(command_id); + + switch (command_id) { + case IDS_CONTENT_CONTEXT_EMOJI: + return true; + + case IDS_CONTENT_CONTEXT_LOOK_UP: + return true; + + default: + return false; + } + } + + void ExecuteCommand(int command_id) override { + switch (command_id) { + case IDS_CONTENT_CONTEXT_EMOJI: + [NSApp orderFrontCharacterPalette:nil]; + break; + + case IDS_CONTENT_CONTEXT_LOOK_UP: + LookUpInDictionary(); + break; + } + } + // TextServicesContextMenu::Delegate: base::string16 GetSelectedText() const override { + if (client_->GetTextInputType() == ui::TEXT_INPUT_TYPE_PASSWORD) + return base::string16(); + return client_->GetSelectedText(); } @@ -55,6 +123,24 @@ class ViewsTextServicesContextMenuMac } private: + // Handler for the "Look Up" menu item. + void LookUpInDictionary() { + gfx::Point baseline_point; + gfx::DecoratedText text; + if (client_->GetWordLookupDataFromSelection(&text, &baseline_point)) { + Widget* widget = client_->GetWidget(); + gfx::NativeView view = widget->GetNativeView(); + views::View::ConvertPointToTarget(client_, widget->GetRootView(), + &baseline_point); + + NSPoint lookup_point = NSMakePoint( + baseline_point.x(), NSHeight([view frame]) - baseline_point.y()); + [view showDefinitionForAttributedString: + gfx::GetAttributedStringFromDecoratedText(text) + atPoint:lookup_point]; + } + } + // Appends and handles the text service menu. ui::TextServicesContextMenu text_services_menu_; @@ -80,4 +166,4 @@ bool ViewsTextServicesContextMenu::IsTextDirectionCheckedForTesting( return static_cast<views::ViewsTextServicesContextMenuMac*>(menu) ->IsTextDirectionChecked(direction); } -} // namespace views
\ No newline at end of file +} // 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 ba3480d6440..ef8b793f745 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.cc +++ b/chromium/ui/views/controls/webview/web_dialog_view.cc @@ -28,7 +28,7 @@ using content::NativeWebKeyboardEvent; using content::WebContents; using content::WebUIMessageHandler; using ui::WebDialogDelegate; -using ui::WebDialogUI; +using ui::WebDialogUIBase; using ui::WebDialogWebContentsDelegate; namespace views { @@ -365,7 +365,7 @@ void WebDialogView::InitDialog() { // Set the delegate. This must be done before loading the page. See // the comment above WebDialogUI in its header file for why. - WebDialogUI::SetDelegate(web_contents, this); + WebDialogUIBase::SetDelegate(web_contents, this); web_view_->LoadInitialURL(GetDialogContentURL()); } diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc index 5144b14e415..4ec50f3b056 100644 --- a/chromium/ui/views/controls/webview/webview.cc +++ b/chromium/ui/views/controls/webview/webview.cc @@ -147,6 +147,10 @@ void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) { // Reset the native view size. holder_->SetNativeViewSize(gfx::Size()); holder_->SetBoundsRect(holder_bounds); + if (is_letterboxing_) { + is_letterboxing_ = false; + OnLetterboxingChanged(); + } return; } @@ -165,6 +169,10 @@ void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) { static_cast<int>(x / capture_size.height()), holder_bounds.height())); } + if (!is_letterboxing_) { + is_letterboxing_ = true; + OnLetterboxingChanged(); + } holder_->SetNativeViewSize(capture_size); holder_->SetBoundsRect(holder_bounds); } diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h index afdd0a03938..99affc147bc 100644 --- a/chromium/ui/views/controls/webview/webview.h +++ b/chromium/ui/views/controls/webview/webview.h @@ -103,6 +103,10 @@ class WEBVIEW_EXPORT WebView : public View, // Called when the web contents is successfully attached. virtual void OnWebContentsAttached() {} + // Called when letterboxing (scaling the native view to preserve aspect + // ratio) is enabled or disabled. + virtual void OnLetterboxingChanged() {} + bool is_letterboxing() const { return is_letterboxing_; } // Overridden from View: void OnBoundsChanged(const gfx::Rect& previous_bounds) override; @@ -162,6 +166,9 @@ class WEBVIEW_EXPORT WebView : public View, // view instead of the normal WebContentsView render view. Note: This will be // false in the case of non-Flash fullscreen. bool is_embedding_fullscreen_widget_; + // Set to true when |holder_| is letterboxed (scaled to be smaller than this + // view, to preserve its aspect ratio). + bool is_letterboxing_ = false; content::BrowserContext* browser_context_; bool allow_accelerators_; View* crashed_overlay_view_ = nullptr; diff --git a/chromium/ui/views/controls/webview/webview_unittest.cc b/chromium/ui/views/controls/webview/webview_unittest.cc index b3c86732c50..b679af9d8d2 100644 --- a/chromium/ui/views/controls/webview/webview_unittest.cc +++ b/chromium/ui/views/controls/webview/webview_unittest.cc @@ -8,10 +8,13 @@ #include <memory> +#include "base/command_line.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/common/content_switches.h" +#include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/web_contents_tester.h" @@ -151,6 +154,9 @@ class WebViewUnitTest : public views::test::WidgetTest { // dependencies from content. SetBrowserClientForTesting(&test_browser_client_); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kDisableBackgroundingOccludedWindowsForTesting); + // Create a top level widget and add a child, and give it a WebView as a // child. top_level_widget_ = CreateTopLevelFramelessPlatformWidget(); @@ -186,6 +192,10 @@ class WebViewUnitTest : public views::test::WidgetTest { } private: + // TODO(lukasza): https://crbug.com/832100: Move the factory into + // TestingProfile, so individual tests don't need to worry about it. + content::ScopedMockRenderProcessHostFactory process_factory_; + content::TestBrowserThreadBundle test_browser_thread_bundle_; std::unique_ptr<content::TestBrowserContext> browser_context_; content::TestContentBrowserClient test_browser_client_; diff --git a/chromium/ui/views/examples/box_layout_example.cc b/chromium/ui/views/examples/box_layout_example.cc index ef7c9146f57..2c4fba15fb2 100644 --- a/chromium/ui/views/examples/box_layout_example.cc +++ b/chromium/ui/views/examples/box_layout_example.cc @@ -6,7 +6,6 @@ #include <vector> -#include "base/memory/ptr_util.h" #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -193,7 +192,7 @@ void ChildPanel::ContentsChanged(Textfield* sender, Textfield* ChildPanel::CreateTextfield() { Textfield* textfield = new Textfield(); - textfield->set_default_width_in_chars(3); + textfield->SetDefaultWidthInChars(3); textfield->SizeToPreferredSize(); textfield->SetText(base::ASCIIToUTF16("0")); textfield->set_controller(this); @@ -242,7 +241,7 @@ Textfield* BoxLayoutExample::CreateRawTextfield(int& horizontal_pos, bool add) { Textfield* text_field = new Textfield(); text_field->SetPosition(gfx::Point(horizontal_pos, vertical_pos)); - text_field->set_default_width_in_chars(3); + text_field->SetDefaultWidthInChars(3); text_field->SetTextInputType(ui::TEXT_INPUT_TYPE_NUMBER); text_field->SizeToPreferredSize(); text_field->SetText(base::ASCIIToUTF16("0")); diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index 807ba934af9..dd63355096e 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -67,10 +67,12 @@ int main(int argc, char** argv) { gl::init::InitializeGLOneOff(); // The ContextFactory must exist before any Compositors are created. - viz::HostFrameSinkManager host_frame_sink_manager_; - viz::FrameSinkManagerImpl frame_sink_manager_; + viz::HostFrameSinkManager host_frame_sink_manager; + viz::FrameSinkManagerImpl frame_sink_manager; + host_frame_sink_manager.SetLocalManager(&frame_sink_manager); + frame_sink_manager.SetLocalClient(&host_frame_sink_manager); auto context_factory = std::make_unique<ui::InProcessContextFactory>( - &host_frame_sink_manager_, &frame_sink_manager_); + &host_frame_sink_manager, &frame_sink_manager); context_factory->set_use_test_surface(false); base::test::ScopedTaskEnvironment scoped_task_environment( diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc index e6b46660e7c..b903e478740 100644 --- a/chromium/ui/views/examples/examples_window.cc +++ b/chromium/ui/views/examples/examples_window.cc @@ -10,7 +10,6 @@ #include <utility> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chromium/ui/views/examples/examples_window_with_content.cc b/chromium/ui/views/examples/examples_window_with_content.cc index bf87c71eac8..1133e88fa9a 100644 --- a/chromium/ui/views/examples/examples_window_with_content.cc +++ b/chromium/ui/views/examples/examples_window_with_content.cc @@ -8,7 +8,6 @@ #include <utility> #include <vector> -#include "base/memory/ptr_util.h" #include "content/public/browser/browser_context.h" #include "ui/views/examples/webview_example.h" diff --git a/chromium/ui/views/examples/text_example.cc b/chromium/ui/views/examples/text_example.cc index 8b95cf23acb..17439cc8bc6 100644 --- a/chromium/ui/views/examples/text_example.cc +++ b/chromium/ui/views/examples/text_example.cc @@ -5,7 +5,6 @@ #include "ui/views/examples/text_example.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font_list.h" diff --git a/chromium/ui/views/examples/tree_view_example.cc b/chromium/ui/views/examples/tree_view_example.cc index c6621eb3911..6626f709a97 100644 --- a/chromium/ui/views/examples/tree_view_example.cc +++ b/chromium/ui/views/examples/tree_view_example.cc @@ -4,7 +4,6 @@ #include "ui/views/examples/tree_view_example.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/menu/menu_model_adapter.h" diff --git a/chromium/ui/views/focus/external_focus_tracker.cc b/chromium/ui/views/focus/external_focus_tracker.cc index 280f09f8ae3..b843dc20795 100644 --- a/chromium/ui/views/focus/external_focus_tracker.cc +++ b/chromium/ui/views/focus/external_focus_tracker.cc @@ -5,7 +5,6 @@ #include "ui/views/focus/external_focus_tracker.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "ui/views/view.h" #include "ui/views/view_tracker.h" diff --git a/chromium/ui/views/focus/focus_manager.cc b/chromium/ui/views/focus/focus_manager.cc index c3c5e4c74f8..fb3b9016097 100644 --- a/chromium/ui/views/focus/focus_manager.cc +++ b/chromium/ui/views/focus/focus_manager.cc @@ -10,7 +10,6 @@ #include "base/auto_reset.h" #include "base/i18n/rtl.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "ui/base/accelerators/accelerator.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" diff --git a/chromium/ui/views/focus/focus_manager_factory.cc b/chromium/ui/views/focus/focus_manager_factory.cc index 85c5e49e462..5ebe513b2e3 100644 --- a/chromium/ui/views/focus/focus_manager_factory.cc +++ b/chromium/ui/views/focus/focus_manager_factory.cc @@ -4,7 +4,6 @@ #include "ui/views/focus/focus_manager_factory.h" -#include "base/memory/ptr_util.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_manager_delegate.h" diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc index ff55f0a3bc5..809b6d8a3ac 100644 --- a/chromium/ui/views/focus/focus_manager_unittest.cc +++ b/chromium/ui/views/focus/focus_manager_unittest.cc @@ -11,7 +11,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/icu_test_util.h" #include "ui/base/accelerators/accelerator.h" diff --git a/chromium/ui/views/focus/focus_traversal_unittest.cc b/chromium/ui/views/focus/focus_traversal_unittest.cc index 5085a704180..2e45e0ab16d 100644 --- a/chromium/ui/views/focus/focus_traversal_unittest.cc +++ b/chromium/ui/views/focus/focus_traversal_unittest.cc @@ -5,7 +5,6 @@ #include <stddef.h> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/ui/views/layout/box_layout.cc b/chromium/ui/views/layout/box_layout.cc index fabf5bb492e..d0d5624bf24 100644 --- a/chromium/ui/views/layout/box_layout.cc +++ b/chromium/ui/views/layout/box_layout.cc @@ -106,12 +106,15 @@ BoxLayout::BoxLayout(BoxLayout::Orientation orientation, BoxLayout::~BoxLayout() { } -void BoxLayout::SetFlexForView(const View* view, int flex_weight) { +void BoxLayout::SetFlexForView(const View* view, + int flex_weight, + bool use_min_size) { DCHECK(host_); DCHECK(view); DCHECK_EQ(host_, view->parent()); DCHECK_GE(flex_weight, 0); - flex_map_[view] = flex_weight; + flex_map_[view].flex_weight = flex_weight; + flex_map_[view].use_min_size = use_min_size; } void BoxLayout::ClearFlexForView(const View* view) { @@ -250,8 +253,9 @@ void BoxLayout::Layout(View* host) { // Calculate flex padding. int current_padding = 0; - if (GetFlexForView(child.view()) > 0) { - current_flex += GetFlexForView(child.view()); + int child_flex = GetFlexForView(child.view()); + if (child_flex > 0) { + current_flex += child_flex; int quot = (main_free_space * current_flex) / flex_sum; int rem = (main_free_space * current_flex) % flex_sum; current_padding = quot - total_padding; @@ -265,7 +269,12 @@ void BoxLayout::Layout(View* host) { // TODO(bruthig): Use the allocated width to determine the cross axis size. // See https://crbug.com/682266. int child_main_axis_size = MainAxisSizeForView(child, child_area.width()); - SetMainAxisSize(child_main_axis_size + current_padding, &bounds); + int child_min_size = GetMinimumSizeForView(child.view()); + if (child_min_size > 0 && !collapse_margins_spacing_) + child_min_size += child.margins().width(); + SetMainAxisSize( + std::max(child_min_size, child_main_axis_size + current_padding), + &bounds); if (MainAxisSize(bounds) > 0 || GetFlexForView(child.view()) > 0) main_position += MainAxisSize(bounds) + MainAxisMarginBetweenViews( @@ -359,7 +368,16 @@ int BoxLayout::GetFlexForView(const View* view) const { if (it == flex_map_.end()) return default_flex_; - return it->second; + return it->second.flex_weight; +} + +int BoxLayout::GetMinimumSizeForView(const View* view) const { + FlexMap::const_iterator it = flex_map_.find(view); + if (it == flex_map_.end() || !it->second.use_min_size) + return 0; + + return (orientation_ == kHorizontal) ? view->GetMinimumSize().width() + : view->GetMinimumSize().height(); } int BoxLayout::MainAxisSize(const gfx::Rect& rect) const { diff --git a/chromium/ui/views/layout/box_layout.h b/chromium/ui/views/layout/box_layout.h index c2bf8901341..315ac561859 100644 --- a/chromium/ui/views/layout/box_layout.h +++ b/chromium/ui/views/layout/box_layout.h @@ -132,10 +132,15 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { // the basis, free space along the main axis is distributed to views in the // ratio of their flex weights. Similarly, if the views will overflow the // parent, space is subtracted in these ratios. + // If true is passed in for |use_min_size|, the given view's minimum size + // is then obtained from calling View::GetMinimumSize(). This will be the + // minimum allowed size for the view along the main axis. False + // for |use_min_size| (the default) will allow the |view| to be resized to a + // minimum size of 0. // // A flex of 0 means this view is not resized. Flex values must not be // negative. - void SetFlexForView(const View* view, int flex); + void SetFlexForView(const View* view, int flex, bool use_min_size = false); // Clears the flex for the given |view|, causing it to use the default // flex. @@ -187,11 +192,19 @@ class VIEWS_EXPORT BoxLayout : public LayoutManager { DISALLOW_COPY_AND_ASSIGN(ViewWrapper); }; - using FlexMap = std::map<const View*, int>; + struct Flex { + int flex_weight; + bool use_min_size; + }; + + using FlexMap = std::map<const View*, Flex>; // Returns the flex for the specified |view|. int GetFlexForView(const View* view) const; + // Returns the minimum size for the specified |view|. + int GetMinimumSizeForView(const View* view) const; + // Returns the size and position along the main axis of |rect|. int MainAxisSize(const gfx::Rect& rect) const; int MainAxisPosition(const gfx::Rect& rect) const; diff --git a/chromium/ui/views/layout/box_layout_unittest.cc b/chromium/ui/views/layout/box_layout_unittest.cc index 8d46a6f15d7..676109da968 100644 --- a/chromium/ui/views/layout/box_layout_unittest.cc +++ b/chromium/ui/views/layout/box_layout_unittest.cc @@ -884,4 +884,31 @@ TEST_F(BoxLayoutTest, NegativeBetweenChildSpacing) { EXPECT_EQ(gfx::Rect(0, 10, 20, 15), v2->bounds()); } +TEST_F(BoxLayoutTest, MinimumChildSize) { + BoxLayout* layout = host_->SetLayoutManager( + std::make_unique<BoxLayout>(BoxLayout::kHorizontal, gfx::Insets())); + StaticSizedView* v1 = new StaticSizedView(gfx::Size(20, 20)); + host_->AddChildView(v1); + StaticSizedView* v2 = new StaticSizedView(gfx::Size(20, 20)); + host_->AddChildView(v2); + + v1->set_minimum_size(gfx::Size(10, 20)); + layout->SetFlexForView(v1, 1, true); + + gfx::Size preferred_size = layout->GetPreferredSize(host_.get()); + EXPECT_EQ(40, preferred_size.width()); + EXPECT_EQ(20, preferred_size.height()); + + host_->SetBounds(0, 0, 15, 20); + host_->Layout(); + EXPECT_EQ(gfx::Rect(0, 0, 10, 20), v1->bounds()); + EXPECT_EQ(gfx::Rect(10, 0, 5, 20), v2->bounds()); + + v1->set_minimum_size(gfx::Size(5, 20)); + + host_->Layout(); + EXPECT_EQ(gfx::Rect(0, 0, 5, 20), v1->bounds()); + EXPECT_EQ(gfx::Rect(5, 0, 10, 20), v2->bounds()); +} + } // namespace views diff --git a/chromium/ui/views/layout/layout_provider.cc b/chromium/ui/views/layout/layout_provider.cc index 44f3dedac84..1902f4ff539 100644 --- a/chromium/ui/views/layout/layout_provider.cc +++ b/chromium/ui/views/layout/layout_provider.cc @@ -5,7 +5,6 @@ #include "ui/views/layout/layout_provider.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/font_list.h" #include "ui/views/style/typography.h" @@ -43,6 +42,7 @@ int LayoutProvider::GetControlHeightForFont(int context, } gfx::Insets LayoutProvider::GetInsetsMetric(int metric) const { + DCHECK_GE(metric, VIEWS_INSETS_START); DCHECK_LT(metric, VIEWS_INSETS_MAX); switch (metric) { case InsetsMetric::INSETS_DIALOG: @@ -58,17 +58,22 @@ gfx::Insets LayoutProvider::GetInsetsMetric(int metric) const { return gfx::Insets(dialog_insets.top(), dialog_insets.left(), 0, dialog_insets.right()); } + case InsetsMetric::INSETS_TOOLTIP_BUBBLE: + return gfx::Insets(8); case InsetsMetric::INSETS_CHECKBOX_RADIO_BUTTON: return gfx::Insets(5, 6); case InsetsMetric::INSETS_VECTOR_IMAGE_BUTTON: return gfx::Insets(4); + case InsetsMetric::INSETS_LABEL_BUTTON: + return gfx::Insets(5, 6); } NOTREACHED(); return gfx::Insets(); } int LayoutProvider::GetDistanceMetric(int metric) const { - DCHECK_GE(metric, VIEWS_INSETS_MAX); + DCHECK_GE(metric, VIEWS_DISTANCE_START); + DCHECK_LT(metric, VIEWS_DISTANCE_MAX); switch (metric) { case DistanceMetric::DISTANCE_BUTTON_HORIZONTAL_PADDING: return 16; diff --git a/chromium/ui/views/layout/layout_provider.h b/chromium/ui/views/layout/layout_provider.h index d67ed3ba617..991b8e51681 100644 --- a/chromium/ui/views/layout/layout_provider.h +++ b/chromium/ui/views/layout/layout_provider.h @@ -32,9 +32,13 @@ enum InsetsMetric { // The margins around the icon/title of a dialog. The bottom margin is implied // by the content insets and the other margins overlap with INSETS_DIALOG. INSETS_DIALOG_TITLE, + // The margins around the edges of a tooltip bubble. + INSETS_TOOLTIP_BUBBLE, // Padding to add to vector image buttons to increase their click and touch // target size. INSETS_VECTOR_IMAGE_BUTTON, + // Padding used in a label button. + INSETS_LABEL_BUTTON, // Embedders must start Insets enum values from this value. VIEWS_INSETS_END, @@ -96,7 +100,10 @@ enum DistanceMetric { DISTANCE_UNRELATED_CONTROL_VERTICAL, // Embedders must start DistanceMetric enum values from here. - VIEWS_DISTANCE_END + VIEWS_DISTANCE_END, + + // All Distance enum values must be below this value. + VIEWS_DISTANCE_MAX = 0x2000 }; // The type of a dialog content element. TEXT should be used for Labels or other diff --git a/chromium/ui/views/linux_ui/OWNERS b/chromium/ui/views/linux_ui/OWNERS index 4733a4f06bf..280ba478dca 100644 --- a/chromium/ui/views/linux_ui/OWNERS +++ b/chromium/ui/views/linux_ui/OWNERS @@ -1 +1 @@ -erg@chromium.org +thomasanderson@chromium.org diff --git a/chromium/ui/views/mouse_watcher.cc b/chromium/ui/views/mouse_watcher.cc index 0f56dcef318..e0c1a3bb44b 100644 --- a/chromium/ui/views/mouse_watcher.cc +++ b/chromium/ui/views/mouse_watcher.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/compiler_specific.h" -#include "base/event_types.h" #include "base/location.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -16,6 +15,7 @@ #include "ui/events/event_constants.h" #include "ui/events/event_handler.h" #include "ui/events/event_utils.h" +#include "ui/events/platform_event.h" #include "ui/views/event_monitor.h" namespace views { @@ -64,8 +64,9 @@ class MouseWatcher::Observer : public ui::EventHandler { // Mouse moved outside the host's zone, start a timer to notify the // listener. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&Observer::NotifyListener, - notify_listener_factory_.GetWeakPtr()), + FROM_HERE, + base::BindOnce(&Observer::NotifyListener, + notify_listener_factory_.GetWeakPtr()), event_type == MouseWatcherHost::MOUSE_MOVE ? base::TimeDelta::FromMilliseconds(kNotifyListenerTimeMs) : mouse_watcher_->notify_on_exit_time_); diff --git a/chromium/ui/views/mus/BUILD.gn b/chromium/ui/views/mus/BUILD.gn index a8e4c997153..dfe239c16d5 100644 --- a/chromium/ui/views/mus/BUILD.gn +++ b/chromium/ui/views/mus/BUILD.gn @@ -80,9 +80,9 @@ jumbo_component("mus") { ] if (is_linux && !is_android) { - deps += [ "//components/font_service/public/cpp" ] + deps += [ "//components/services/font/public/cpp" ] data_deps = [ - "//components/font_service", + "//components/services/font:font_service", ] } } @@ -122,7 +122,7 @@ jumbo_static_library("test_support") { ":mus", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//services/catalog:lib", "//services/service_manager/background:lib", "//services/service_manager/public/cpp", @@ -192,6 +192,7 @@ test("views_mus_unittests") { "//ui/views", "//ui/views:test_support_internal", "//ui/views:views_unittests_sources", + "//ui/views/mus/remote_view:tests", "//ui/wm", "//url", ] @@ -244,7 +245,7 @@ test("views_mus_interactive_ui_tests") { ":mus", ":test_support", "//base", - "//mojo/edk/system", + "//mojo/edk", "//testing/gmock", "//testing/gtest", "//ui/aura", diff --git a/chromium/ui/views/mus/DEPS b/chromium/ui/views/mus/DEPS index 4d8a1fd285a..21ea014c764 100644 --- a/chromium/ui/views/mus/DEPS +++ b/chromium/ui/views/mus/DEPS @@ -1,7 +1,7 @@ include_rules = [ "+cc", "-cc/blink", - "+components/font_service/public", + "+components/services/font/public", "+components/gpu", "+mojo/cc", "+mojo/common", diff --git a/chromium/ui/views/mus/aura_init.cc b/chromium/ui/views/mus/aura_init.cc index 7631ab57b0e..ea6490d4ec3 100644 --- a/chromium/ui/views/mus/aura_init.cc +++ b/chromium/ui/views/mus/aura_init.cc @@ -25,7 +25,7 @@ #include "ui/views/views_delegate.h" #if defined(OS_LINUX) -#include "components/font_service/public/cpp/font_loader.h" +#include "components/services/font/public/cpp/font_loader.h" #include "ui/gfx/platform_font_linux.h" #endif 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 b24db3fc938..c417e738573 100644 --- a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc +++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc @@ -4,7 +4,6 @@ #include "ui/views/mus/desktop_window_tree_host_mus.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/aura/client/aura_constants.h" @@ -211,7 +210,7 @@ DesktopWindowTreeHostMus::DesktopWindowTreeHostMus( MusClient::Get()->AddObserver(this); MusClient::Get()->window_tree_client()->focus_synchronizer()->AddObserver( this); - desktop_native_widget_aura_->content_window()->AddObserver(this); + content_window()->AddObserver(this); // DesktopNativeWidgetAura registers the association between |content_window_| // and Widget, but code may also want to go from the root (window()) to the // Widget. This call enables that. @@ -226,7 +225,7 @@ DesktopWindowTreeHostMus::~DesktopWindowTreeHostMus() { // the cursor-client needs to be unset on the root-window before // |cursor_manager_| is destroyed. aura::client::SetCursorClient(window(), nullptr); - desktop_native_widget_aura_->content_window()->RemoveObserver(this); + content_window()->RemoveObserver(this); MusClient::Get()->RemoveObserver(this); MusClient::Get()->window_tree_client()->focus_synchronizer()->RemoveObserver( this); @@ -293,8 +292,7 @@ bool DesktopWindowTreeHostMus::ShouldSendClientAreaToServer() const { return type == WIP::TYPE_WINDOW || type == WIP::TYPE_PANEL; } -void DesktopWindowTreeHostMus::Init(aura::Window* content_window, - const Widget::InitParams& params) { +void DesktopWindowTreeHostMus::Init(const Widget::InitParams& params) { // |TYPE_WINDOW| and |TYPE_PANEL| are forced to transparent as otherwise the // window is opaque and the client decorations drawn by the window manager // would not be seen. @@ -302,7 +300,7 @@ void DesktopWindowTreeHostMus::Init(aura::Window* content_window, params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW || params.type == Widget::InitParams::TYPE_WINDOW || params.type == Widget::InitParams::TYPE_PANEL; - content_window->SetTransparent(transparent); + content_window()->SetTransparent(transparent); window()->SetTransparent(transparent); window()->SetProperty(aura::client::kShowStateKey, params.show_state); @@ -325,18 +323,14 @@ void DesktopWindowTreeHostMus::Init(aura::Window* content_window, params.parent->GetHost()->window(), window()); } - if (!params.accept_events) { + if (!params.accept_events) window()->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); - } else { - aura::WindowPortMus::Get(content_window)->SetCanAcceptDrops(true); - } + else + aura::WindowPortMus::Get(content_window())->SetCanAcceptDrops(true); } void DesktopWindowTreeHostMus::OnNativeWidgetCreated( const Widget::InitParams& params) { - window()->SetName(params.name); - desktop_native_widget_aura_->content_window()->SetName( - "DesktopNativeWidgetAura - content window"); if (params.parent && params.parent->GetHost()) { parent_ = static_cast<DesktopWindowTreeHostMus*>(params.parent->GetHost()); parent_->children_.insert(this); @@ -396,8 +390,8 @@ void DesktopWindowTreeHostMus::Close() { // Close doesn't delete this immediately, as 'this' may still be on the stack // resulting in possible crashes when the stack unwindes. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&DesktopWindowTreeHostMus::CloseNow, - close_widget_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&DesktopWindowTreeHostMus::CloseNow, + close_widget_factory_.GetWeakPtr())); } void DesktopWindowTreeHostMus::CloseNow() { @@ -495,10 +489,9 @@ void DesktopWindowTreeHostMus::CenterWindow(const gfx::Size& size) { gfx::Rect bounds_to_center_in = GetWorkAreaBoundsInScreen(); // If there is a transient parent and it fits |size|, then center over it. - aura::Window* content_window = desktop_native_widget_aura_->content_window(); - if (wm::GetTransientParent(content_window)) { + if (wm::GetTransientParent(content_window())) { gfx::Rect transient_parent_bounds = - wm::GetTransientParent(content_window)->GetBoundsInScreen(); + wm::GetTransientParent(content_window())->GetBoundsInScreen(); if (transient_parent_bounds.height() >= size.height() && transient_parent_bounds.width() >= size.width()) { bounds_to_center_in = transient_parent_bounds; @@ -792,7 +785,7 @@ void DesktopWindowTreeHostMus::OnActiveFocusClientChanged( void DesktopWindowTreeHostMus::OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) { - DCHECK_EQ(window, desktop_native_widget_aura_->content_window()); + DCHECK_EQ(window, content_window()); DCHECK(!window->GetRootWindow() || this->window() == window->GetRootWindow()); if (!this->window()) return; @@ -851,4 +844,8 @@ void DesktopWindowTreeHostMus::SetBoundsInPixels( } } +aura::Window* DesktopWindowTreeHostMus::content_window() { + return desktop_native_widget_aura_->content_window(); +} + } // namespace views 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 2884d650d58..e1dca1c6735 100644 --- a/chromium/ui/views/mus/desktop_window_tree_host_mus.h +++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.h @@ -61,8 +61,7 @@ class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus bool ShouldSendClientAreaToServer() const; // DesktopWindowTreeHost: - void Init(aura::Window* content_window, - const Widget::InitParams& params) override; + void Init(const Widget::InitParams& params) override; void OnNativeWidgetCreated(const Widget::InitParams& params) override; void OnActiveWindowChanged(bool active) override; void OnWidgetInitDone() override; @@ -143,6 +142,9 @@ class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus void HideImpl() override; void SetBoundsInPixels(const gfx::Rect& bounds_in_pixels) override; + // Accessor for DesktopNativeWidgetAura::content_window(). + aura::Window* content_window(); + internal::NativeWidgetDelegate* native_widget_delegate_; DesktopNativeWidgetAura* desktop_native_widget_aura_; 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 34a105de7de..50851a87bfa 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 @@ -7,7 +7,6 @@ #include "base/debug/stack_trace.h" #include "base/run_loop.h" -#include "base/memory/ptr_util.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/client/transient_window_client.h" diff --git a/chromium/ui/views/mus/drag_interactive_uitest.cc b/chromium/ui/views/mus/drag_interactive_uitest.cc index 08bab8b2ae7..50bb37c5d49 100644 --- a/chromium/ui/views/mus/drag_interactive_uitest.cc +++ b/chromium/ui/views/mus/drag_interactive_uitest.cc @@ -124,11 +124,11 @@ void DragTest_Part2(int64_t display_id, if (!result) quit_closure.Run(); - ui::mojom::RemoteEventDispatcher* dispatcher = - MusClient::Get()->GetTestingEventDispater(); - dispatcher->DispatchEvent( + ui::mojom::EventInjector* event_injector = + MusClient::Get()->GetTestingEventInjector(); + event_injector->InjectEvent( display_id, CreateMouseUpEvent(30, 30), - base::Bind(&DragTest_Part3, display_id, quit_closure)); + base::BindOnce(&DragTest_Part3, display_id, quit_closure)); } void DragTest_Part1(int64_t display_id, @@ -138,11 +138,11 @@ void DragTest_Part1(int64_t display_id, if (!result) quit_closure.Run(); - ui::mojom::RemoteEventDispatcher* dispatcher = - MusClient::Get()->GetTestingEventDispater(); - dispatcher->DispatchEvent( + ui::mojom::EventInjector* event_injector = + MusClient::Get()->GetTestingEventInjector(); + event_injector->InjectEvent( display_id, CreateMouseMoveEvent(30, 30), - base::Bind(&DragTest_Part2, display_id, quit_closure)); + base::BindOnce(&DragTest_Part2, display_id, quit_closure)); } TEST_F(DragTestInteractive, DragTest) { @@ -175,11 +175,11 @@ TEST_F(DragTestInteractive, DragTest) { { base::RunLoop run_loop; - ui::mojom::RemoteEventDispatcher* dispatcher = - MusClient::Get()->GetTestingEventDispater(); - dispatcher->DispatchEvent( + ui::mojom::EventInjector* event_injector = + MusClient::Get()->GetTestingEventInjector(); + event_injector->InjectEvent( display_id, CreateMouseDownEvent(10, 10), - base::Bind(&DragTest_Part1, display_id, run_loop.QuitClosure())); + base::BindOnce(&DragTest_Part1, display_id, run_loop.QuitClosure())); run_loop.Run(); } diff --git a/chromium/ui/views/mus/mus_client.cc b/chromium/ui/views/mus/mus_client.cc index 9399f8228d7..1e049056dfb 100644 --- a/chromium/ui/views/mus/mus_client.cc +++ b/chromium/ui/views/mus/mus_client.cc @@ -104,15 +104,12 @@ MusClient::MusClient(service_manager::Connector* connector, if (testing_state == MusClientTestingState::CREATE_TESTING_STATE) { connector->BindInterface(ui::mojom::kServiceName, &server_test_ptr_); - connector->BindInterface(ui::mojom::kServiceName, - &remote_event_dispatcher_ptr_); + connector->BindInterface(ui::mojom::kServiceName, &event_injector_); } - window_tree_client_ = std::make_unique<aura::WindowTreeClient>( - connector, this, nullptr /* window_manager_delegate */, - nullptr /* window_tree_client_request */, std::move(io_task_runner)); + window_tree_client_ = aura::WindowTreeClient::CreateForWindowTreeFactory( + connector, this, true, std::move(io_task_runner)); aura::Env::GetInstance()->SetWindowTreeClient(window_tree_client_.get()); - window_tree_client_->ConnectViaWindowTreeFactory(); pointer_watcher_event_router_ = std::make_unique<PointerWatcherEventRouter>(window_tree_client_.get()); @@ -291,9 +288,9 @@ ui::mojom::WindowServerTest* MusClient::GetTestingInterface() const { return server_test_ptr_.get(); } -ui::mojom::RemoteEventDispatcher* MusClient::GetTestingEventDispater() const { - CHECK(remote_event_dispatcher_ptr_); - return remote_event_dispatcher_ptr_.get(); +ui::mojom::EventInjector* MusClient::GetTestingEventInjector() const { + CHECK(event_injector_); + return event_injector_.get(); } std::unique_ptr<DesktopWindowTreeHost> MusClient::CreateDesktopWindowTreeHost( diff --git a/chromium/ui/views/mus/mus_client.h b/chromium/ui/views/mus/mus_client.h index 666f75eb533..0543e03247a 100644 --- a/chromium/ui/views/mus/mus_client.h +++ b/chromium/ui/views/mus/mus_client.h @@ -13,7 +13,7 @@ #include "base/macros.h" #include "services/service_manager/public/cpp/identity.h" -#include "services/ui/public/interfaces/remote_event_dispatcher.mojom.h" +#include "services/ui/public/interfaces/event_injector.mojom.h" #include "services/ui/public/interfaces/window_server_test.mojom.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/mus/window_tree_client_delegate.h" @@ -127,9 +127,9 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate, // with MusClientTestingState::CREATE_TESTING_STATE. ui::mojom::WindowServerTest* GetTestingInterface() const; - // Returns an interface to dispatch events in the mus server. Only available - // when created with MusClientTestingState::CREATE_TESTING_STATE. - ui::mojom::RemoteEventDispatcher* GetTestingEventDispater() const; + // Returns an interface to inject events into the Window Service. Only + // available when created with MusClientTestingState::CREATE_TESTING_STATE. + ui::mojom::EventInjector* GetTestingEventInjector() const; private: friend class AuraInit; @@ -182,7 +182,7 @@ class VIEWS_MUS_EXPORT MusClient : public aura::WindowTreeClientDelegate, std::unique_ptr<PointerWatcherEventRouter> pointer_watcher_event_router_; ui::mojom::WindowServerTestPtr server_test_ptr_; - ui::mojom::RemoteEventDispatcherPtr remote_event_dispatcher_ptr_; + ui::mojom::EventInjectorPtr event_injector_; DISALLOW_COPY_AND_ASSIGN(MusClient); }; diff --git a/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc b/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc index bdccaff4aa8..7634fff74bc 100644 --- a/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc +++ b/chromium/ui/views/mus/pointer_watcher_event_router_unittest.cc @@ -6,7 +6,6 @@ #include <memory> -#include "base/memory/ptr_util.h" #include "base/test/scoped_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/test/mus/window_tree_client_private.h" diff --git a/chromium/ui/views/mus/remote_view/BUILD.gn b/chromium/ui/views/mus/remote_view/BUILD.gn new file mode 100644 index 00000000000..a5232997d72 --- /dev/null +++ b/chromium/ui/views/mus/remote_view/BUILD.gn @@ -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. + +source_set("remote_view_host") { + sources = [ + "remote_view_host.cc", + "remote_view_host.h", + ] + + deps = [ + "//base", + "//ui/aura", + "//ui/views", + ] +} + +source_set("remote_view_provider") { + sources = [ + "remote_view_provider.cc", + "remote_view_provider.h", + ] + + deps = [ + "//base", + "//ui/aura", + "//ui/views", + "//ui/views/mus", + ] +} + +source_set("test_support") { + testonly = true + + sources = [ + "remote_view_provider_test_api.cc", + "remote_view_provider_test_api.h", + ] + + deps = [ + ":remote_view_provider", + "//base", + ] +} + +source_set("tests") { + testonly = true + + sources = [ + "remote_view_host_unittest.cc", + "remote_view_provider_unittest.cc", + ] + + deps = [ + ":remote_view_host", + ":remote_view_provider", + ":test_support", + "//base", + "//testing/gtest", + "//ui/aura", + "//ui/aura:test_support", + "//ui/base", + "//ui/views", + "//ui/views/mus", + ] +} diff --git a/chromium/ui/views/mus/remote_view/README.md b/chromium/ui/views/mus/remote_view/README.md new file mode 100644 index 00000000000..391437e842e --- /dev/null +++ b/chromium/ui/views/mus/remote_view/README.md @@ -0,0 +1,55 @@ +This directory contains helper classes to embed a `Window`. + +## `RemoteViewProvider` + +`RemoteViewProvider` wraps the work needed to provide a `Window` to another +(remote) client. Typical usage is to create an instance of it with the window to +provide to the remote client. + +e.g. +``` + ... + + // Step 1: Prepares for embedding after |window_for_remote_client_| is ready. + void StartEmbed() { + remote_view_provider_ = + std::make_unique<views::RemoteViewProvider>(window_for_remote_client_); + remote_view_client->GetEmbedToken(base::BindOnce( + &EmbeddedClient::OnGotEmbedToken, base::Unretained(this))); + } + + void OnGotEmbedToken(const base::UnguessableToken& token) { + // Step 2: Pass |token| to the embedder. + } + + // A Window to to provide to the remote client. + aura::Window* window_for_remote_client_; + + // Helper to prepare |window_for_remote_client_| for embedding. + std::unique_ptr<views::RemoteViewProvider> remote_view_provider_; + + ... +``` + +## `RemoteViewHost` + +`RemoteViewHost` wraps the work on the embedder side and is a `NativeViewHost` +that embeds the window from the `RemoteViewProvider`. `RemoveViewHost` is a +`View` that you add to your existing `View` hierarchy. + +e.g. +``` + class EmbedderView : public views::View { + public: + ... + // Creates a RemoteViewHost using |token| from the embedded client. + void AddEmbeddedView(const base::UnguessableToken& token) { + // Ownership will be passed to the view hierarchy in AddChildView. + views::RemoteViewHost* remote_view_host = new RemoteViewHost(); + remote_view_host->EmbedUsingToken(token, base::DoNothing()); + AddChildView(remote_view_host); + } + ... + }; +``` + diff --git a/chromium/ui/views/mus/remote_view/remote_view_host.cc b/chromium/ui/views/mus/remote_view/remote_view_host.cc new file mode 100644 index 00000000000..c5f9e577de0 --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_host.cc @@ -0,0 +1,76 @@ +// 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/mus/remote_view/remote_view_host.h" + +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/env.h" +#include "ui/aura/mus/window_port_mus.h" + +namespace views { + +RemoteViewHost::RemoteViewHost() = default; +RemoteViewHost::~RemoteViewHost() = default; + +void RemoteViewHost::EmbedUsingToken(const base::UnguessableToken& embed_token, + int embed_flags, + EmbedCallback callback) { + // Only works with mus. + DCHECK_EQ(aura::Env::Mode::MUS, aura::Env::GetInstanceDontCreate()->mode()); + + embed_token_ = embed_token; + embed_flags_ = embed_flags; + embed_callback_ = std::move(callback); + + if (GetWidget()) + CreateEmbeddingRoot(); +} + +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()); + + aura::WindowPortMus::Get(embedding_root_.get()) + ->EmbedUsingToken(embed_token_, embed_flags_, + base::BindOnce(&RemoteViewHost::OnEmbedResult, + weak_ptr_factory_.GetWeakPtr())); +} + +void RemoteViewHost::OnEmbedResult(bool success) { + LOG_IF(ERROR, !success) << "Failed to embed, token=" << embed_token_; + + if (!success && embedding_root_) + embedding_root_.reset(); + + if (embed_callback_) + std::move(embed_callback_).Run(success); +} + +void RemoteViewHost::AddedToWidget() { + if (!native_view() && !embed_token_.is_empty()) + CreateEmbeddingRoot(); +} + +} // 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 new file mode 100644 index 00000000000..44b1552f325 --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_host.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_MUS_REMOTE_VIEW_REMOTE_VIEW_HOST_H_ +#define UI_VIEWS_MUS_REMOTE_VIEW_REMOTE_VIEW_HOST_H_ + +#include <memory> + +#include "base/callback_forward.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/unguessable_token.h" +#include "ui/aura/window.h" +#include "ui/views/controls/native/native_view_host.h" + +namespace views { + +// A view at the embedder side to embed an aura::Window from another window +// tree. Note this only works with mus. +class RemoteViewHost : public views::NativeViewHost { + public: + RemoteViewHost(); + ~RemoteViewHost() override; + + // Embeds the remote contents after this view is added to a widget. + // |embed_token| is the token obtained from the WindowTree embed API + // (ScheduleEmbed/ForExistingClient). |embed_flags| are the embedding flags + // (see window_tree_constants.mojom). |callback| is an optional callback + // invoked with the embed result. + // Note that |callback| should not be used to add the view to a widget because + // the actual embedding only happens after the view is added. + using EmbedCallback = base::OnceCallback<void(bool success)>; + void EmbedUsingToken(const base::UnguessableToken& embed_token, + int embed_flags, + EmbedCallback callback); + + private: + // Creates the embedding aura::Window and attach to it. + void CreateEmbeddingRoot(); + + // Invoked after the embed operation. + void OnEmbedResult(bool success); + + // views::NativeViewHost: + void AddedToWidget() override; + + base::UnguessableToken embed_token_; + int embed_flags_ = 0; + EmbedCallback embed_callback_; + + std::unique_ptr<aura::Window> embedding_root_; + base::WeakPtrFactory<RemoteViewHost> weak_ptr_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(RemoteViewHost); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_REMOTE_VIEW_REMOTE_VIEW_HOST_H_ 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 new file mode 100644 index 00000000000..611c00151cd --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_host_unittest.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/views/mus/remote_view/remote_view_host.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/unguessable_token.h" +#include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/mus/test_window_tree.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace views { + +namespace { + +// Embeds using |token| and waits for it. Returns true if embed succeeds. +bool Embed(RemoteViewHost* host, const base::UnguessableToken& token) { + base::RunLoop run_loop; + bool embed_result = false; + host->EmbedUsingToken( + token, 0u /* no flags */, + base::BindOnce( + [](base::RunLoop* run_loop, bool* result, bool success) { + *result = success; + run_loop->Quit(); + }, + &run_loop, &embed_result)); + run_loop.Run(); + return embed_result; +} + +} // namespace + +class RemoteViewHostTest : public aura::test::AuraTestBase { + public: + RemoteViewHostTest() = default; + ~RemoteViewHostTest() override = default; + + // aura::test::AuraTestBase + void SetUp() override { + EnableMusWithTestWindowTree(); + AuraTestBase::SetUp(); + } + + // Creates a widget to host |contents|. + std::unique_ptr<views::Widget> CreateTestWidget(views::View* contents) { + std::unique_ptr<views::Widget> widget = std::make_unique<views::Widget>(); + views::Widget::InitParams params; + params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(0, 0, 100, 100); + params.context = root_window(); + widget->Init(params); + widget->SetContentsView(contents); + + return widget; + } + + // Helper callback invoked during embed to simulate adding to widget during + // embed operation. + void CreateTestWidgetWhileEmbeddingHelper( + base::RunLoop* run_loop, + views::View* contents, + std::unique_ptr<views::Widget>* widget, + bool success) { + ASSERT_TRUE(success); + *widget = CreateTestWidget(contents); + run_loop->Quit(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(RemoteViewHostTest); +}; + +// Tests that the embed operation fails with an unknown token and RemoteViewHost +// will not be attached. +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()); + + // 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()); +} + +// Tests when RemoveViewHost is added to a widget before embedding. +TEST_F(RemoteViewHostTest, AddToWidgetBeforeEmbed) { + const base::UnguessableToken token = base::UnguessableToken::Create(); + window_tree()->AddScheduledEmbedToken(token); + + // Ownership will be passed to |widget| later. + RemoteViewHost* host = new RemoteViewHost(); + + // |host| is not attached because embed operation is not performed. + EXPECT_FALSE(host->native_view()); + std::unique_ptr<views::Widget> widget = CreateTestWidget(host); + EXPECT_FALSE(host->native_view()); + + // Embed succeeds. + EXPECT_TRUE(Embed(host, token)); + + // |host| is now attached to the embedding window. + EXPECT_TRUE(host->native_view()); +} + +// Tests when RemoveViewHost is added to a widget after embedding. +TEST_F(RemoteViewHostTest, AddToWidgetAfterEmbed) { + const base::UnguessableToken token = base::UnguessableToken::Create(); + window_tree()->AddScheduledEmbedToken(token); + + // Ownership will be passed to |widget| later. + RemoteViewHost* host = new RemoteViewHost(); + + // Request embedding but it will be deferred until added to a widget. + base::RunLoop run_loop; + bool embed_result = false; + host->EmbedUsingToken( + token, 0u /* no flags */, + base::BindOnce( + [](base::RunLoop* run_loop, bool* result, bool success) { + *result = success; + run_loop->Quit(); + }, + &run_loop, &embed_result)); + + // |host| is not attached before adding to a widget. + EXPECT_FALSE(host->native_view()); + + // Add to a widget and wait for embed to finish. + std::unique_ptr<views::Widget> widget = CreateTestWidget(host); + run_loop.Run(); + + // |host| is attached after added to a widget. + EXPECT_TRUE(host->native_view()); +} + +} // namespace views diff --git a/chromium/ui/views/mus/remote_view/remote_view_provider.cc b/chromium/ui/views/mus/remote_view/remote_view_provider.cc new file mode 100644 index 00000000000..12286cd26af --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_provider.cc @@ -0,0 +1,154 @@ +// 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/mus/remote_view/remote_view_provider.h" + +#include <utility> + +#include "base/callback_helpers.h" +#include "base/logging.h" +#include "base/scoped_observer.h" +#include "ui/aura/mus/embed_root.h" +#include "ui/aura/mus/window_tree_client.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/gfx/geometry/size.h" +#include "ui/views/mus/mus_client.h" + +namespace views { + +// static +aura::WindowTreeClient* RemoteViewProvider::window_tree_client_for_test = + nullptr; + +// Observes a window and invokes a callback when the window is destroyed. +class RemoteViewProvider::EmbeddedWindowObserver : public aura::WindowObserver { + public: + EmbeddedWindowObserver(aura::Window* window, base::OnceClosure on_destroyed) + : window_observer_(this), on_destroyed_(std::move(on_destroyed)) { + window_observer_.Add(window); + } + ~EmbeddedWindowObserver() override = default; + + // aura::WindowObserver: + void OnWindowDestroyed(aura::Window* window) override { + DCHECK(!on_destroyed_.is_null()); + + window_observer_.RemoveAll(); + base::ResetAndReturn(&on_destroyed_).Run(); + } + + private: + ScopedObserver<aura::Window, EmbeddedWindowObserver> window_observer_; + base::OnceClosure on_destroyed_; + + DISALLOW_COPY_AND_ASSIGN(EmbeddedWindowObserver); +}; + +// Observes a window and invokes a callback when the window size changes. +class RemoteViewProvider::EmbeddingWindowObserver + : public aura::WindowObserver { + public: + using SizeChangedCallback = base::RepeatingCallback<void(const gfx::Size&)>; + EmbeddingWindowObserver(aura::Window* window, + const SizeChangedCallback& callback) + : window_observer_(this), on_size_changed_(callback) { + window_observer_.Add(window); + } + ~EmbeddingWindowObserver() override = default; + + // aura::WindowObserver: + void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + ui::PropertyChangeReason reason) override { + on_size_changed_.Run(new_bounds.size()); + } + void OnWindowDestroyed(aura::Window* window) override { + window_observer_.RemoveAll(); + } + + private: + ScopedObserver<aura::Window, EmbeddingWindowObserver> window_observer_; + SizeChangedCallback on_size_changed_; + + DISALLOW_COPY_AND_ASSIGN(EmbeddingWindowObserver); +}; + +RemoteViewProvider::RemoteViewProvider(aura::Window* embedded) + : embedded_(embedded) { + DCHECK(embedded_); + embedded_window_observer_ = std::make_unique<EmbeddedWindowObserver>( + embedded_, base::BindOnce(&RemoteViewProvider::OnEmbeddedWindowDestroyed, + base::Unretained(this))); +} + +RemoteViewProvider::~RemoteViewProvider() = default; + +void RemoteViewProvider::GetEmbedToken(GetEmbedTokenCallback callback) { + DCHECK(embedded_); + + if (embed_root_) { + std::move(callback).Run(embed_root_->token()); + return; + } + + DCHECK(get_embed_token_callback_.is_null()); + get_embed_token_callback_ = std::move(callback); + + aura::WindowTreeClient* window_tree_client = window_tree_client_for_test; + if (!window_tree_client) { + DCHECK(views::MusClient::Exists()); + window_tree_client = views::MusClient::Get()->window_tree_client(); + } + + embed_root_ = window_tree_client->CreateEmbedRoot(this); +} + +void RemoteViewProvider::SetCallbacks(const OnEmbedCallback& on_embed, + const OnUnembedCallback& on_unembed) { + on_embed_callback_ = on_embed; + on_unembed_callback_ = on_unembed; +} + +void RemoteViewProvider::OnEmbeddedWindowDestroyed() { + embedded_ = nullptr; +} + +void RemoteViewProvider::OnEmbeddingWindowResized(const gfx::Size& size) { + // |embedded_| is owned by external code. Bail if it is destroyed while being + // embedded. + if (!embedded_) + return; + embedded_->SetBounds(gfx::Rect(size)); +} + +void RemoteViewProvider::OnEmbedTokenAvailable( + const base::UnguessableToken& token) { + if (get_embed_token_callback_) + std::move(get_embed_token_callback_).Run(token); +} + +void RemoteViewProvider::OnEmbed(aura::Window* window) { + DCHECK(embedded_); + + embedding_window_observer_ = std::make_unique<EmbeddingWindowObserver>( + window, base::BindRepeating(&RemoteViewProvider::OnEmbeddingWindowResized, + base::Unretained(this))); + OnEmbeddingWindowResized(window->bounds().size()); + window->AddChild(embedded_); + + if (on_embed_callback_) + on_embed_callback_.Run(window); +} + +void RemoteViewProvider::OnUnembed() { + embedding_window_observer_.reset(); + embed_root_.reset(); + + if (on_unembed_callback_) + on_unembed_callback_.Run(); +} + +} // namespace views diff --git a/chromium/ui/views/mus/remote_view/remote_view_provider.h b/chromium/ui/views/mus/remote_view/remote_view_provider.h new file mode 100644 index 00000000000..9717d5f8a76 --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_provider.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_VIEWS_MUS_REMOTE_VIEW_REMOTE_VIEW_PROVIDER_H_ +#define UI_VIEWS_MUS_REMOTE_VIEW_REMOTE_VIEW_PROVIDER_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/unguessable_token.h" +#include "ui/aura/mus/embed_root_delegate.h" + +namespace aura { +class EmbedRoot; +class Window; +class WindowTreeClient; +} // namespace aura + +namespace gfx { +class Size; +} + +namespace views { + +namespace test { +class RemoteViewProviderTestApi; +} + +// Creates an EmbedRoot for an embedded aura::Window on the client side and +// updates the embedded window size when the embedder changes size. For example, +// allows app list in ash to embed web-based "answer cards" that are rendered +// by the browser. Note this works only with mus. +class RemoteViewProvider : public aura::EmbedRootDelegate { + public: + explicit RemoteViewProvider(aura::Window* embedded); + ~RemoteViewProvider() override; + + static void SetWindowTreeClientForTest(aura::WindowTreeClient* tree_client); + + // Gets the embed token. An embed token is obtained from one of WindowTree's + // schedule embed calls (ScheduleEmbed or ScheduleEmbedForExistingClient). An + // embedder calls EmbedUsingToken using the token to embed desired contents. + // The embed token here is from an aura::EmbedRoot that uses + // ScheduleEmbedForExistingClient for embedding part of an existing + // WindowTreeClient. |callback| is invoked when the token is available. + using GetEmbedTokenCallback = + base::OnceCallback<void(const base::UnguessableToken& token)>; + void GetEmbedToken(GetEmbedTokenCallback callback); + + // Optional callbacks to be invoked when embed or unembed happens. + using OnEmbedCallback = base::RepeatingCallback<void(aura::Window* embedder)>; + using OnUnembedCallback = base::RepeatingClosure; + void SetCallbacks(const OnEmbedCallback& on_embed, + const OnUnembedCallback& on_unembed); + + private: + friend class test::RemoteViewProviderTestApi; + + class EmbeddedWindowObserver; + class EmbeddingWindowObserver; + + // Invoked when |embedded_| is destroyed. + void OnEmbeddedWindowDestroyed(); + + // Invoked when embedder changes size. + void OnEmbeddingWindowResized(const gfx::Size& size); + + // aura::EmbedRootDelegate: + void OnEmbedTokenAvailable(const base::UnguessableToken& token) override; + void OnEmbed(aura::Window* window) override; + void OnUnembed() override; + + // An aura::Window to be embedded. Not owned. + aura::Window* embedded_; + + std::unique_ptr<aura::EmbedRoot> embed_root_; + GetEmbedTokenCallback get_embed_token_callback_; + OnEmbedCallback on_embed_callback_; + OnUnembedCallback on_unembed_callback_; + + // An aura::WindowTreeClient for test. Use RemoteViewProviderTestApi to set + // it. + static aura::WindowTreeClient* window_tree_client_for_test; + + // Observes the |embedded_| and clears it when it is gone. + std::unique_ptr<EmbeddedWindowObserver> embedded_window_observer_; + + // Observes the embeddding window provided by embedder. + std::unique_ptr<EmbeddingWindowObserver> embedding_window_observer_; + + DISALLOW_COPY_AND_ASSIGN(RemoteViewProvider); +}; + +} // namespace views + +#endif // UI_VIEWS_MUS_REMOTE_VIEW_REMOTE_VIEW_PROVIDER_H_ diff --git a/chromium/ui/views/mus/remote_view/remote_view_provider_test_api.cc b/chromium/ui/views/mus/remote_view/remote_view_provider_test_api.cc new file mode 100644 index 00000000000..c3668ba2114 --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_provider_test_api.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/views/mus/remote_view/remote_view_provider_test_api.h" + +#include "ui/views/mus/remote_view/remote_view_provider.h" + +namespace views { +namespace test { + +void RemoteViewProviderTestApi::SetWindowTreeClient( + aura::WindowTreeClient* window_tree_client) { + RemoteViewProvider::window_tree_client_for_test = window_tree_client; +} + +} // namespace test +} // namespace views diff --git a/chromium/ui/views/mus/remote_view/remote_view_provider_test_api.h b/chromium/ui/views/mus/remote_view/remote_view_provider_test_api.h new file mode 100644 index 00000000000..49212fa6762 --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_provider_test_api.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_MUS_REMOTE_VIEW_REMOTE_VIEW_PROVIDER_TEST_API_H_ +#define UI_VIEWS_MUS_REMOTE_VIEW_REMOTE_VIEW_PROVIDER_TEST_API_H_ + +#include "base/macros.h" + +namespace aura { +class WindowTreeClient; +} + +namespace views { +namespace test { + +class RemoteViewProviderTestApi { + public: + // Sets an aura::WindowTreeClient to use with RemoteViewProvider. + static void SetWindowTreeClient(aura::WindowTreeClient* window_tree_client); + + private: + DISALLOW_COPY_AND_ASSIGN(RemoteViewProviderTestApi); +}; + +} // namespace test +} // namespace views + +#endif // UI_VIEWS_MUS_REMOTE_VIEW_REMOTE_VIEW_PROVIDER_TEST_API_H_ diff --git a/chromium/ui/views/mus/remote_view/remote_view_provider_unittest.cc b/chromium/ui/views/mus/remote_view/remote_view_provider_unittest.cc new file mode 100644 index 00000000000..f87a3202ebc --- /dev/null +++ b/chromium/ui/views/mus/remote_view/remote_view_provider_unittest.cc @@ -0,0 +1,169 @@ +// 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/mus/remote_view/remote_view_provider.h" + +#include <memory> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/unguessable_token.h" +#include "ui/aura/mus/window_mus.h" +#include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/mus/test_window_tree.h" +#include "ui/aura/window.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/mus/remote_view/remote_view_provider_test_api.h" + +namespace views { + +class RemoteViewProviderTest : public aura::test::AuraTestBase { + public: + RemoteViewProviderTest() = default; + ~RemoteViewProviderTest() override = default; + + // aura::test::AuraTestBase + void SetUp() override { + EnableMusWithTestWindowTree(); + AuraTestBase::SetUp(); + + test::RemoteViewProviderTestApi::SetWindowTreeClient( + window_tree_client_impl()); + + embedded_ = std::make_unique<aura::Window>(nullptr); + embedded_->set_owned_by_parent(false); + embedded_->Init(ui::LAYER_NOT_DRAWN); + embedded_->SetBounds(gfx::Rect(100, 50)); + + provider_ = std::make_unique<RemoteViewProvider>(embedded_.get()); + } + + void TearDown() override { + // EmbedRoot in |provider_| must be released before WindowTreeClient. + provider_.reset(); + + AuraTestBase::TearDown(); + } + + // Gets the embed token and waits for it. + base::UnguessableToken GetEmbedToken() { + base::RunLoop run_loop; + base::UnguessableToken token; + provider_->GetEmbedToken(base::BindOnce( + [](base::RunLoop* run_loop, base::UnguessableToken* out_token, + const base::UnguessableToken& token) { + *out_token = token; + run_loop->Quit(); + }, + &run_loop, &token)); + run_loop.Run(); + return token; + } + + // Simulates EmbedUsingToken call on embedder side and waits for + // WindowTreeClient to create a local embedder window. + aura::Window* SimulateEmbedUsingTokenAndGetEmbedder( + const base::UnguessableToken& token) { + base::RunLoop run_loop; + aura::Window* embedder = nullptr; + provider_->SetCallbacks( + base::BindRepeating( + [](base::RunLoop* run_loop, aura::Window** out_embedder, + aura::Window* embedder) { + *out_embedder = embedder; + run_loop->Quit(); + }, + &run_loop, &embedder), + base::DoNothing() /* OnUnembedCallback */); + window_tree()->AddEmbedRootForToken(token); + run_loop.Run(); + return embedder; + } + + // Helper to simulate embed. + aura::Window* SimulateEmbed() { + base::UnguessableToken token = GetEmbedToken(); + if (token.is_empty()) { + ADD_FAILURE() << "Failed to get embed token."; + return nullptr; + } + + return SimulateEmbedUsingTokenAndGetEmbedder(token); + } + + // Simulates the embedder window close. + void SimulateEmbedderClose(aura::Window* embedder) { + base::RunLoop run_loop; + provider_->SetCallbacks( + base::DoNothing() /* OnEmbedCallback */, + base::BindRepeating([](base::RunLoop* run_loop) { run_loop->Quit(); }, + &run_loop)); + + const ui::Id embedder_window_id = + aura::WindowMus::Get(embedder)->server_id(); + window_tree()->RemoveEmbedderWindow(embedder_window_id); + run_loop.Run(); + } + + protected: + std::unique_ptr<aura::Window> embedded_; + std::unique_ptr<RemoteViewProvider> provider_; + + private: + DISALLOW_COPY_AND_ASSIGN(RemoteViewProviderTest); +}; + +// Tests the basics on the embedded client. +TEST_F(RemoteViewProviderTest, Embed) { + aura::Window* embedder = SimulateEmbed(); + ASSERT_TRUE(embedder); + + // |embedded_| has the same non-empty size with |embedder| after embed. + EXPECT_EQ(embedded_->bounds().size(), embedder->bounds().size()); + EXPECT_FALSE(embedded_->bounds().IsEmpty()); + EXPECT_FALSE(embedder->bounds().IsEmpty()); + + // |embedded_| resizes with |embedder|. + const gfx::Rect new_bounds(embedder->bounds().width() + 100, + embedder->bounds().height() + 50); + embedder->SetBounds(new_bounds); + EXPECT_EQ(embedded_->bounds().size(), embedder->bounds().size()); + EXPECT_FALSE(embedded_->bounds().IsEmpty()); + EXPECT_FALSE(embedder->bounds().IsEmpty()); +} + +// Tests when |embedded_| is released first. +TEST_F(RemoteViewProviderTest, EmbeddedReleasedFirst) { + SimulateEmbed(); + embedded_.reset(); +} + +// Tests when |provider_| is released first. +TEST_F(RemoteViewProviderTest, ClientReleasedFirst) { + SimulateEmbed(); + provider_.reset(); +} + +// Tests when embedder goes away first. +TEST_F(RemoteViewProviderTest, EmbedderReleasedFirst) { + aura::Window* embedder = SimulateEmbed(); + ASSERT_TRUE(embedder); + + SimulateEmbedderClose(embedder); +} + +// Tests that the client can embed again. +TEST_F(RemoteViewProviderTest, EmbedAgain) { + aura::Window* embedder = SimulateEmbed(); + ASSERT_TRUE(embedder); + + SimulateEmbedderClose(embedder); + + aura::Window* new_embedder = SimulateEmbed(); + ASSERT_TRUE(new_embedder); + EXPECT_NE(new_embedder, embedder); +} + +} // namespace views diff --git a/chromium/ui/views/mus/views_mus_test_suite.cc b/chromium/ui/views/mus/views_mus_test_suite.cc index 0008b8eeaf4..3b2cfcd1c39 100644 --- a/chromium/ui/views/mus/views_mus_test_suite.cc +++ b/chromium/ui/views/mus/views_mus_test_suite.cc @@ -10,7 +10,6 @@ #include "base/base_switches.h" #include "base/command_line.h" #include "base/files/file_path.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" @@ -86,8 +85,8 @@ class ServiceManagerConnection { base::Thread::Options options; thread_.StartWithOptions(options); thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&ServiceManagerConnection::SetUpConnections, - base::Unretained(this), &wait)); + FROM_HERE, base::BindOnce(&ServiceManagerConnection::SetUpConnections, + base::Unretained(this), &wait)); wait.Wait(); } @@ -95,8 +94,9 @@ class ServiceManagerConnection { base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&ServiceManagerConnection::TearDownConnections, - base::Unretained(this), &wait)); + FROM_HERE, + base::BindOnce(&ServiceManagerConnection::TearDownConnections, + base::Unretained(this), &wait)); wait.Wait(); } @@ -111,8 +111,8 @@ class ServiceManagerConnection { base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&ServiceManagerConnection::CloneConnector, - base::Unretained(this), &wait)); + FROM_HERE, base::BindOnce(&ServiceManagerConnection::CloneConnector, + base::Unretained(this), &wait)); wait.Wait(); DCHECK(service_manager_connector_); return service_manager_connector_.get(); diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc index 85ee0946242..d93eacd40f1 100644 --- a/chromium/ui/views/painter.cc +++ b/chromium/ui/views/painter.cc @@ -5,7 +5,6 @@ #include "ui/views/painter.h" #include "base/logging.h" -#include "base/memory/ptr_util.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_delegate.h" #include "ui/compositor/layer_owner.h" diff --git a/chromium/ui/views/selection_controller.cc b/chromium/ui/views/selection_controller.cc index aa400d8bf22..b4a3a403932 100644 --- a/chromium/ui/views/selection_controller.cc +++ b/chromium/ui/views/selection_controller.cc @@ -164,7 +164,8 @@ void SelectionController::TrackMouseClicks(const ui::MouseEvent& event) { base::TimeDelta time_delta = event.time_stamp() - last_click_time_; if (!last_click_time_.is_null() && time_delta.InMilliseconds() <= GetDoubleClickInterval() && - !View::ExceededDragThreshold(event.location() - last_click_location_)) { + !View::ExceededDragThreshold(event.root_location() - + last_click_location_)) { // Upon clicking after a triple click, the count should go back to // double click and alternate between double and triple. This assignment // maps 0 to 1, 1 to 2, 2 to 1. @@ -173,7 +174,7 @@ void SelectionController::TrackMouseClicks(const ui::MouseEvent& event) { aggregated_clicks_ = 0; } last_click_time_ = event.time_stamp(); - last_click_location_ = event.location(); + last_click_location_ = event.root_location(); } } diff --git a/chromium/ui/views/style/platform_style.cc b/chromium/ui/views/style/platform_style.cc index b12430178e9..99793bc88bd 100644 --- a/chromium/ui/views/style/platform_style.cc +++ b/chromium/ui/views/style/platform_style.cc @@ -4,11 +4,12 @@ #include "ui/views/style/platform_style.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/range/range.h" #include "ui/gfx/shadow_value.h" +#include "ui/gfx/utf16_indexing.h" #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/controls/button/label_button.h" @@ -55,6 +56,7 @@ const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = false; const bool PlatformStyle::kUseRipples = true; const bool PlatformStyle::kTextfieldScrollsToStartOnFocusChange = false; const bool PlatformStyle::kTextfieldUsesDragCursorWhenDraggable = true; +const bool PlatformStyle::kShouldElideBookmarksInBookmarksBar = false; // static std::unique_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) { @@ -68,6 +70,15 @@ std::unique_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) { // static void PlatformStyle::OnTextfieldEditFailed() {} +// static +gfx::Range PlatformStyle::RangeToDeleteBackwards(const base::string16& text, + size_t cursor_position) { + // Delete one code point, which may be two UTF-16 words. + size_t previous_grapheme_index = + gfx::UTF16OffsetToIndex(text, cursor_position, -1); + return gfx::Range(cursor_position, previous_grapheme_index); +} + #endif // OS_MACOSX #if !defined(DESKTOP_LINUX) diff --git a/chromium/ui/views/style/platform_style.h b/chromium/ui/views/style/platform_style.h index d5b7e3bf951..25710b7968e 100644 --- a/chromium/ui/views/style/platform_style.h +++ b/chromium/ui/views/style/platform_style.h @@ -11,6 +11,10 @@ #include "ui/views/controls/button/button.h" #include "ui/views/views_export.h" +namespace gfx { +class Range; +} // namespace gfx + namespace views { class Border; @@ -66,6 +70,10 @@ class VIEWS_EXPORT PlatformStyle { // dragging but available to do so. static const bool kTextfieldUsesDragCursorWhenDraggable; + // Whether bookmarks in the bookmarks bar are elided [and show elipses at the + // tail] or fade out. + static const bool kShouldElideBookmarksInBookmarksBar; + // Creates the default scrollbar for the given orientation. static std::unique_ptr<ScrollBar> CreateScrollBar(bool is_horizontal); @@ -83,6 +91,14 @@ class VIEWS_EXPORT PlatformStyle { // the failed edit if platform-appropriate. static void OnTextfieldEditFailed(); + // When deleting backwards in |string| with the cursor at index + // |cursor_position|, return the range of UTF-16 words to be deleted. + // This is to support deleting entire graphemes instead of individual + // characters when necessary on Mac, and code points made from surrogate + // pairs on other platforms. + static gfx::Range RangeToDeleteBackwards(const base::string16& text, + size_t cursor_position); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformStyle); }; diff --git a/chromium/ui/views/style/platform_style_mac.mm b/chromium/ui/views/style/platform_style_mac.mm index 5d14515be15..36cdfff642d 100644 --- a/chromium/ui/views/style/platform_style_mac.mm +++ b/chromium/ui/views/style/platform_style_mac.mm @@ -4,7 +4,7 @@ #include "ui/views/style/platform_style.h" -#include "base/memory/ptr_util.h" +#include "base/strings/sys_string_conversions.h" #include "ui/base/ui_features.h" #include "ui/gfx/color_utils.h" #include "ui/views/controls/button/label_button.h" @@ -12,6 +12,24 @@ #import <Cocoa/Cocoa.h> +extern "C" { +// From CFString private headers. +typedef CF_ENUM(CFIndex, CFStringCharacterClusterType) { + kCFStringGraphemeCluster = 1, /* Unicode Grapheme Cluster */ + kCFStringComposedCharacterCluster = + 2, /* Compose all non-base (including spacing marks) */ + kCFStringCursorMovementCluster = + 3, /* Cluster suitable for cursor movements */ + kCFStringBackwardDeletionCluster = + 4 /* Cluster suitable for backward deletion */ +}; + +CFRange CFStringGetRangeOfCharacterClusterAtIndex( + CFStringRef string, + CFIndex index, + CFStringCharacterClusterType type); +} + namespace views { const int PlatformStyle::kMinLabelButtonWidth = 32; @@ -22,6 +40,7 @@ const bool PlatformStyle::kSelectAllOnRightClickWhenUnfocused = true; const bool PlatformStyle::kTextfieldScrollsToStartOnFocusChange = true; const bool PlatformStyle::kTextfieldUsesDragCursorWhenDraggable = false; const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = true; +const bool PlatformStyle::kShouldElideBookmarksInBookmarksBar = true; const bool PlatformStyle::kUseRipples = false; const Button::NotifyAction PlatformStyle::kMenuNotifyActivationAction = @@ -44,4 +63,23 @@ void PlatformStyle::OnTextfieldEditFailed() { NSBeep(); } +// static +gfx::Range PlatformStyle::RangeToDeleteBackwards(const base::string16& text, + size_t cursor_position) { + if (cursor_position == 0) + return gfx::Range(); + + base::ScopedCFTypeRef<CFStringRef> cf_string( + CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, text.data(), + text.size(), kCFAllocatorNull)); + CFRange range_to_delete = CFStringGetRangeOfCharacterClusterAtIndex( + cf_string, cursor_position - 1, kCFStringBackwardDeletionCluster); + + if (range_to_delete.location == NSNotFound) + return gfx::Range(); + + // The range needs to be reversed to undo correctly. + return gfx::Range(range_to_delete.location + range_to_delete.length, + range_to_delete.location); +} } // namespace views diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.cc b/chromium/ui/views/touchui/touch_selection_controller_impl.cc index 4fa1a8a98e8..6e5d1816bff 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.cc @@ -210,16 +210,9 @@ typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView; // cursor and expanding the hit-test area just below the visible drag handle. class TouchHandleWindowTargeter : public aura::WindowTargeter { public: - TouchHandleWindowTargeter() = default; - ~TouchHandleWindowTargeter() override = default; - void SetHitTestOffset(int offset) { - const gfx::Insets insets(offset, 0, -offset, 0); - SetInsets(insets, insets); + SetInsets(gfx::Insets(offset, 0, -offset, 0)); } - - private: - DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter); }; // A View that displays the text selection handle. diff --git a/chromium/ui/views/vector_icons/checkbox_active.icon b/chromium/ui/views/vector_icons/checkbox_active.icon index 199c3fd90d0..d9ffe0349cf 100644 --- a/chromium/ui/views/vector_icons/checkbox_active.icon +++ b/chromium/ui/views/vector_icons/checkbox_active.icon @@ -23,6 +23,4 @@ LINE_TO, 4.5f, 7, LINE_TO, 6.5f, 9, LINE_TO, 11.5f, 4.5f, LINE_TO, 12.5f, 5.5f, -CLOSE, - -END +CLOSE diff --git a/chromium/ui/views/vector_icons/checkbox_normal.icon b/chromium/ui/views/vector_icons/checkbox_normal.icon index 0b1efe54c03..8ea38e0107d 100644 --- a/chromium/ui/views/vector_icons/checkbox_normal.icon +++ b/chromium/ui/views/vector_icons/checkbox_normal.icon @@ -22,6 +22,4 @@ CUBIC_TO, 13.551f, 14, 14, 13.551f, 14, 12.996f, V_LINE_TO, 3.004f, CUBIC_TO, 14, 2.449f, 13.551f, 2, 12.996f, 2, LINE_TO, 12.996f, 2, -CLOSE, - -END +CLOSE diff --git a/chromium/ui/views/vector_icons/close.icon b/chromium/ui/views/vector_icons/close.icon new file mode 100644 index 00000000000..0786371e70d --- /dev/null +++ b/chromium/ui/views/vector_icons/close.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 12.07f, 6.8f, +LINE_TO, 10, 8.87f, +LINE_TO, 7.93f, 6.8f, +LINE_TO, 6.8f, 7.93f, +LINE_TO, 8.87f, 10, +LINE_TO, 6.8f, 12.07f, +LINE_TO, 7.93f, 13.2f, +LINE_TO, 10, 11.13f, +R_LINE_TO, 2.07f, 2.07f, +R_LINE_TO, 1.13f, -1.13f, +LINE_TO, 11.13f, 10, +LINE_TO, 13.2f, 7.93f, +LINE_TO, 12.07f, 6.8f, +CLOSE, +MOVE_TO, 10, 2, +R_CUBIC_TO, -4.42f, 0, -8, 3.58f, -8, 8, +R_CUBIC_TO, 0, 4.42f, 3.58f, 8, 8, 8, +R_CUBIC_TO, 4.42f, 0, 8, -3.58f, 8, -8, +R_CUBIC_TO, 0, -4.42f, -3.58f, -8, -8, -8, +CLOSE, +R_MOVE_TO, 0, 14, +R_CUBIC_TO, -3.31f, 0, -6, -2.69f, -6, -6, +R_CUBIC_TO, 0, -3.31f, 2.69f, -6, 6, -6, +R_CUBIC_TO, 3.31f, 0, 6, 2.69f, 6, 6, +R_CUBIC_TO, 0, 3.31f, -2.69f, 6, -6, 6, +CLOSE diff --git a/chromium/ui/views/vector_icons/ic_close.icon b/chromium/ui/views/vector_icons/ic_close.icon new file mode 100644 index 00000000000..686da72bcda --- /dev/null +++ b/chromium/ui/views/vector_icons/ic_close.icon @@ -0,0 +1,33 @@ +// 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. + +CANVAS_DIMENSIONS, 48, +MOVE_TO, 38, 12.82f, +LINE_TO, 35.18f, 10, +LINE_TO, 24, 21.18f, +LINE_TO, 12.82f, 10, +LINE_TO, 10, 12.82f, +LINE_TO, 21.18f, 24, +LINE_TO, 10, 35.18f, +LINE_TO, 12.82f, 38, +LINE_TO, 24, 26.82f, +LINE_TO, 35.18f, 38, +LINE_TO, 38, 35.18f, +LINE_TO, 26.82f, 24, +CLOSE + +CANVAS_DIMENSIONS, 24, +MOVE_TO, 19, 6.41f, +LINE_TO, 17.59f, 5, +LINE_TO, 12, 10.59f, +LINE_TO, 6.41f, 5, +LINE_TO, 5, 6.41f, +LINE_TO, 10.59f, 12, +LINE_TO, 5, 17.59f, +LINE_TO, 6.41f, 19, +LINE_TO, 12, 13.41f, +LINE_TO, 17.59f, 19, +LINE_TO, 19, 17.59f, +LINE_TO, 13.41f, 12, +CLOSE diff --git a/chromium/ui/views/vector_icons/info.icon b/chromium/ui/views/vector_icons/info.icon new file mode 100644 index 00000000000..2044827534a --- /dev/null +++ b/chromium/ui/views/vector_icons/info.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 9, 14, +R_H_LINE_TO, 2, +R_V_LINE_TO, -4, +H_LINE_TO, 9, +R_V_LINE_TO, 4, +CLOSE, +R_MOVE_TO, 1, -12, +R_CUBIC_TO, -4.42f, 0, -8, 3.58f, -8, 8, +R_CUBIC_TO, 0, 4.42f, 3.58f, 8, 8, 8, +R_CUBIC_TO, 4.42f, 0, 8, -3.58f, 8, -8, +R_CUBIC_TO, 0, -4.42f, -3.58f, -8, -8, -8, +CLOSE, +R_MOVE_TO, 0, 14, +R_CUBIC_TO, -3.31f, 0, -6, -2.69f, -6, -6, +R_CUBIC_TO, 0, -3.31f, 2.69f, -6, 6, -6, +R_CUBIC_TO, 3.31f, 0, 6, 2.69f, 6, 6, +R_CUBIC_TO, 0, 3.31f, -2.69f, 6, -6, 6, +CLOSE, +MOVE_TO, 9, 8, +R_H_LINE_TO, 2, +V_LINE_TO, 6, +H_LINE_TO, 9, +R_V_LINE_TO, 2, +CLOSE diff --git a/chromium/ui/views/vector_icons/launch.icon b/chromium/ui/views/vector_icons/launch.icon new file mode 100644 index 00000000000..8c97192cc12 --- /dev/null +++ b/chromium/ui/views/vector_icons/launch.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 16, 16, +H_LINE_TO, 4, +V_LINE_TO, 4, +R_H_LINE_TO, 6, +V_LINE_TO, 2, +H_LINE_TO, 4, +CUBIC_TO, 3, 2, 2, 3, 2, 4, +R_V_LINE_TO, 12, +R_CUBIC_TO, 0, 1, 1, 2, 2, 2, +R_H_LINE_TO, 12, +R_CUBIC_TO, 1, 0, 2, -1, 2, -2, +R_V_LINE_TO, -6, +R_H_LINE_TO, -2, +R_V_LINE_TO, 6, +CLOSE, +MOVE_TO, 12, 2, +R_V_LINE_TO, 2, +R_H_LINE_TO, 2.5f, +LINE_TO, 6, 12.5f, +LINE_TO, 7.5f, 14, +LINE_TO, 16, 5.5f, +V_LINE_TO, 8, +R_H_LINE_TO, 2, +V_LINE_TO, 2, +R_H_LINE_TO, -6, +CLOSE diff --git a/chromium/ui/views/vector_icons/menu_check.1x.icon b/chromium/ui/views/vector_icons/menu_check.1x.icon deleted file mode 100644 index 739f6682e31..00000000000 --- a/chromium/ui/views/vector_icons/menu_check.1x.icon +++ /dev/null @@ -1,14 +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. - -CANVAS_DIMENSIONS, 16, -MOVE_TO, 2, 9.22f, -R_LINE_TO, 1.36f, -1.38f, -R_LINE_TO, 2.4f, 2.48f, -R_LINE_TO, 7.2f, -7.4f, -R_LINE_TO, 1.35f, 1.39f, -R_LINE_TO, -8.55f, 8.78f, -LINE_TO, 2, 9.22f, -CLOSE, -END diff --git a/chromium/ui/views/vector_icons/menu_check.icon b/chromium/ui/views/vector_icons/menu_check.icon index c06a2ccf0ee..3e24215cc5a 100644 --- a/chromium/ui/views/vector_icons/menu_check.icon +++ b/chromium/ui/views/vector_icons/menu_check.icon @@ -10,5 +10,14 @@ LINE_TO, 25.48f, 7, LINE_TO, 28, 9.59f, LINE_TO, 12.02f, 26, LINE_TO, 5, 18.78f, -CLOSE, -END +CLOSE + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 2, 9.22f, +R_LINE_TO, 1.36f, -1.38f, +R_LINE_TO, 2.4f, 2.48f, +R_LINE_TO, 7.2f, -7.4f, +R_LINE_TO, 1.35f, 1.39f, +R_LINE_TO, -8.55f, 8.78f, +LINE_TO, 2, 9.22f, +CLOSE diff --git a/chromium/ui/views/vector_icons/menu_radio_empty.icon b/chromium/ui/views/vector_icons/menu_radio_empty.icon index 62ef2daec82..047257b4759 100644 --- a/chromium/ui/views/vector_icons/menu_radio_empty.icon +++ b/chromium/ui/views/vector_icons/menu_radio_empty.icon @@ -4,5 +4,4 @@ CANVAS_DIMENSIONS, 32, CIRCLE, 16, 16, 14, -CIRCLE, 16, 16, 11, -END +CIRCLE, 16, 16, 11 diff --git a/chromium/ui/views/vector_icons/menu_radio_selected.icon b/chromium/ui/views/vector_icons/menu_radio_selected.icon index 0533b99ba7e..5f94564ccec 100644 --- a/chromium/ui/views/vector_icons/menu_radio_selected.icon +++ b/chromium/ui/views/vector_icons/menu_radio_selected.icon @@ -5,5 +5,4 @@ CANVAS_DIMENSIONS, 32, CIRCLE, 16, 16, 14, CIRCLE, 16, 16, 11, -CIRCLE, 16, 16, 7, -END +CIRCLE, 16, 16, 7 diff --git a/chromium/ui/views/vector_icons/new_incognito_window.icon b/chromium/ui/views/vector_icons/new_incognito_window.icon new file mode 100644 index 00000000000..68d83e4898f --- /dev/null +++ b/chromium/ui/views/vector_icons/new_incognito_window.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 8.13f, 2.04f, +LINE_TO, 10, 2.67f, +R_LINE_TO, 1.85f, -0.62f, +R_ARC_TO, 0.89f, 0.89f, 0, 0, 1, 1.11f, 0.54f, +LINE_TO, 15, 8, +H_LINE_TO, 5, +R_LINE_TO, 2.03f, -5.42f, +R_ARC_TO, 0.89f, 0.89f, 0, 0, 1, 1.11f, -0.54f, +CLOSE, +MOVE_TO, 18, 10, +H_LINE_TO, 2, +V_LINE_TO, 9, +R_H_LINE_TO, 16, +R_V_LINE_TO, 1, +CLOSE, +R_MOVE_TO, -4.06f, 7, +R_ARC_TO, 3.03f, 3.03f, 0, 0, 1, -3.03f, -2.75f, +R_CUBIC_TO, -0.84f, -0.53f, -1.55f, -0.2f, -1.8f, -0.01f, +CUBIC_TO, 8.97f, 15.78f, 7.67f, 17, 6.06f, 17, +CUBIC_TO, 4.37f, 17, 3, 15.65f, 3, 14, +R_CUBIC_TO, 0, -1.65f, 1.37f, -3, 3.06f, -3, +R_CUBIC_TO, 1.45f, 0, 2.65f, 0.98f, 2.98f, 2.31f, +R_ARC_TO, 2.45f, 2.45f, 0, 0, 1, 1.92f, 0.01f, +ARC_TO, 3.04f, 3.04f, 0, 0, 1, 13.94f, 11, +CUBIC_TO, 15.63f, 11, 17, 12.35f, 17, 14, +R_CUBIC_TO, 0, 1.65f, -1.37f, 3, -3.06f, 3, +CLOSE, +MOVE_TO, 14, 16, +R_ARC_TO, 2, 2, 0, 1, 0, 0, -4, +R_ARC_TO, 2, 2, 0, 0, 0, 0, 4, +CLOSE, +R_MOVE_TO, -8, 0, +R_ARC_TO, 2, 2, 0, 1, 0, 0, -4, +R_ARC_TO, 2, 2, 0, 0, 0, 0, 4, +CLOSE diff --git a/chromium/ui/views/vector_icons/new_tab.icon b/chromium/ui/views/vector_icons/new_tab.icon new file mode 100644 index 00000000000..51bbe0ecbfd --- /dev/null +++ b/chromium/ui/views/vector_icons/new_tab.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 4, 2, +R_H_LINE_TO, 12, +R_ARC_TO, 2, 2, 0, 0, 1, 2, 2, +R_V_LINE_TO, 12, +R_ARC_TO, 2, 2, 0, 0, 1, -2, 2, +H_LINE_TO, 4, +R_ARC_TO, 2, 2, 0, 0, 1, -2, -2, +V_LINE_TO, 4, +R_ARC_TO, 2, 2, 0, 0, 1, 2, -2, +CLOSE, +R_MOVE_TO, 0, 2, +R_V_LINE_TO, 12, +R_H_LINE_TO, 12, +V_LINE_TO, 4, +H_LINE_TO, 4, +CLOSE, +R_MOVE_TO, 4, 0, +R_H_LINE_TO, 8, +R_V_LINE_TO, 4, +R_H_LINE_TO, -6, +R_ARC_TO, 2, 2, 0, 0, 1, -2, -2, +V_LINE_TO, 4, +CLOSE diff --git a/chromium/ui/views/vector_icons/new_window.icon b/chromium/ui/views/vector_icons/new_window.icon new file mode 100644 index 00000000000..8d1e690424d --- /dev/null +++ b/chromium/ui/views/vector_icons/new_window.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 15, 5, +R_H_LINE_TO, 3, +R_V_LINE_TO, 2, +R_H_LINE_TO, -3, +R_V_LINE_TO, 3, +R_H_LINE_TO, -2, +V_LINE_TO, 7, +R_H_LINE_TO, -3, +V_LINE_TO, 5, +R_H_LINE_TO, 3, +V_LINE_TO, 2, +R_H_LINE_TO, 2, +R_V_LINE_TO, 3, +CLOSE, +R_MOVE_TO, 1, 11, +R_V_LINE_TO, -4, +R_H_LINE_TO, 2, +R_V_LINE_TO, 4, +R_CUBIC_TO, 0, 1, -1, 2, -2, 2, +H_LINE_TO, 4, +R_CUBIC_TO, -1, 0, -2, -1, -2, -2, +V_LINE_TO, 4, +R_CUBIC_TO, 0, -1, 1, -2, 2, -2, +R_H_LINE_TO, 4, +R_V_LINE_TO, 2, +H_LINE_TO, 4, +R_V_LINE_TO, 12, +R_H_LINE_TO, 12, +CLOSE diff --git a/chromium/ui/views/vector_icons/open.icon b/chromium/ui/views/vector_icons/open.icon new file mode 100644 index 00000000000..fa352617a2f --- /dev/null +++ b/chromium/ui/views/vector_icons/open.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 6, 4, +R_V_LINE_TO, 2, +R_H_LINE_TO, 6.5f, +LINE_TO, 4, 14.5f, +LINE_TO, 5.5f, 16, +LINE_TO, 14, 7.5f, +V_LINE_TO, 14, +R_H_LINE_TO, 2, +V_LINE_TO, 4, +CLOSE diff --git a/chromium/ui/views/vector_icons/options.icon b/chromium/ui/views/vector_icons/options.icon new file mode 100644 index 00000000000..fbd0aa537f5 --- /dev/null +++ b/chromium/ui/views/vector_icons/options.icon @@ -0,0 +1,17 @@ +// 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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 14, 10, +R_ARC_TO, 2, 2, 0, 1, 1, 4, 0, +ARC_TO, 2, 2, 0, 0, 1, 14, 10, +CLOSE, +R_MOVE_TO, -2, 0, +R_ARC_TO, 2, 2, 0, 1, 1, -4, 0, +ARC_TO, 2, 2, 0, 0, 1, 12, 10, +CLOSE, +R_MOVE_TO, -6, 0, +R_ARC_TO, 2, 2, 0, 1, 1, -4, 0, +ARC_TO, 2, 2, 0, 0, 1, 6, 10, +CLOSE diff --git a/chromium/ui/views/vector_icons/pin.icon b/chromium/ui/views/vector_icons/pin.icon new file mode 100644 index 00000000000..24cc87a2cf3 --- /dev/null +++ b/chromium/ui/views/vector_icons/pin.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 14.8f, 7.76f, +LINE_TO, 12.24f, 5.2f, +R_LINE_TO, -5.01f, 5.01f, +H_LINE_TO, 5.73f, +LINE_TO, 9.79f, 14.27f, +V_LINE_TO, 12.77f, +LINE_TO, 14.8f, 7.76f, +CLOSE, +R_MOVE_TO, 3.2f, 0.64f, +R_LINE_TO, -1.28f, 1.28f, +R_LINE_TO, -0.64f, -0.64f, +R_LINE_TO, -4.48f, 4.48f, +R_V_LINE_TO, 2.56f, +R_LINE_TO, -1.28f, 1.28f, +R_LINE_TO, -3.2f, -3.2f, +LINE_TO, 3.28f, 18, +H_LINE_TO, 2, +R_V_LINE_TO, -1.28f, +R_LINE_TO, 3.84f, -3.84f, +R_LINE_TO, -3.2f, -3.2f, +LINE_TO, 3.92f, 8.4f, +R_H_LINE_TO, 2.56f, +R_LINE_TO, 4.48f, -4.48f, +R_LINE_TO, -0.64f, -0.64f, +LINE_TO, 11.6f, 2, +LINE_TO, 18, 8.4f, +CLOSE diff --git a/chromium/ui/views/vector_icons/radio_button_active.icon b/chromium/ui/views/vector_icons/radio_button_active.icon index 0533b99ba7e..5f94564ccec 100644 --- a/chromium/ui/views/vector_icons/radio_button_active.icon +++ b/chromium/ui/views/vector_icons/radio_button_active.icon @@ -5,5 +5,4 @@ CANVAS_DIMENSIONS, 32, CIRCLE, 16, 16, 14, CIRCLE, 16, 16, 11, -CIRCLE, 16, 16, 7, -END +CIRCLE, 16, 16, 7 diff --git a/chromium/ui/views/vector_icons/radio_button_normal.icon b/chromium/ui/views/vector_icons/radio_button_normal.icon index 62ef2daec82..047257b4759 100644 --- a/chromium/ui/views/vector_icons/radio_button_normal.icon +++ b/chromium/ui/views/vector_icons/radio_button_normal.icon @@ -4,5 +4,4 @@ CANVAS_DIMENSIONS, 32, CIRCLE, 16, 16, 14, -CIRCLE, 16, 16, 11, -END +CIRCLE, 16, 16, 11 diff --git a/chromium/ui/views/vector_icons/submenu_arrow.1x.icon b/chromium/ui/views/vector_icons/submenu_arrow.1x.icon deleted file mode 100644 index c964eeed576..00000000000 --- a/chromium/ui/views/vector_icons/submenu_arrow.1x.icon +++ /dev/null @@ -1,12 +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. - -CANVAS_DIMENSIONS, 8, -FLIPS_IN_RTL, -MOVE_TO, 2, 8, -R_LINE_TO, 5, -4, -R_LINE_TO, -5, -4, -R_V_LINE_TO, 8, -CLOSE, -END diff --git a/chromium/ui/views/vector_icons/submenu_arrow.icon b/chromium/ui/views/vector_icons/submenu_arrow.icon index 2ebf8a3bb8a..9d633095483 100644 --- a/chromium/ui/views/vector_icons/submenu_arrow.icon +++ b/chromium/ui/views/vector_icons/submenu_arrow.icon @@ -8,5 +8,12 @@ MOVE_TO, 3, 16, R_LINE_TO, 11, -8, LINE_TO, 3, 0, R_V_LINE_TO, 16, -CLOSE, -END +CLOSE + +CANVAS_DIMENSIONS, 8, +FLIPS_IN_RTL, +MOVE_TO, 2, 8, +R_LINE_TO, 5, -4, +R_LINE_TO, -5, -4, +R_V_LINE_TO, 8, +CLOSE diff --git a/chromium/ui/views/vector_icons/uninstall.icon b/chromium/ui/views/vector_icons/uninstall.icon new file mode 100644 index 00000000000..4e567278b0d --- /dev/null +++ b/chromium/ui/views/vector_icons/uninstall.icon @@ -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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 13, 3, +V_LINE_TO, 2, +H_LINE_TO, 7, +R_V_LINE_TO, 1, +H_LINE_TO, 3, +R_V_LINE_TO, 2, +R_H_LINE_TO, 1, +R_V_LINE_TO, 11, +R_CUBIC_TO, 0, 1.1f, 0.9f, 2, 2, 2, +R_H_LINE_TO, 8, +R_CUBIC_TO, 1.1f, 0, 2, -0.9f, 2, -2, +V_LINE_TO, 5, +R_H_LINE_TO, 1, +V_LINE_TO, 3, +R_H_LINE_TO, -4, +CLOSE, +R_MOVE_TO, 1, 13, +H_LINE_TO, 6, +V_LINE_TO, 5, +R_H_LINE_TO, 8, +R_V_LINE_TO, 11, +CLOSE diff --git a/chromium/ui/views/vector_icons/unpin.icon b/chromium/ui/views/vector_icons/unpin.icon new file mode 100644 index 00000000000..c8ee6fd29b6 --- /dev/null +++ b/chromium/ui/views/vector_icons/unpin.icon @@ -0,0 +1,23 @@ +// 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. + +CANVAS_DIMENSIONS, 20, +MOVE_TO, 18, 8.4f, +R_LINE_TO, -1.28f, 1.28f, +R_LINE_TO, -0.64f, -0.64f, +R_LINE_TO, -4.48f, 4.48f, +R_V_LINE_TO, 2.56f, +R_LINE_TO, -1.28f, 1.28f, +R_LINE_TO, -3.2f, -3.2f, +LINE_TO, 3.28f, 18, +H_LINE_TO, 2, +R_V_LINE_TO, -1.28f, +R_LINE_TO, 3.84f, -3.84f, +R_LINE_TO, -3.2f, -3.2f, +LINE_TO, 3.92f, 8.4f, +R_H_LINE_TO, 2.56f, +R_LINE_TO, 4.48f, -4.48f, +R_LINE_TO, -0.64f, -0.64f, +LINE_TO, 11.6f, 2, +CLOSE diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index 8568fae0fc1..f544882a25c 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -12,7 +12,6 @@ #include "base/containers/adapters.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" @@ -437,9 +436,7 @@ void View::SetPreferredSize(const gfx::Size& size) { } void View::SizeToPreferredSize() { - gfx::Size pref_size = GetPreferredSize(); - if ((pref_size.width() != width()) || (pref_size.height() != height())) - SetBounds(x(), y(), pref_size.width(), pref_size.height()); + SetSize(GetPreferredSize()); } gfx::Size View::GetMinimumSize() const { diff --git a/chromium/ui/views/view_tracker_unittest.cc b/chromium/ui/views/view_tracker_unittest.cc index de14208dfd7..ab06814fc79 100644 --- a/chromium/ui/views/view_tracker_unittest.cc +++ b/chromium/ui/views/view_tracker_unittest.cc @@ -4,7 +4,6 @@ #include "ui/views/view_tracker.h" -#include "base/memory/ptr_util.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view.h" diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc index 82631bbd6ce..a294b7f2be1 100644 --- a/chromium/ui/views/view_unittest.cc +++ b/chromium/ui/views/view_unittest.cc @@ -323,6 +323,23 @@ TEST_F(ViewTest, LayoutCalledInvalidateAndOriginChanges) { EXPECT_TRUE(child->did_layout_); } +// Tests that SizeToPreferredSize will trigger a Layout if the size has changed +// or if layout is marked invalid. +TEST_F(ViewTest, SizeToPreferredSizeInducesLayout) { + TestView example_view; + example_view.SetPreferredSize(gfx::Size(101, 102)); + example_view.SizeToPreferredSize(); + EXPECT_TRUE(example_view.did_layout_); + + example_view.Reset(); + example_view.SizeToPreferredSize(); + EXPECT_FALSE(example_view.did_layout_); + + example_view.InvalidateLayout(); + example_view.SizeToPreferredSize(); + EXPECT_TRUE(example_view.did_layout_); +} + //////////////////////////////////////////////////////////////////////////////// // OnBoundsChanged //////////////////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc index 964f1390be0..a64b2cb2551 100644 --- a/chromium/ui/views/views_delegate.cc +++ b/chromium/ui/views/views_delegate.cc @@ -5,7 +5,6 @@ #include "ui/views/views_delegate.h" #include "base/command_line.h" -#include "base/memory/ptr_util.h" #include "build/build_config.h" #include "ui/views/views_touch_selection_controller_factory.h" #include "ui/views/widget/native_widget_private.h" diff --git a/chromium/ui/views/views_perftests.cc b/chromium/ui/views/views_perftests.cc index 65a61183415..6740c887c41 100644 --- a/chromium/ui/views/views_perftests.cc +++ b/chromium/ui/views/views_perftests.cc @@ -11,5 +11,6 @@ int main(int argc, char** argv) { mojo::edk::Init(); return base::LaunchUnitTestsSerially( argc, argv, - base::Bind(&views::ViewsTestSuite::Run, base::Unretained(&test_suite))); + base::BindOnce(&views::ViewsTestSuite::Run, + base::Unretained(&test_suite))); } diff --git a/chromium/ui/views/views_test_suite.cc b/chromium/ui/views/views_test_suite.cc index 82c406c5ea9..44f34e279a4 100644 --- a/chromium/ui/views/views_test_suite.cc +++ b/chromium/ui/views/views_test_suite.cc @@ -31,12 +31,14 @@ ViewsTestSuite::~ViewsTestSuite() {} int ViewsTestSuite::RunTests() { return base::LaunchUnitTests( - argc_, argv_, base::Bind(&ViewsTestSuite::Run, base::Unretained(this))); + argc_, argv_, + base::BindOnce(&ViewsTestSuite::Run, base::Unretained(this))); } int ViewsTestSuite::RunTestsSerially() { return base::LaunchUnitTestsSerially( - argc_, argv_, base::Bind(&ViewsTestSuite::Run, base::Unretained(this))); + argc_, argv_, + base::BindOnce(&ViewsTestSuite::Run, base::Unretained(this))); } void ViewsTestSuite::Initialize() { diff --git a/chromium/ui/views/widget/desktop_aura/OWNERS b/chromium/ui/views/widget/desktop_aura/OWNERS index deec8966b35..9069e120fb6 100644 --- a/chromium/ui/views/widget/desktop_aura/OWNERS +++ b/chromium/ui/views/widget/desktop_aura/OWNERS @@ -1,3 +1,2 @@ -# Elliot is the owner of all the X11 stuff. -per-file *x11*=erg@chromium.org -per-file window_event_filter*=erg@chromium.org +per-file *x11*=thomasanderson@chromium.org +per-file window_event_filter*=thomasanderson@chromium.org 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 55dcdf780eb..ad0123acabc 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 @@ -7,7 +7,6 @@ #include <stddef.h> #include <stdint.h> -#include "base/event_types.h" #include "base/lazy_instance.h" #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -30,6 +29,7 @@ #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/platform/platform_event_source.h" +#include "ui/events/platform_event.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_atom_cache.h" 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 5158b0d4e37..3a7edcd000f 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 @@ -4,7 +4,6 @@ #include "ui/views/widget/desktop_aura/desktop_drop_target_win.h" -#include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/win/win_util.h" #include "ui/aura/client/drag_drop_client.h" 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 cde45a41be5..bb391ec3b33 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 @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "services/ui/public/interfaces/window_manager_constants.mojom.h" @@ -348,7 +347,8 @@ void DesktopNativeWidgetAura::OnDesktopWindowTreeHostDestroyed( } void DesktopNativeWidgetAura::HandleActivationChanged(bool active) { - native_widget_delegate_->OnNativeWidgetActivationChanged(active); + if (!native_widget_delegate_->OnNativeWidgetActivationChanged(active)) + return; wm::ActivationClient* activation_client = wm::GetActivationClient(host_->window()); if (!activation_client) @@ -432,7 +432,7 @@ void DesktopNativeWidgetAura::InitNativeWidget( } host_.reset(desktop_window_tree_host_->AsWindowTreeHost()); } - desktop_window_tree_host_->Init(content_window_, params); + desktop_window_tree_host_->Init(params); host_->window()->AddChild(content_window_); host_->window()->SetProperty(kDesktopNativeWidgetAuraKey, this); @@ -473,6 +473,8 @@ void DesktopNativeWidgetAura::InitNativeWidget( aura::client::SetCursorClient(host_->window(), cursor_manager_); } + host_->window()->SetName(params.name); + content_window_->SetName("DesktopNativeWidgetAura - content window"); desktop_window_tree_host_->OnNativeWidgetCreated(params); UpdateWindowTransparency(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h index 812a37dac77..fc4d17f3285 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.h @@ -51,6 +51,8 @@ class FocusManagerEventHandler; class TooltipManagerAura; class WindowReorderer; +// DesktopNativeWidgetAura handles top-level widgets on Windows, Linux, and +// Chrome OS with mash. class VIEWS_EXPORT DesktopNativeWidgetAura : public internal::NativeWidgetPrivate, public aura::WindowDelegate, 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 0e8bd99e750..e9ddabeeda6 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 @@ -134,6 +134,31 @@ TEST_F(DesktopNativeWidgetAuraTest, WidgetNotVisibleOnlyWindowTreeHostShown) { EXPECT_FALSE(widget.IsVisible()); } +TEST_F(DesktopNativeWidgetAuraTest, DesktopAuraWindowShowFrameless) { + Widget widget; + Widget::InitParams init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); + init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + init_params.native_widget = new DesktopNativeWidgetAura(&widget); + widget.Init(init_params); + + // Make sure that changing frame type doesn't crash when there's no non-client + // view. + ASSERT_EQ(nullptr, widget.non_client_view()); + widget.DebugToggleFrameType(); + widget.Show(); + +#if defined(OS_WIN) + // On Windows also make sure that handling WM_SYSCOMMAND doesn't crash with + // custom frame. Frame type needs to be toggled again if Aero Glass is + // disabled. + if (widget.ShouldUseNativeFrame()) + widget.DebugToggleFrameType(); + SendMessage(widget.GetNativeWindow()->GetHost()->GetAcceleratedWidget(), + WM_SYSCOMMAND, SC_RESTORE, 0); +#endif // OS_WIN +} + // Verify that the cursor state is shared between two native widgets. TEST_F(DesktopNativeWidgetAuraTest, GlobalCursorState) { // Create two native widgets, each owning different root windows. @@ -276,8 +301,8 @@ TEST_F(DesktopNativeWidgetAuraTest, WidgetCanBeDestroyedFromNestedLoop) { base::Closure quit_runloop = run_loop.QuitClosure(); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&QuitNestedLoopAndCloseWidget, base::Passed(&widget), - base::Unretained(&quit_runloop))); + base::BindOnce(&QuitNestedLoopAndCloseWidget, std::move(widget), + base::Unretained(&quit_runloop))); run_loop.Run(); } 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 17464aafc78..2822c8e41ef 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -6,6 +6,8 @@ #include "base/command_line.h" #include "base/logging.h" +#include "base/memory/protected_memory_cfi.h" +#include "base/strings/stringprintf.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "ui/aura/window.h" @@ -33,14 +35,22 @@ #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" +#include <dlfcn.h> + namespace { // static -gfx::ICCProfile GetICCProfileFromBestMonitor() { +gfx::ICCProfile GetICCProfileForMonitor(int monitor) { gfx::ICCProfile icc_profile; if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kHeadless)) return icc_profile; - Atom property = gfx::GetAtom("_ICC_PROFILE"); + std::string atom_name; + if (monitor == 0) { + atom_name = "_ICC_PROFILE"; + } else { + atom_name = base::StringPrintf("_ICC_PROFILE_%d", monitor); + } + Atom property = gfx::GetAtom(atom_name.c_str()); if (property != x11::None) { Atom prop_type = x11::None; int prop_format = 0; @@ -108,7 +118,7 @@ namespace views { DesktopScreenX11::DesktopScreenX11() : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), - has_xrandr_(false), + xrandr_version_(0), xrandr_event_base_(0), primary_display_index_(0), weak_factory_(this) { @@ -118,12 +128,11 @@ DesktopScreenX11::DesktopScreenX11() // use the new interface instead of the 1.2 one. int randr_version_major = 0; int randr_version_minor = 0; - has_xrandr_ = XRRQueryVersion( - xdisplay_, &randr_version_major, &randr_version_minor) && - randr_version_major == 1 && - randr_version_minor >= 3; - - if (has_xrandr_) { + if (XRRQueryVersion(xdisplay_, &randr_version_major, &randr_version_minor)) { + xrandr_version_ = randr_version_major * 100 + randr_version_minor; + } + // Need at least xrandr version 1.3. + if (xrandr_version_ >= 103) { int error_base_ignored = 0; XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored); @@ -144,7 +153,7 @@ DesktopScreenX11::DesktopScreenX11() DesktopScreenX11::~DesktopScreenX11() { if (views::LinuxUI::instance()) views::LinuxUI::instance()->AddDeviceScaleFactorObserver(this); - if (has_xrandr_ && ui::PlatformEventSource::GetInstance()) + if (xrandr_version_ >= 103 && ui::PlatformEventSource::GetInstance()) ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); } @@ -297,7 +306,7 @@ DesktopScreenX11::DesktopScreenX11( const std::vector<display::Display>& test_displays) : xdisplay_(gfx::GetXDisplay()), x_root_window_(DefaultRootWindow(xdisplay_)), - has_xrandr_(false), + xrandr_version_(0), xrandr_event_base_(0), displays_(test_displays), primary_display_index_(0), @@ -306,8 +315,16 @@ DesktopScreenX11::DesktopScreenX11( views::LinuxUI::instance()->AddDeviceScaleFactorObserver(this); } +typedef XRRMonitorInfo* (*XRRGetMonitors)(::Display*, Window, bool, int*); +typedef void (*XRRFreeMonitors)(XRRMonitorInfo*); + +PROTECTED_MEMORY_SECTION base::ProtectedMemory<XRRGetMonitors> + g_XRRGetMonitors_ptr; +PROTECTED_MEMORY_SECTION base::ProtectedMemory<XRRFreeMonitors> + g_XRRFreeMonitors_ptr; + std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { - DCHECK(has_xrandr_); + DCHECK(xrandr_version_ >= 103); std::vector<display::Display> displays; gfx::XScopedPtr< XRRScreenResources, @@ -318,6 +335,30 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { return GetFallbackDisplayList(); } + std::map<RROutput, int> output_to_monitor; + if (xrandr_version_ >= 105) { + void* xrandr_lib = dlopen(NULL, RTLD_NOW); + if (xrandr_lib) { + static base::ProtectedMemory<XRRGetMonitors>::Initializer get_init( + &g_XRRGetMonitors_ptr, reinterpret_cast<XRRGetMonitors>( + dlsym(xrandr_lib, "XRRGetMonitors"))); + static base::ProtectedMemory<XRRFreeMonitors>::Initializer free_init( + &g_XRRFreeMonitors_ptr, reinterpret_cast<XRRFreeMonitors>( + dlsym(xrandr_lib, "XRRFreeMonitors"))); + if (*g_XRRGetMonitors_ptr && *g_XRRFreeMonitors_ptr) { + int nmonitors = 0; + XRRMonitorInfo* monitors = base::UnsanitizedCfiCall( + g_XRRGetMonitors_ptr)(xdisplay_, x_root_window_, false, &nmonitors); + for (int monitor = 0; monitor < nmonitors; monitor++) { + for (int j = 0; j < monitors[monitor].noutput; j++) { + output_to_monitor[monitors[monitor].outputs[j]] = monitor; + } + } + base::UnsanitizedCfiCall(g_XRRFreeMonitors_ptr)(monitors); + } + } + } + primary_display_index_ = 0; RROutput primary_display_id = XRRGetOutputPrimary(xdisplay_, x_root_window_); @@ -397,10 +438,10 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { if (is_primary_display) primary_display_index_ = displays.size(); - // TODO(ccameron): Populate this based on this specific display. - // http://crbug.com/735613 if (!display::Display::HasForceColorProfile()) { - gfx::ICCProfile icc_profile = GetICCProfileFromBestMonitor(); + auto monitor_iter = output_to_monitor.find(output_id); + gfx::ICCProfile icc_profile = GetICCProfileForMonitor( + monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second); icc_profile.HistogramDisplay(display.id()); display.set_color_space(icc_profile.GetColorSpace()); } @@ -424,7 +465,7 @@ void DesktopScreenX11::RestartDelayedConfigurationTask() { void DesktopScreenX11::UpdateDisplays() { std::vector<display::Display> old_displays = displays_; - if (has_xrandr_) + if (xrandr_version_ > 103) SetDisplaysInternal(BuildDisplaysFromXRandRInfo()); else SetDisplaysInternal(GetFallbackDisplayList()); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h index 842eb013535..6b7cb73d7f6 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.h @@ -87,8 +87,8 @@ class VIEWS_EXPORT DesktopScreenX11 : public display::Screen, ::Display* xdisplay_; ::Window x_root_window_; - // Whether the x server supports the XRandR extension. - bool has_xrandr_; + // XRandR version. MAJOR * 100 + MINOR. Zero if no xrandr is present. + int xrandr_version_; // The base of the event numbers used to represent XRandr events used in // decoding events regarding output add/remove. diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h index 834ab5d2379..437a0294a4d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host.h @@ -48,8 +48,7 @@ class VIEWS_EXPORT DesktopWindowTreeHost { // Sets up resources needed before the WindowEventDispatcher has been created. // It is expected this calls InitHost() on the WindowTreeHost. - virtual void Init(aura::Window* content_window, - const Widget::InitParams& params) = 0; + virtual void Init(const Widget::InitParams& params) = 0; // Invoked once the DesktopNativeWidgetAura has been created. virtual void OnNativeWidgetCreated(const Widget::InitParams& params) = 0; 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 new file mode 100644 index 00000000000..97b54ace8b5 --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc @@ -0,0 +1,426 @@ +// 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/client/drag_drop_client.h" +#include "ui/aura/client/transient_window_client.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/views/corewm/tooltip_aura.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/window/native_frame_view.h" +#include "ui/wm/core/window_util.h" + +namespace views { + +DesktopWindowTreeHostPlatform::DesktopWindowTreeHostPlatform( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura) + : native_widget_delegate_(native_widget_delegate), + desktop_native_widget_aura_(desktop_native_widget_aura) {} + +DesktopWindowTreeHostPlatform::~DesktopWindowTreeHostPlatform() { + DCHECK(got_on_closed_); + desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this); + DestroyDispatcher(); +} + +void DesktopWindowTreeHostPlatform::SetBoundsInDIP( + const gfx::Rect& bounds_in_dip) { + DCHECK_NE(0, device_scale_factor()); + SetBoundsInPixels( + gfx::ConvertRectToPixel(device_scale_factor(), bounds_in_dip)); +} + +void DesktopWindowTreeHostPlatform::Init(const Widget::InitParams& params) { + CreateAndSetDefaultPlatformWindow(); + // TODO(sky): this should be |params.force_software_compositing|, figure out + // why it has to be true now. + const bool use_software_compositing = true; + CreateCompositor(viz::FrameSinkId(), use_software_compositing); + aura::WindowTreeHost::OnAcceleratedWidgetAvailable(); + InitHost(); + if (!params.bounds.IsEmpty()) + SetBoundsInDIP(params.bounds); + window()->Show(); +} + +void DesktopWindowTreeHostPlatform::OnNativeWidgetCreated( + const Widget::InitParams& params) { + native_widget_delegate_->OnNativeWidgetCreated(true); +} + +void DesktopWindowTreeHostPlatform::OnWidgetInitDone() {} + +void DesktopWindowTreeHostPlatform::OnActiveWindowChanged(bool active) {} + +std::unique_ptr<corewm::Tooltip> +DesktopWindowTreeHostPlatform::CreateTooltip() { + return std::make_unique<corewm::TooltipAura>(); +} + +std::unique_ptr<aura::client::DragDropClient> +DesktopWindowTreeHostPlatform::CreateDragDropClient( + DesktopNativeCursorManager* cursor_manager) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return nullptr; +} + +void DesktopWindowTreeHostPlatform::Close() { + if (waiting_for_close_now_) + return; + + // Hide while waiting for the close. + platform_window()->Hide(); + + waiting_for_close_now_ = true; + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&DesktopWindowTreeHostPlatform::CloseNow, + weak_factory_.GetWeakPtr())); +} + +void DesktopWindowTreeHostPlatform::CloseNow() { + auto weak_ref = weak_factory_.GetWeakPtr(); + // Deleting the PlatformWindow may not result in OnClosed() being called, if + // not behave as though it was. + SetPlatformWindow(nullptr); + if (!weak_ref || got_on_closed_) + return; + + got_on_closed_ = true; + desktop_native_widget_aura_->OnHostClosed(); +} + +aura::WindowTreeHost* DesktopWindowTreeHostPlatform::AsWindowTreeHost() { + return this; +} + +void DesktopWindowTreeHostPlatform::ShowWindowWithState( + ui::WindowShowState show_state) { + if (compositor()) { + platform_window()->Show(); + compositor()->SetVisible(true); + } + + switch (show_state) { + case ui::SHOW_STATE_MAXIMIZED: + platform_window()->Maximize(); + break; + case ui::SHOW_STATE_MINIMIZED: + platform_window()->Minimize(); + break; + case ui::SHOW_STATE_FULLSCREEN: + // TODO(sky): this isn't necessarily the same as explicitly setting + // fullscreen. + platform_window()->ToggleFullscreen(); + break; + default: + break; + } + + if (native_widget_delegate_->CanActivate()) { + if (show_state != ui::SHOW_STATE_INACTIVE) + Activate(); + + // SetInitialFocus() should be always be called, even for + // SHOW_STATE_INACTIVE. If the window has to stay inactive, the method will + // do the right thing. + // Activate() might fail if the window is non-activatable. In this case, we + // should pass SHOW_STATE_INACTIVE to SetInitialFocus() to stop the initial + // focused view from getting focused. See https://crbug.com/515594 for + // example. + native_widget_delegate_->SetInitialFocus( + IsActive() ? show_state : ui::SHOW_STATE_INACTIVE); + } +} + +void DesktopWindowTreeHostPlatform::ShowMaximizedWithBounds( + const gfx::Rect& restored_bounds) { + // TODO: support |restored_bounds|. + ShowWindowWithState(ui::SHOW_STATE_MAXIMIZED); +} + +bool DesktopWindowTreeHostPlatform::IsVisible() const { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return true; +} + +void DesktopWindowTreeHostPlatform::SetSize(const gfx::Size& size) { + gfx::Rect screen_bounds = + gfx::ConvertRectToDIP(device_scale_factor(), GetBoundsInPixels()); + screen_bounds.set_size(size); + SetBoundsInDIP(screen_bounds); +} + +void DesktopWindowTreeHostPlatform::StackAbove(aura::Window* window) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::StackAtTop() { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::CenterWindow(const gfx::Size& size) { + gfx::Rect bounds_to_center_in = GetWorkAreaBoundsInScreen(); + + // If there is a transient parent and it fits |size|, then center over it. + aura::Window* content_window = desktop_native_widget_aura_->content_window(); + if (wm::GetTransientParent(content_window)) { + gfx::Rect transient_parent_bounds = + wm::GetTransientParent(content_window)->GetBoundsInScreen(); + if (transient_parent_bounds.height() >= size.height() && + transient_parent_bounds.width() >= size.width()) { + bounds_to_center_in = transient_parent_bounds; + } + } + + gfx::Rect resulting_bounds(bounds_to_center_in); + resulting_bounds.ClampToCenteredSize(size); + SetBoundsInDIP(resulting_bounds); +} + +void DesktopWindowTreeHostPlatform::GetWindowPlacement( + gfx::Rect* bounds, + ui::WindowShowState* show_state) const { + NOTIMPLEMENTED_LOG_ONCE(); + *bounds = gfx::Rect(0, 0, 640, 840); + *show_state = ui::SHOW_STATE_NORMAL; +} + +gfx::Rect DesktopWindowTreeHostPlatform::GetWindowBoundsInScreen() const { + gfx::Rect bounds = + gfx::ConvertRectToDIP(device_scale_factor(), GetBoundsInPixels()); + bounds += display::Screen::GetScreen() + ->GetDisplayNearestWindow(const_cast<aura::Window*>(window())) + .bounds() + .OffsetFromOrigin(); + return bounds; +} + +gfx::Rect DesktopWindowTreeHostPlatform::GetClientAreaBoundsInScreen() const { + // View-to-screen coordinate system transformations depend on this returning + // the full window bounds, for example View::ConvertPointToScreen(). + return GetWindowBoundsInScreen(); +} + +gfx::Rect DesktopWindowTreeHostPlatform::GetRestoredBounds() const { + NOTIMPLEMENTED_LOG_ONCE(); + return gfx::Rect(0, 0, 640, 840); +} + +std::string DesktopWindowTreeHostPlatform::GetWorkspace() const { + return std::string(); +} + +gfx::Rect DesktopWindowTreeHostPlatform::GetWorkAreaBoundsInScreen() const { + // TODO(sky): GetDisplayNearestWindow() should take a const aura::Window*. + return display::Screen::GetScreen() + ->GetDisplayNearestWindow(const_cast<aura::Window*>(window())) + .work_area(); +} + +void DesktopWindowTreeHostPlatform::SetShape( + std::unique_ptr<Widget::ShapeRects> native_shape) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::Activate() { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::Deactivate() { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +bool DesktopWindowTreeHostPlatform::IsActive() const { + return is_active_; +} + +void DesktopWindowTreeHostPlatform::Maximize() { + platform_window()->Maximize(); +} + +void DesktopWindowTreeHostPlatform::Minimize() { + platform_window()->Minimize(); +} + +void DesktopWindowTreeHostPlatform::Restore() { + platform_window()->Restore(); +} + +bool DesktopWindowTreeHostPlatform::IsMaximized() const { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + +bool DesktopWindowTreeHostPlatform::IsMinimized() const { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + +bool DesktopWindowTreeHostPlatform::HasCapture() const { + return platform_window()->HasCapture(); +} + +void DesktopWindowTreeHostPlatform::SetAlwaysOnTop(bool always_on_top) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +bool DesktopWindowTreeHostPlatform::IsAlwaysOnTop() const { + // TODO: needs PlatformWindow support. + return false; +} + +void DesktopWindowTreeHostPlatform::SetVisibleOnAllWorkspaces( + bool always_visible) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +bool DesktopWindowTreeHostPlatform::IsVisibleOnAllWorkspaces() const { + // TODO: needs PlatformWindow support. + return false; +} + +bool DesktopWindowTreeHostPlatform::SetWindowTitle( + const base::string16& title) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + +void DesktopWindowTreeHostPlatform::ClearNativeFocus() { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +Widget::MoveLoopResult DesktopWindowTreeHostPlatform::RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source, + Widget::MoveLoopEscapeBehavior escape_behavior) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return Widget::MOVE_LOOP_CANCELED; +} + +void DesktopWindowTreeHostPlatform::EndMoveLoop() { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::SetVisibilityChangedAnimationsEnabled( + bool value) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +NonClientFrameView* DesktopWindowTreeHostPlatform::CreateNonClientFrameView() { + return ShouldUseNativeFrame() ? new NativeFrameView(GetWidget()) : nullptr; +} + +bool DesktopWindowTreeHostPlatform::ShouldUseNativeFrame() const { + return false; +} + +bool DesktopWindowTreeHostPlatform::ShouldWindowContentsBeTransparent() const { + return false; +} + +void DesktopWindowTreeHostPlatform::FrameTypeChanged() {} + +void DesktopWindowTreeHostPlatform::SetFullscreen(bool fullscreen) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +bool DesktopWindowTreeHostPlatform::IsFullscreen() const { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + +void DesktopWindowTreeHostPlatform::SetOpacity(float opacity) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::SetWindowIcons( + const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::InitModalType(ui::ModalType modal_type) { + // TODO: needs PlatformWindow support (alternatively, remove as + // DesktopWindowTreeHostX11 doesn't support at all). + NOTIMPLEMENTED_LOG_ONCE(); +} + +void DesktopWindowTreeHostPlatform::FlashFrame(bool flash_frame) { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +bool DesktopWindowTreeHostPlatform::IsAnimatingClosed() const { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + +bool DesktopWindowTreeHostPlatform::IsTranslucentWindowOpacitySupported() + const { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); + return false; +} + +void DesktopWindowTreeHostPlatform::SizeConstraintsChanged() { + // TODO: needs PlatformWindow support. + NOTIMPLEMENTED_LOG_ONCE(); +} + +bool DesktopWindowTreeHostPlatform::ShouldUpdateWindowTransparency() const { + return false; +} + +bool DesktopWindowTreeHostPlatform::ShouldUseDesktopNativeCursorManager() + const { + return true; +} + +bool DesktopWindowTreeHostPlatform::ShouldCreateVisibilityController() const { + return true; +} + +void DesktopWindowTreeHostPlatform::OnClosed() { + got_on_closed_ = true; + desktop_native_widget_aura_->OnHostClosed(); +} + +void DesktopWindowTreeHostPlatform::OnCloseRequest() { + GetWidget()->Close(); +} + +void DesktopWindowTreeHostPlatform::OnActivationChanged(bool active) { + is_active_ = active; + aura::WindowTreeHostPlatform::OnActivationChanged(active); + desktop_native_widget_aura_->HandleActivationChanged(active); +} + +Widget* DesktopWindowTreeHostPlatform::GetWidget() { + return native_widget_delegate_->AsWidget(); +} + +} // namespace views 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 new file mode 100644 index 00000000000..e9bc864128e --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h @@ -0,0 +1,116 @@ +// 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_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_PLATFORM_H_ +#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_PLATFORM_H_ + +#include "base/memory/weak_ptr.h" +#include "ui/aura/window_tree_host_platform.h" +#include "ui/views/views_export.h" +#include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" + +namespace views { + +class VIEWS_EXPORT DesktopWindowTreeHostPlatform + : public aura::WindowTreeHostPlatform, + public DesktopWindowTreeHost { + public: + DesktopWindowTreeHostPlatform( + internal::NativeWidgetDelegate* native_widget_delegate, + DesktopNativeWidgetAura* desktop_native_widget_aura); + ~DesktopWindowTreeHostPlatform() override; + + void SetBoundsInDIP(const gfx::Rect& bounds_in_dip); + + // DesktopWindowTreeHost: + void Init(const Widget::InitParams& params) override; + void OnNativeWidgetCreated(const Widget::InitParams& params) override; + void OnWidgetInitDone() override; + void OnActiveWindowChanged(bool active) override; + std::unique_ptr<corewm::Tooltip> CreateTooltip() override; + std::unique_ptr<aura::client::DragDropClient> CreateDragDropClient( + DesktopNativeCursorManager* cursor_manager) override; + void Close() override; + void CloseNow() override; + aura::WindowTreeHost* AsWindowTreeHost() override; + void ShowWindowWithState(ui::WindowShowState show_state) override; + void ShowMaximizedWithBounds(const gfx::Rect& restored_bounds) override; + bool IsVisible() const override; + void SetSize(const gfx::Size& size) override; + void StackAbove(aura::Window* window) override; + void StackAtTop() override; + void CenterWindow(const gfx::Size& size) override; + void GetWindowPlacement(gfx::Rect* bounds, + ui::WindowShowState* show_state) const override; + gfx::Rect GetWindowBoundsInScreen() const override; + gfx::Rect GetClientAreaBoundsInScreen() const override; + gfx::Rect GetRestoredBounds() const override; + std::string GetWorkspace() const override; + gfx::Rect GetWorkAreaBoundsInScreen() const override; + void SetShape(std::unique_ptr<Widget::ShapeRects> native_shape) override; + void Activate() override; + void Deactivate() override; + bool IsActive() const override; + void Maximize() override; + void Minimize() override; + void Restore() override; + bool IsMaximized() const override; + bool IsMinimized() const override; + bool HasCapture() const override; + void SetAlwaysOnTop(bool always_on_top) override; + bool IsAlwaysOnTop() const override; + void SetVisibleOnAllWorkspaces(bool always_visible) override; + bool IsVisibleOnAllWorkspaces() const override; + bool SetWindowTitle(const base::string16& title) override; + void ClearNativeFocus() override; + Widget::MoveLoopResult RunMoveLoop( + const gfx::Vector2d& drag_offset, + Widget::MoveLoopSource source, + Widget::MoveLoopEscapeBehavior escape_behavior) override; + void EndMoveLoop() override; + void SetVisibilityChangedAnimationsEnabled(bool value) override; + NonClientFrameView* CreateNonClientFrameView() override; + bool ShouldUseNativeFrame() const override; + bool ShouldWindowContentsBeTransparent() const override; + void FrameTypeChanged() override; + void SetFullscreen(bool fullscreen) override; + bool IsFullscreen() const override; + void SetOpacity(float opacity) override; + void SetWindowIcons(const gfx::ImageSkia& window_icon, + const gfx::ImageSkia& app_icon) override; + void InitModalType(ui::ModalType modal_type) override; + void FlashFrame(bool flash_frame) override; + bool IsAnimatingClosed() const override; + bool IsTranslucentWindowOpacitySupported() const override; + void SizeConstraintsChanged() override; + bool ShouldUpdateWindowTransparency() const override; + bool ShouldUseDesktopNativeCursorManager() const override; + bool ShouldCreateVisibilityController() const override; + + // WindowTreeHostPlatform: + void OnClosed() override; + void OnCloseRequest() override; + void OnActivationChanged(bool active) override; + + private: + Widget* GetWidget(); + + internal::NativeWidgetDelegate* const native_widget_delegate_; + DesktopNativeWidgetAura* const desktop_native_widget_aura_; + + // Set to true when Close() is called. + bool waiting_for_close_now_ = false; + + bool got_on_closed_ = false; + + bool is_active_ = false; + + base::WeakPtrFactory<DesktopWindowTreeHostPlatform> weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostPlatform); +}; + +} // namespace views + +#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_WINDOW_TREE_HOST_PLATFORM_H_ diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc index 19f103c0385..c8f868bf416 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc @@ -81,7 +81,6 @@ DesktopWindowTreeHostWin::DesktopWindowTreeHostWin( : message_handler_(new HWNDMessageHandler(this)), native_widget_delegate_(native_widget_delegate), desktop_native_widget_aura_(desktop_native_widget_aura), - content_window_(NULL), drag_drop_client_(NULL), should_animate_window_close_(false), pending_close_(false), @@ -90,7 +89,6 @@ DesktopWindowTreeHostWin::DesktopWindowTreeHostWin( } DesktopWindowTreeHostWin::~DesktopWindowTreeHostWin() { - // WARNING: |content_window_| has been destroyed by the time we get here. desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this); DestroyDispatcher(); } @@ -108,16 +106,13 @@ aura::Window* DesktopWindowTreeHostWin::GetContentWindowForHWND(HWND hwnd) { //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostWin, DesktopWindowTreeHost implementation: -void DesktopWindowTreeHostWin::Init(aura::Window* content_window, - const Widget::InitParams& params) { - // TODO(beng): SetInitParams(). - content_window_ = content_window; +void DesktopWindowTreeHostWin::Init(const Widget::InitParams& params) { wants_mouse_events_when_inactive_ = params.wants_mouse_events_when_inactive; - wm::SetAnimationHost(content_window_, this); + wm::SetAnimationHost(content_window(), this); if (params.type == Widget::InitParams::TYPE_WINDOW && !params.remove_standard_frame) - content_window_->SetProperty(aura::client::kAnimationsDisabledKey, true); + content_window()->SetProperty(aura::client::kAnimationsDisabledKey, true); ConfigureWindowStyles(message_handler_.get(), params, GetWidget()->widget_delegate(), @@ -148,12 +143,12 @@ void DesktopWindowTreeHostWin::OnNativeWidgetCreated( if (cursor_client) is_cursor_visible_ = cursor_client->IsCursorVisible(); - window()->SetProperty(kContentWindowForRootWindow, content_window_); + window()->SetProperty(kContentWindowForRootWindow, content_window()); window()->SetProperty(kDesktopWindowTreeHostKey, this); should_animate_window_close_ = - content_window_->type() != aura::client::WINDOW_TYPE_NORMAL && - !wm::WindowAnimationsDisabled(content_window_); + content_window()->type() != aura::client::WINDOW_TYPE_NORMAL && + !wm::WindowAnimationsDisabled(content_window()); // TODO this is not invoked *after* Init(), but should be ok. SetWindowTransparency(); @@ -181,7 +176,7 @@ void DesktopWindowTreeHostWin::Close() { if (should_animate_window_close_) { pending_close_ = true; const bool is_animating = - content_window_->layer()->GetAnimator()->IsAnimatingProperty( + content_window()->layer()->GetAnimator()->IsAnimatingProperty( ui::LayerAnimationElement::VISIBILITY); // Animation may not start for a number of reasons. if (!is_animating) @@ -401,7 +396,7 @@ void DesktopWindowTreeHostWin::EndMoveLoop() { void DesktopWindowTreeHostWin::SetVisibilityChangedAnimationsEnabled( bool value) { message_handler_->SetVisibilityChangedAnimationsEnabled(value); - content_window_->SetProperty(aura::client::kAnimationsDisabledKey, !value); + content_window()->SetProperty(aura::client::kAnimationsDisabledKey, !value); } NonClientFrameView* DesktopWindowTreeHostWin::CreateNonClientFrameView() { @@ -434,10 +429,10 @@ void DesktopWindowTreeHostWin::SetFullscreen(bool fullscreen) { // TODO(sky): workaround for ScopedFullscreenVisibility showing window // directly. Instead of this should listen for visibility changes and then // update window. - if (message_handler_->IsVisible() && !content_window_->TargetVisibility()) { + if (message_handler_->IsVisible() && !content_window()->TargetVisibility()) { if (compositor()) compositor()->SetVisible(true); - content_window_->Show(); + content_window()->Show(); } SetWindowTransparency(); } @@ -447,7 +442,7 @@ bool DesktopWindowTreeHostWin::IsFullscreen() const { } void DesktopWindowTreeHostWin::SetOpacity(float opacity) { - content_window_->layer()->SetOpacity(opacity); + content_window()->layer()->SetOpacity(opacity); } void DesktopWindowTreeHostWin::SetWindowIcons( @@ -577,6 +572,10 @@ void DesktopWindowTreeHostWin::ReleaseSystemKeyEventCapture() { keyboard_hook_.reset(); } +bool DesktopWindowTreeHostWin::IsKeyLocked(int native_key_code) { + return keyboard_hook_ && keyboard_hook_->IsKeyLocked(native_key_code); +} + void DesktopWindowTreeHostWin::SetCursorNative(gfx::NativeCursor cursor) { ui::CursorLoaderWin cursor_loader; cursor_loader.SetPlatformCursor(&cursor); @@ -727,7 +726,8 @@ gfx::Size DesktopWindowTreeHostWin::DIPToScreenSize( } void DesktopWindowTreeHostWin::ResetWindowControls() { - GetWidget()->non_client_view()->ResetWindowControls(); + if (GetWidget()->non_client_view()) + GetWidget()->non_client_view()->ResetWindowControls(); } gfx::NativeViewAccessible DesktopWindowTreeHostWin::GetNativeViewAccessible() { @@ -845,7 +845,8 @@ void DesktopWindowTreeHostWin::HandleFrameChanged() { CheckForMonitorChange(); SetWindowTransparency(); // Replace the frame and layout the contents. - GetWidget()->non_client_view()->UpdateFrame(); + if (GetWidget()->non_client_view()) + GetWidget()->non_client_view()->UpdateFrame(); } void DesktopWindowTreeHostWin::HandleNativeFocus(HWND last_focused_window) { @@ -856,9 +857,9 @@ void DesktopWindowTreeHostWin::HandleNativeBlur(HWND focused_window) { // TODO(beng): inform the native_widget_delegate_. } -bool DesktopWindowTreeHostWin::HandleMouseEvent(const ui::MouseEvent& event) { - SendEventToSink(const_cast<ui::MouseEvent*>(&event)); - return event.handled(); +bool DesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { + SendEventToSink(event); + return event->handled(); } bool DesktopWindowTreeHostWin::HandlePointerEvent(ui::PointerEvent* event) { @@ -870,8 +871,7 @@ void DesktopWindowTreeHostWin::HandleKeyEvent(ui::KeyEvent* event) { SendEventToSink(event); } -void DesktopWindowTreeHostWin::HandleTouchEvent( - const ui::TouchEvent& event) { +void DesktopWindowTreeHostWin::HandleTouchEvent(ui::TouchEvent* event) { // HWNDMessageHandler asynchronously processes touch events. Because of this // it's possible for the aura::WindowEventDispatcher to have been destroyed // by the time we attempt to process them. @@ -885,10 +885,10 @@ void DesktopWindowTreeHostWin::HandleTouchEvent( DesktopWindowTreeHostWin* target = host->window()->GetProperty(kDesktopWindowTreeHostKey); if (target && target->HasCapture() && target != this) { - POINT target_location(event.location().ToPOINT()); + POINT target_location(event->location().ToPOINT()); ClientToScreen(GetHWND(), &target_location); ScreenToClient(target->GetHWND(), &target_location); - ui::TouchEvent target_event(event, static_cast<View*>(NULL), + ui::TouchEvent target_event(*event, static_cast<View*>(NULL), static_cast<View*>(NULL)); target_event.set_location(gfx::Point(target_location)); target_event.set_root_location(target_event.location()); @@ -896,7 +896,7 @@ void DesktopWindowTreeHostWin::HandleTouchEvent( return; } } - SendEventToSink(const_cast<ui::TouchEvent*>(&event)); + SendEventToSink(event); } bool DesktopWindowTreeHostWin::HandleIMEMessage(UINT message, @@ -949,10 +949,14 @@ void DesktopWindowTreeHostWin::PostHandleMSG(UINT message, LPARAM l_param) { } -bool DesktopWindowTreeHostWin::HandleScrollEvent( - const ui::ScrollEvent& event) { - SendEventToSink(const_cast<ui::ScrollEvent*>(&event)); - return event.handled(); +bool DesktopWindowTreeHostWin::HandleScrollEvent(ui::ScrollEvent* event) { + SendEventToSink(event); + return event->handled(); +} + +bool DesktopWindowTreeHostWin::HandleGestureEvent(ui::GestureEvent* event) { + SendEventToSink(event); + return event->handled(); } void DesktopWindowTreeHostWin::HandleWindowSizeChanging() { @@ -965,16 +969,18 @@ void DesktopWindowTreeHostWin::HandleWindowSizeUnchanged() { // changed (can occur on Windows 10 when snapping a window to the side of // the screen). In that case do a resize to the current size to reenable // swaps. - if (compositor()) { - compositor()->SetScaleAndSize( - compositor()->device_scale_factor(), - message_handler_->GetClientAreaBounds().size(), - window()->GetLocalSurfaceId()); - } + if (compositor()) + compositor()->ReenableSwap(); } void DesktopWindowTreeHostWin::HandleWindowScaleFactorChanged( float window_scale_factor) { + // TODO(ccameron): This will violate surface invariants, and is insane. + // Shouldn't the scale factor and window pixel size changes be sent + // atomically? And how does this interact with updates to display::Display? + // Should we expect the display::Display to be updated before this? If so, + // why can't we use the DisplayObserver that the base WindowTreeHost is + // using? if (compositor()) { compositor()->SetScaleAndSize( window_scale_factor, message_handler_->GetClientAreaBounds().size(), @@ -1002,7 +1008,7 @@ void DesktopWindowTreeHostWin::SetWindowTransparency() { compositor()->SetBackgroundColor(transparent ? SK_ColorTRANSPARENT : SK_ColorWHITE); window()->SetTransparent(transparent); - content_window_->SetTransparent(transparent); + content_window()->SetTransparent(transparent); } bool DesktopWindowTreeHostWin::IsModalWindowActive() const { @@ -1031,6 +1037,10 @@ void DesktopWindowTreeHostWin::CheckForMonitorChange() { OnHostDisplayChanged(); } +aura::Window* DesktopWindowTreeHostWin::content_window() { + return desktop_native_widget_aura_->content_window(); +} + //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHost, public: diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index 732f492c102..4120d3ae63f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h @@ -56,8 +56,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin protected: // Overridden from DesktopWindowTreeHost: - void Init(aura::Window* content_window, - const Widget::InitParams& params) override; + void Init(const Widget::InitParams& params) override; void OnNativeWidgetCreated(const Widget::InitParams& params) override; void OnActiveWindowChanged(bool active) override; void OnWidgetInitDone() override; @@ -134,6 +133,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin bool CaptureSystemKeyEventsImpl( base::Optional<base::flat_set<int>> keys_codes) override; void ReleaseSystemKeyEventCapture() override; + bool IsKeyLocked(int native_key_code) override; void SetCursorNative(gfx::NativeCursor cursor) override; void OnCursorVisibilityChangedNative(bool show) override; void MoveCursorToScreenLocationInPixels( @@ -193,10 +193,10 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin void HandleFrameChanged() override; void HandleNativeFocus(HWND last_focused_window) override; void HandleNativeBlur(HWND focused_window) override; - bool HandleMouseEvent(const ui::MouseEvent& event) override; + bool HandleMouseEvent(ui::MouseEvent* event) override; bool HandlePointerEvent(ui::PointerEvent* event) override; void HandleKeyEvent(ui::KeyEvent* event) override; - void HandleTouchEvent(const ui::TouchEvent& event) override; + void HandleTouchEvent(ui::TouchEvent* event) override; bool HandleIMEMessage(UINT message, WPARAM w_param, LPARAM l_param, @@ -213,7 +213,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin LPARAM l_param, LRESULT* result) override; void PostHandleMSG(UINT message, WPARAM w_param, LPARAM l_param) override; - bool HandleScrollEvent(const ui::ScrollEvent& event) override; + bool HandleScrollEvent(ui::ScrollEvent* event) override; + bool HandleGestureEvent(ui::GestureEvent* event) override; void HandleWindowSizeChanging() override; void HandleWindowSizeUnchanged() override; void HandleWindowScaleFactorChanged(float window_scale_factor) override; @@ -232,6 +233,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin // has changed, and, if so, inform the aura::WindowTreeHost. void CheckForMonitorChange(); + // Accessor for DesktopNativeWidgetAura::content_window(). + aura::Window* content_window(); + HMONITOR last_monitor_from_window_ = nullptr; std::unique_ptr<HWNDMessageHandler> message_handler_; @@ -243,8 +247,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin DesktopNativeWidgetAura* desktop_native_widget_aura_; - aura::Window* content_window_; - // Owned by DesktopNativeWidgetAura. DesktopDragDropClientWin* drag_drop_client_; 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 f8550833ee6..806360dbc69 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 @@ -148,7 +148,6 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( drag_drop_client_(NULL), native_widget_delegate_(native_widget_delegate), desktop_native_widget_aura_(desktop_native_widget_aura), - content_window_(NULL), window_parent_(NULL), custom_window_shape_(false), urgency_hint_set_(false), @@ -388,13 +387,11 @@ void DesktopWindowTreeHostX11::CleanUpWindowList( //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation: -void DesktopWindowTreeHostX11::Init(aura::Window* content_window, - const Widget::InitParams& params) { - content_window_ = content_window; +void DesktopWindowTreeHostX11::Init(const Widget::InitParams& params) { activatable_ = (params.activatable == Widget::InitParams::ACTIVATABLE_YES); if (params.type == Widget::InitParams::TYPE_WINDOW) - content_window_->SetProperty(aura::client::kAnimationsDisabledKey, true); + content_window()->SetProperty(aura::client::kAnimationsDisabledKey, true); // TODO(erg): Check whether we *should* be building a WindowTreeHost here, or // whether we should be proxying requests to another DRWHL. @@ -414,7 +411,7 @@ void DesktopWindowTreeHostX11::Init(aura::Window* content_window, void DesktopWindowTreeHostX11::OnNativeWidgetCreated( const Widget::InitParams& params) { - window()->SetProperty(kViewsWindowForRootWindow, content_window_); + window()->SetProperty(kViewsWindowForRootWindow, content_window()); window()->SetProperty(kHostForRootWindow, this); // Ensure that the X11DesktopHandler exists so that it tracks create/destroy @@ -518,7 +515,7 @@ aura::WindowTreeHost* DesktopWindowTreeHostX11::AsWindowTreeHost() { void DesktopWindowTreeHostX11::ShowWindowWithState( ui::WindowShowState show_state) { if (compositor()) - compositor()->SetVisible(true); + SetVisible(true); if (!IsVisible() || !window_mapped_in_server_) MapWindow(show_state); @@ -605,9 +602,9 @@ void DesktopWindowTreeHostX11::CenterWindow(const gfx::Size& size) { // If |window_|'s transient parent bounds are big enough to contain |size|, // use them instead. - if (wm::GetTransientParent(content_window_)) { + if (wm::GetTransientParent(content_window())) { gfx::Rect transient_parent_rect = - wm::GetTransientParent(content_window_)->GetBoundsInScreen(); + wm::GetTransientParent(content_window())->GetBoundsInScreen(); if (transient_parent_rect.height() >= size.height() && transient_parent_rect.width() >= size.width()) { parent_bounds_in_pixels = ToPixelRect(transient_parent_rect); @@ -875,6 +872,13 @@ bool DesktopWindowTreeHostX11::IsAlwaysOnTop() const { return is_always_on_top_; } +void DesktopWindowTreeHostX11::SetVisible(bool visible) { + if (compositor()) + compositor()->SetVisible(visible); + if (IsVisible() != visible) + native_widget_delegate_->OnNativeWidgetVisibilityChanged(visible); +} + void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) { ui::SetWMSpecState(xwindow_, always_visible, gfx::GetAtom("_NET_WM_STATE_STICKY"), x11::None); @@ -932,12 +936,13 @@ bool DesktopWindowTreeHostX11::SetWindowTitle(const base::string16& title) { void DesktopWindowTreeHostX11::ClearNativeFocus() { // This method is weird and misnamed. Instead of clearing the native focus, - // it sets the focus to our |content_window_|, which will trigger a cascade + // it sets the focus to our content_window(), which will trigger a cascade // of focus changes into views. - if (content_window_ && aura::client::GetFocusClient(content_window_) && - content_window_->Contains( - aura::client::GetFocusClient(content_window_)->GetFocusedWindow())) { - aura::client::GetFocusClient(content_window_)->FocusWindow(content_window_); + if (content_window() && aura::client::GetFocusClient(content_window()) && + content_window()->Contains( + aura::client::GetFocusClient(content_window())->GetFocusedWindow())) { + aura::client::GetFocusClient(content_window()) + ->FocusWindow(content_window()); } } @@ -948,7 +953,7 @@ Widget::MoveLoopResult DesktopWindowTreeHostX11::RunMoveLoop( wm::WindowMoveSource window_move_source = source == Widget::MOVE_LOOP_SOURCE_MOUSE ? wm::WINDOW_MOVE_SOURCE_MOUSE : wm::WINDOW_MOVE_SOURCE_TOUCH; - if (x11_window_move_client_->RunMoveLoop(content_window_, drag_offset, + if (x11_window_move_client_->RunMoveLoop(content_window(), drag_offset, window_move_source) == wm::MOVE_SUCCESSFUL) return Widget::MOVE_LOOP_SUCCESSFUL; @@ -1179,15 +1184,14 @@ gfx::AcceleratedWidget DesktopWindowTreeHostX11::GetAcceleratedWidget() { void DesktopWindowTreeHostX11::ShowImpl() { ShowWindowWithState(ui::SHOW_STATE_NORMAL); - native_widget_delegate_->OnNativeWidgetVisibilityChanged(true); } void DesktopWindowTreeHostX11::HideImpl() { if (IsVisible()) { XWithdrawWindow(xdisplay_, xwindow_, 0); window_mapped_in_client_ = false; + native_widget_delegate_->OnNativeWidgetVisibilityChanged(false); } - native_widget_delegate_->OnNativeWidgetVisibilityChanged(false); } gfx::Rect DesktopWindowTreeHostX11::GetBoundsInPixels() const { @@ -1310,6 +1314,10 @@ void DesktopWindowTreeHostX11::ReleaseSystemKeyEventCapture() { keyboard_hook_.reset(); } +bool DesktopWindowTreeHostX11::IsKeyLocked(int native_key_code) { + return keyboard_hook_ && keyboard_hook_->IsKeyLocked(native_key_code); +} + void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) { XDefineCursor(xdisplay_, xwindow_, cursor.platform()); } @@ -1628,11 +1636,11 @@ void DesktopWindowTreeHostX11::OnWMStateUpdated() { // minimized. if (is_minimized != was_minimized) { if (is_minimized) { - compositor()->SetVisible(false); - content_window_->Hide(); + SetVisible(false); + content_window()->Hide(); } else { - content_window_->Show(); - compositor()->SetVisible(true); + content_window()->Show(); + SetVisible(true); } } @@ -1759,10 +1767,10 @@ void DesktopWindowTreeHostX11::DispatchMouseEvent(ui::MouseEvent* event) { // events on the ash desktop are clicking in what Windows considers to be a // non client area.) Likewise, we won't want to do the following in any // WindowTreeHost that hosts ash. - if (content_window_ && content_window_->delegate()) { + if (content_window() && content_window()->delegate()) { int flags = event->flags(); int hit_test_code = - content_window_->delegate()->GetNonClientComponent(event->location()); + content_window()->delegate()->GetNonClientComponent(event->location()); if (hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE) flags |= ui::EF_IS_NON_CLIENT; event->set_flags(flags); @@ -1927,7 +1935,7 @@ void DesktopWindowTreeHostX11::SetWindowTransparency() { compositor()->SetBackgroundColor(use_argb_visual_ ? SK_ColorTRANSPARENT : SK_ColorWHITE); window()->SetTransparent(use_argb_visual_); - content_window_->SetTransparent(use_argb_visual_); + content_window()->SetTransparent(use_argb_visual_); } void DesktopWindowTreeHostX11::Relayout() { @@ -2338,6 +2346,10 @@ void DesktopWindowTreeHostX11::RestartDelayedResizeTask() { FROM_HERE, delayed_resize_task_.callback()); } +aura::Window* DesktopWindowTreeHostX11::content_window() { + return desktop_native_widget_aura_->content_window(); +} + //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHost, public: 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 021a1d9ad2b..79d677baa9f 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 @@ -51,7 +51,8 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 DesktopNativeWidgetAura* desktop_native_widget_aura); ~DesktopWindowTreeHostX11() override; - // A way of converting an X11 |xid| host window into a |content_window_|. + // A way of converting an X11 |xid| host window into the content_window() + // of the associated DesktopNativeWidgetAura. static aura::Window* GetContentWindowForXID(XID xid); // A way of converting an X11 |xid| host window into this object. @@ -88,8 +89,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 protected: // Overridden from DesktopWindowTreeHost: - void Init(aura::Window* content_window, - const Widget::InitParams& params) override; + void Init(const Widget::InitParams& params) override; void OnNativeWidgetCreated(const Widget::InitParams& params) override; void OnWidgetInitDone() override; void OnActiveWindowChanged(bool active) override; @@ -167,6 +167,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 bool CaptureSystemKeyEventsImpl( base::Optional<base::flat_set<int>> keys_codes) override; void ReleaseSystemKeyEventCapture() override; + bool IsKeyLocked(int native_key_code) override; void SetCursorNative(gfx::NativeCursor cursor) override; void MoveCursorToScreenLocationInPixels( const gfx::Point& location_in_pixels) override; @@ -188,7 +189,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // initialization related to talking to the X11 server. void InitX11Window(const Widget::InitParams& params); - // Creates an aura::WindowEventDispatcher to contain the |content_window|, + // Creates an aura::WindowEventDispatcher to contain the content_window() // along with all aura client objects that direct behavior. aura::WindowEventDispatcher* InitDispatcher(const Widget::InitParams& params); @@ -282,6 +283,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 // the queue) and adds it back at the end of the queue. void RestartDelayedResizeTask(); + // Set visibility and fire OnNativeWidgetVisibilityChanged() if it changed. + void SetVisible(bool visible); + + // Accessor for DesktopNativeWidgetAura::content_window(). + aura::Window* content_window(); + // X11 things // The display and the native X window hosting the root window. XDisplay* xdisplay_; @@ -352,8 +359,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 DesktopNativeWidgetAura* desktop_native_widget_aura_; - aura::Window* content_window_; - // We can optionally have a parent which can order us to close, or own // children who we're responsible for closing when we CloseNow(). DesktopWindowTreeHostX11* window_parent_; 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 94d8a5768b1..73507805813 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 @@ -8,7 +8,6 @@ #include "base/command_line.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" 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 1e17f0f4cf4..e0a11a00d46 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,7 +11,6 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "third_party/skia/include/core/SkRect.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index a9be3abbd01..76f19a26a0b 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/location.h" -#include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" @@ -147,7 +146,6 @@ void NativeWidgetAura::SetShadowElevationFromInitParams( // NativeWidgetAura, internal::NativeWidgetPrivate implementation: void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { - // Aura needs to know which desktop (Ash or regular) will manage this widget. // See Widget::InitParams::context for details. DCHECK(params.parent || params.context); @@ -520,8 +518,8 @@ void NativeWidgetAura::Close() { if (!close_widget_factory_.HasWeakPtrs()) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&NativeWidgetAura::CloseNow, - close_widget_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&NativeWidgetAura::CloseNow, + close_widget_factory_.GetWeakPtr())); } } diff --git a/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc b/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc index 28bfeac0fef..b4de50c7e2b 100644 --- a/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc +++ b/chromium/ui/views/widget/native_widget_aura_interactive_uitest.cc @@ -4,7 +4,6 @@ #include "ui/views/widget/native_widget_aura.h" -#include "base/memory/ptr_util.h" #include "ui/aura/window.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/test/native_widget_factory.h" diff --git a/chromium/ui/views/widget/native_widget_delegate.h b/chromium/ui/views/widget/native_widget_delegate.h index 8f2e56a4a4d..018bc2ce9d3 100644 --- a/chromium/ui/views/widget/native_widget_delegate.h +++ b/chromium/ui/views/widget/native_widget_delegate.h @@ -56,7 +56,8 @@ class VIEWS_EXPORT NativeWidgetDelegate { virtual bool IsAlwaysRenderAsActive() const = 0; // Called when the activation state of a window has changed. - virtual void OnNativeWidgetActivationChanged(bool active) = 0; + // Returns true if this event should be handled. + virtual bool OnNativeWidgetActivationChanged(bool active) = 0; // Called when native focus moves from one native view to another. virtual void OnNativeFocus() = 0; diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm index f5db821551e..223eb1191d1 100644 --- a/chromium/ui/views/widget/native_widget_mac.mm +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -8,6 +8,7 @@ #include <utility> +#include "base/command_line.h" #import "base/mac/bind_objc_block.h" #include "base/mac/foundation_util.h" #include "base/mac/scoped_nsobject.h" @@ -16,6 +17,7 @@ #include "components/crash/core/common/crash_key.h" #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" #import "ui/base/cocoa/window_size_constants.h" +#include "ui/base/ui_base_switches.h" #include "ui/gfx/font_list.h" #import "ui/gfx/mac/coordinate_conversion.h" #import "ui/gfx/mac/nswindow_frame_controls.h" @@ -46,6 +48,11 @@ namespace views { namespace { +bool AreModalAnimationsEnabled() { + return !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableModalAnimations); +} + 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 @@ -332,11 +339,8 @@ void NativeWidgetMac::SetSize(const gfx::Size& size) { } void NativeWidgetMac::StackAbove(gfx::NativeView native_view) { - // NativeWidgetMac currently only has machinery for stacking windows, and only - // stacks child windows above parents. That's currently all this is used for. - // DCHECK if a new use case comes along. - DCHECK(bridge_ && bridge_->parent()); - DCHECK_EQ([native_view window], bridge_->parent()->GetNSWindow()); + NSInteger view_parent = native_view.window.windowNumber; + [GetNativeWindow() orderWindow:NSWindowAbove relativeTo:view_parent]; } void NativeWidgetMac::StackAtTop() { @@ -365,7 +369,8 @@ void NativeWidgetMac::Close() { } // For other modal types, animate the close. - if (bridge_->animate() && delegate_->IsModal()) { + if (bridge_->animate() && AreModalAnimationsEnabled() && + delegate_->IsModal()) { [ViewsNSWindowCloseAnimator closeWindowWithAnimation:window]; return; } diff --git a/chromium/ui/views/widget/root_view.cc b/chromium/ui/views/widget/root_view.cc index 5d0dddf78c7..d07c9ba57f4 100644 --- a/chromium/ui/views/widget/root_view.cc +++ b/chromium/ui/views/widget/root_view.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "build/build_config.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/cursor/cursor.h" #include "ui/base/dragdrop/drag_drop_types.h" @@ -73,7 +74,8 @@ class PreEventDispatchHandler : public ui::EventHandler { View* v = NULL; if (owner_->GetFocusManager()) // Can be NULL in unittests. v = owner_->GetFocusManager()->GetFocusedView(); - +// macOS doesn't have keyboard-triggered context menus. +#if !defined(OS_MACOSX) // Special case to handle keyboard-triggered context menus. if (v && v->enabled() && ((event->key_code() == ui::VKEY_APPS) || (event->key_code() == ui::VKEY_F10 && event->IsShiftDown()))) { @@ -88,6 +90,7 @@ class PreEventDispatchHandler : public ui::EventHandler { v->ShowContextMenu(location, ui::MENU_SOURCE_KEYBOARD); event->StopPropagation(); } +#endif } View* owner_; diff --git a/chromium/ui/views/widget/root_view_unittest.cc b/chromium/ui/views/widget/root_view_unittest.cc index 9f0cd55a02f..82959179009 100644 --- a/chromium/ui/views/widget/root_view_unittest.cc +++ b/chromium/ui/views/widget/root_view_unittest.cc @@ -6,6 +6,7 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "build/build_config.h" #include "ui/events/event_utils.h" #include "ui/views/context_menu_controller.h" #include "ui/views/test/views_test_base.h" @@ -111,6 +112,10 @@ class TestContextMenuController : public ContextMenuController { // Tests that context menus are shown for certain key events (Shift+F10 // and VKEY_APPS) by the pre-target handler installed on RootView. TEST_F(RootViewTest, ContextMenuFromKeyEvent) { +#if defined(OS_MACOSX) + // This behavior is intentionally unsupported on macOS. + return; +#endif Widget widget; Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP); diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 926519dfa33..7bc25d68ab4 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -84,6 +84,9 @@ void NotifyCaretBoundsChanged(ui::InputMethod* input_method) { } // namespace +// static +bool Widget::g_disable_activation_change_handling_ = false; + // A default implementation of WidgetDelegate, used by Widget when no // WidgetDelegate is supplied. class DefaultWidgetDelegate : public WidgetDelegate { @@ -325,6 +328,9 @@ void Widget::Init(const InitParams& in_params) { params.delegate : new DefaultWidgetDelegate(this); widget_delegate_->set_can_activate(can_activate); + // Henceforth, ensure the delegate outlives the Widget. + widget_delegate_->can_delete_this_ = false; + ownership_ = params.ownership; native_widget_ = CreateNativeWidget(params, this)->AsNativeWidgetPrivate(); root_view_.reset(CreateRootView()); @@ -1030,7 +1036,10 @@ bool Widget::IsAlwaysRenderAsActive() const { return always_render_as_active_; } -void Widget::OnNativeWidgetActivationChanged(bool active) { +bool Widget::OnNativeWidgetActivationChanged(bool active) { + if (g_disable_activation_change_handling_) + return false; + // On windows we may end up here before we've completed initialization (from // an WM_NCACTIVATE). If that happens the WidgetDelegate likely doesn't know // the Widget and will crash attempting to access it. @@ -1042,6 +1051,8 @@ void Widget::OnNativeWidgetActivationChanged(bool active) { if (non_client_view()) non_client_view()->frame_view()->ActivationChanged(active); + + return true; } void Widget::OnNativeFocus() { @@ -1092,6 +1103,7 @@ void Widget::OnNativeWidgetDestroying() { void Widget::OnNativeWidgetDestroyed() { for (WidgetObserver& observer : observers_) observer.OnWidgetDestroyed(this); + widget_delegate_->can_delete_this_ = true; widget_delegate_->DeleteDelegate(); widget_delegate_ = NULL; native_widget_destroyed_ = true; diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index 6c0c60807a2..e2df1f67a8b 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -276,8 +276,9 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // hierarchy this widget should be placed. (This is separate from |parent|; // if you pass a RootWindow to |parent|, your window will be parented to // |parent|. If you pass a RootWindow to |context|, we ask that RootWindow - // where it wants your window placed.) NULL is not allowed if you are using - // aura. + // where it wants your window placed.) Nullptr is not allowed on Windows and + // Linux. Nullptr is allowed on Chrome OS, which will place the window on + // the default desktop for new windows. gfx::NativeWindow context; // If true, forces the window to be shown in the taskbar, even for window // types that do not appear in the taskbar by default (popup and bubble). @@ -789,7 +790,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, bool CanActivate() const override; bool IsAlwaysRenderAsActive() const override; void SetAlwaysRenderAsActive(bool always_render_as_active) override; - void OnNativeWidgetActivationChanged(bool active) override; + bool OnNativeWidgetActivationChanged(bool active) override; void OnNativeFocus() override; void OnNativeBlur() override; void OnNativeWidgetVisibilityChanging(bool visible) override; @@ -858,6 +859,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, friend class ButtonTest; friend class TextfieldTest; friend class ViewAuraTest; + friend void DisableActivationChangeHandlingForTests(); // Persists the window's restored position and "show" state using the // window delegate. @@ -883,6 +885,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // layer. const View::Views& GetViewsWithLayers(); + static bool g_disable_activation_change_handling_; + internal::NativeWidgetPrivate* native_widget_; base::ObserverList<WidgetObserver> observers_; diff --git a/chromium/ui/views/widget/widget_delegate.cc b/chromium/ui/views/widget/widget_delegate.cc index 294113b8996..ea1c78ff1d6 100644 --- a/chromium/ui/views/widget/widget_delegate.cc +++ b/chromium/ui/views/widget/widget_delegate.cc @@ -4,6 +4,7 @@ #include "ui/views/widget/widget_delegate.h" +#include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "services/ui/public/interfaces/window_manager_constants.mojom.h" #include "ui/gfx/image/image_skia.h" @@ -17,9 +18,9 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // WidgetDelegate: -WidgetDelegate::WidgetDelegate() - : default_contents_view_(NULL), - can_activate_(true) { +WidgetDelegate::WidgetDelegate() = default; +WidgetDelegate::~WidgetDelegate() { + CHECK(can_delete_this_) << "A WidgetDelegate must outlive its Widget"; } void WidgetDelegate::OnWidgetMove() { diff --git a/chromium/ui/views/widget/widget_delegate.h b/chromium/ui/views/widget/widget_delegate.h index 8ae82197483..c1cfaebc417 100644 --- a/chromium/ui/views/widget/widget_delegate.h +++ b/chromium/ui/views/widget/widget_delegate.h @@ -184,12 +184,16 @@ class VIEWS_EXPORT WidgetDelegate { virtual void GetAccessiblePanes(std::vector<View*>* panes) {} protected: - virtual ~WidgetDelegate() {} + virtual ~WidgetDelegate(); private: - View* default_contents_view_; + friend class Widget; - bool can_activate_; + View* default_contents_view_ = nullptr; + bool can_activate_ = true; + + // Managed by Widget. Ensures |this| outlives its Widget. + bool can_delete_this_ = true; DISALLOW_COPY_AND_ASSIGN(WidgetDelegate); }; diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index 1cad461f7dd..db44bd41b88 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -377,9 +377,7 @@ class TouchEventHandler : public ui::EventHandler { } void WaitForEvents() { - base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); - base::MessageLoopForUI::ScopedNestableTaskAllower allow_nested(loop); - base::RunLoop run_loop; + base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); } @@ -543,11 +541,12 @@ TEST_F(WidgetTestInteractive, DisableCaptureWidgetFromMousePress) { gfx::Point location(20, 20); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&Widget::OnMouseEvent, base::Unretained(second), - base::Owned(new ui::MouseEvent( - ui::ET_MOUSE_RELEASED, location, location, - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON)))); + FROM_HERE, + base::BindOnce( + &Widget::OnMouseEvent, base::Unretained(second), + base::Owned(new ui::MouseEvent( + ui::ET_MOUSE_RELEASED, location, location, ui::EventTimeForNow(), + ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)))); ui::MouseEvent press(ui::ET_MOUSE_PRESSED, location, location, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON); @@ -845,8 +844,9 @@ class WidgetActivationTest : public Widget { ~WidgetActivationTest() override {} - void OnNativeWidgetActivationChanged(bool active) override { + bool OnNativeWidgetActivationChanged(bool active) override { active_ = active; + return true; } bool active() const { return active_; } diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index 76a9b283676..b978aabb83c 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -3226,13 +3226,7 @@ class FullscreenAwareFrame : public views::NonClientFrameView { // Tests that frame Layout is called when a widget goes fullscreen without // changing its size or title. -// Fails on Mac, but only on bots. http://crbug.com/607403. -#if defined(OS_MACOSX) -#define MAYBE_FullscreenFrameLayout DISABLED_FullscreenFrameLayout -#else -#define MAYBE_FullscreenFrameLayout FullscreenFrameLayout -#endif -TEST_F(WidgetTest, MAYBE_FullscreenFrameLayout) { +TEST_F(WidgetTest, FullscreenFrameLayout) { WidgetAutoclosePtr widget(CreateTopLevelPlatformWidget()); FullscreenAwareFrame* frame = new FullscreenAwareFrame(widget.get()); widget->non_client_view()->SetFrameView(frame); // Owns |frame|. diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index c8fb7eecb9c..6a468941cf4 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -31,7 +31,6 @@ #include "ui/base/ime/text_input_type.h" #include "ui/base/ui_base_features.h" #include "ui/base/view_prop.h" -#include "ui/base/win/direct_manipulation.h" #include "ui/base/win/internal_constants.h" #include "ui/base/win/lock_state.h" #include "ui/base/win/mouse_wheel_util.h" @@ -369,6 +368,8 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) left_button_down_on_caption_(false), background_fullscreen_hack_(false), pointer_events_for_touch_(features::IsUsingWMPointerForTouch()), + precision_touchpad_scroll_phase_enabled_(base::FeatureList::IsEnabled( + features::kPrecisionTouchpadScrollPhase)), autohide_factory_(this), weak_factory_(this) {} @@ -411,13 +412,6 @@ void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { DCHECK(delegate_->GetHWNDMessageDelegateInputMethod()); delegate_->GetHWNDMessageDelegateInputMethod()->AddObserver(this); - // Direct Manipulation is enabled on Windows 10+. The CreateInstance function - // returns NULL if Direct Manipulation is not available. - direct_manipulation_helper_ = - ui::win::DirectManipulationHelper::CreateInstance(); - if (direct_manipulation_helper_) - direct_manipulation_helper_->Initialize(hwnd()); - // Disable pen flicks (http://crbug.com/506977) base::win::DisableFlicks(hwnd()); } @@ -456,8 +450,8 @@ void HWNDMessageHandler::Close() { // dereference us when the callback returns). waiting_for_close_now_ = true; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&HWNDMessageHandler::CloseNow, weak_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&HWNDMessageHandler::CloseNow, + weak_factory_.GetWeakPtr())); } } @@ -601,8 +595,6 @@ void HWNDMessageHandler::Show() { ShowWindowWithState(ui::SHOW_STATE_INACTIVE); } } - if (direct_manipulation_helper_) - direct_manipulation_helper_->Activate(hwnd()); } void HWNDMessageHandler::ShowWindowWithState(ui::WindowShowState show_state) { @@ -1068,6 +1060,119 @@ void HWNDMessageHandler::HandleParentChanged() { touch_ids_.clear(); } +void HWNDMessageHandler::ApplyPinchZoomScale(float scale) { + POINT cursor_pos = {0}; + ::GetCursorPos(&cursor_pos); + ScreenToClient(hwnd(), &cursor_pos); + + ui::GestureEventDetails event_details(ui::ET_GESTURE_PINCH_UPDATE); + event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD); + event_details.set_scale(scale); + + ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE, + base::TimeTicks::Now(), event_details); + delegate_->HandleGestureEvent(&event); +} + +void HWNDMessageHandler::ApplyPinchZoomBegin() { + POINT cursor_pos = {0}; + ::GetCursorPos(&cursor_pos); + ScreenToClient(hwnd(), &cursor_pos); + + ui::GestureEventDetails event_details(ui::ET_GESTURE_PINCH_BEGIN); + event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD); + + ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE, + base::TimeTicks::Now(), event_details); + delegate_->HandleGestureEvent(&event); +} + +void HWNDMessageHandler::ApplyPinchZoomEnd() { + POINT cursor_pos = {0}; + ::GetCursorPos(&cursor_pos); + ScreenToClient(hwnd(), &cursor_pos); + + ui::GestureEventDetails event_details(ui::ET_GESTURE_PINCH_END); + event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD); + + ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE, + base::TimeTicks::Now(), event_details); + delegate_->HandleGestureEvent(&event); +} + +void HWNDMessageHandler::ApplyPanGestureEvent( + int scroll_x, + int scroll_y, + ui::EventMomentumPhase momentum_phase, + ui::ScrollEventPhase phase) { + gfx::Vector2d offset{scroll_x, scroll_y}; + + POINT root_location = {0}; + ::GetCursorPos(&root_location); + + POINT location = {root_location.x, root_location.y}; + ScreenToClient(hwnd(), &location); + + gfx::Point cursor_location(location); + gfx::Point cursor_root_location(root_location); + + int modifiers = ui::GetModifiersFromKeyState(); + + if (precision_touchpad_scroll_phase_enabled_) { + ui::ScrollEvent event(ui::ET_SCROLL, cursor_location, ui::EventTimeForNow(), + modifiers, scroll_x, scroll_y, scroll_x, scroll_y, 2, + momentum_phase, phase); + delegate_->HandleScrollEvent(&event); + } else { + ui::MouseWheelEvent wheel_event( + offset, cursor_location, cursor_root_location, base::TimeTicks::Now(), + modifiers | ui::EF_PRECISION_SCROLLING_DELTA, ui::EF_NONE); + delegate_->HandleMouseEvent(&wheel_event); + } +} + +void HWNDMessageHandler::ApplyPanGestureScroll(int scroll_x, int scroll_y) { + ApplyPanGestureEvent(scroll_x, scroll_y, ui::EventMomentumPhase::NONE, + ui::ScrollEventPhase::kUpdate); +} + +void HWNDMessageHandler::ApplyPanGestureFling(int scroll_x, int scroll_y) { + ApplyPanGestureEvent(scroll_x, scroll_y, + ui::EventMomentumPhase::INERTIAL_UPDATE, + ui::ScrollEventPhase::kNone); +} + +void HWNDMessageHandler::ApplyPanGestureScrollBegin(int scroll_x, + int scroll_y) { + // Phase information will be ingored in ApplyPanGestureEvent(). + ApplyPanGestureEvent(scroll_x, scroll_y, ui::EventMomentumPhase::NONE, + ui::ScrollEventPhase::kBegan); +} + +void HWNDMessageHandler::ApplyPanGestureScrollEnd() { + if (!precision_touchpad_scroll_phase_enabled_) + return; + + ApplyPanGestureEvent(0, 0, ui::EventMomentumPhase::NONE, + ui::ScrollEventPhase::kEnd); +} + +void HWNDMessageHandler::ApplyPanGestureFlingBegin() { + if (!precision_touchpad_scroll_phase_enabled_) + return; + + ApplyPanGestureEvent(0, 0, ui::EventMomentumPhase::BEGAN, + ui::ScrollEventPhase::kNone); +} + +void HWNDMessageHandler::ApplyPanGestureFlingEnd() { + if (!precision_touchpad_scroll_phase_enabled_) + return; + + ApplyPanGestureEvent(0, 0, ui::EventMomentumPhase::END, + ui::ScrollEventPhase::kNone); +} + //////////////////////////////////////////////////////////////////////////////// // HWNDMessageHandler, private: @@ -1334,8 +1439,9 @@ void HWNDMessageHandler::ForceRedrawWindow(int attempts) { if (--attempts <= 0) return; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&HWNDMessageHandler::ForceRedrawWindow, - weak_factory_.GetWeakPtr(), attempts), + FROM_HERE, + base::BindOnce(&HWNDMessageHandler::ForceRedrawWindow, + weak_factory_.GetWeakPtr(), attempts), base::TimeDelta::FromMilliseconds(500)); return; } @@ -2127,7 +2233,7 @@ LRESULT HWNDMessageHandler::OnScrollMessage(UINT message, MSG msg = { hwnd(), message, w_param, l_param, static_cast<DWORD>(GetMessageTime())}; ui::ScrollEvent event(msg); - delegate_->HandleScrollEvent(event); + delegate_->HandleScrollEvent(&event); return 0; } @@ -2347,8 +2453,9 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, event_time, &touch_events); touch_down_contexts_++; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&HWNDMessageHandler::ResetTouchDownContext, - weak_factory_.GetWeakPtr()), + FROM_HERE, + base::BindOnce(&HWNDMessageHandler::ResetTouchDownContext, + weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kTouchDownContextResetTimeout)); } else { if (input[i].dwFlags & TOUCHEVENTF_MOVE) { @@ -2381,8 +2488,8 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, // events on windows don't fire if we enter a modal loop in the context of // a touch event. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&HWNDMessageHandler::HandleTouchEvents, - weak_factory_.GetWeakPtr(), touch_events)); + FROM_HERE, base::BindOnce(&HWNDMessageHandler::HandleTouchEvents, + weak_factory_.GetWeakPtr(), touch_events)); } CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(l_param)); SetMsgHandled(FALSE); @@ -2487,8 +2594,9 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { // and send us further updates. ignore_window_pos_changes_ = true; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&HWNDMessageHandler::StopIgnoringPosChanges, - weak_factory_.GetWeakPtr())); + FROM_HERE, + base::BindOnce(&HWNDMessageHandler::StopIgnoringPosChanges, + weak_factory_.GetWeakPtr())); } last_monitor_ = monitor; last_monitor_rect_ = monitor_rect; @@ -2540,13 +2648,9 @@ void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { SetDwmFrameExtension(DwmFrameState::ON); if (window_pos->flags & SWP_SHOWWINDOW) { delegate_->HandleVisibilityChanged(true); - if (direct_manipulation_helper_) - direct_manipulation_helper_->Activate(hwnd()); SetDwmFrameExtension(DwmFrameState::ON); } else if (window_pos->flags & SWP_HIDEWINDOW) { delegate_->HandleVisibilityChanged(false); - if (direct_manipulation_helper_) - direct_manipulation_helper_->Deactivate(hwnd()); } SetMsgHandled(FALSE); @@ -2575,8 +2679,10 @@ void HWNDMessageHandler::OnSessionChange(WPARAM status_code) { void HWNDMessageHandler::HandleTouchEvents(const TouchEvents& touch_events) { base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); - for (size_t i = 0; i < touch_events.size() && ref; ++i) - delegate_->HandleTouchEvent(touch_events[i]); + for (size_t i = 0; i < touch_events.size() && ref; ++i) { + ui::TouchEvent* touch_event = const_cast<ui::TouchEvent*>(&touch_events[i]); + delegate_->HandleTouchEvent(touch_event); + } } void HWNDMessageHandler::ResetTouchDownContext() { @@ -2686,25 +2792,22 @@ LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message, // OnMouseEvent. active_mouse_tracking_flags_ = 0; } else if (event.type() == ui::ET_MOUSEWHEEL) { + ui::MouseWheelEvent mouse_wheel_event(msg); // Reroute the mouse wheel to the window under the pointer if applicable. return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) || - delegate_->HandleMouseEvent(ui::MouseWheelEvent(msg))) ? 0 : 1; + delegate_->HandleMouseEvent(&mouse_wheel_event)) + ? 0 + : 1; } // There are cases where the code handling the message destroys the window, // so use the weak ptr to check if destruction occured or not. base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); - bool handled = delegate_->HandleMouseEvent(event); + bool handled = delegate_->HandleMouseEvent(&event); if (!ref.get()) return 0; - if (direct_manipulation_helper_ && track_mouse && - (message == WM_MOUSEWHEEL || message == WM_MOUSEHWHEEL)) { - direct_manipulation_helper_->HandleMouseWheel(hwnd(), message, w_param, - l_param); - } - if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU && w_param != HTCAPTION && delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { @@ -2799,7 +2902,7 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, // There are cases where the code handling the message destroys the // window, so use the weak ptr to check if destruction occurred or not. base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); - delegate_->HandleTouchEvent(event); + delegate_->HandleTouchEvent(&event); if (event_type == ui::ET_TOUCH_RELEASED) id_generator_.ReleaseNumber(pointer_id); @@ -2835,9 +2938,9 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypePen(UINT message, base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); if (event) { if (event->IsTouchEvent()) { - delegate_->HandleTouchEvent(*event->AsTouchEvent()); + delegate_->HandleTouchEvent(event->AsTouchEvent()); } else if (event->IsMouseEvent()) { - delegate_->HandleMouseEvent(*event->AsMouseEvent()); + delegate_->HandleMouseEvent(event->AsMouseEvent()); } else { NOTREACHED(); } @@ -3031,9 +3134,6 @@ void HWNDMessageHandler::SetBoundsInternal(const gfx::Rect& bounds_in_pixels, delegate_->HandleClientSizeChanged(GetClientAreaBounds().size()); ResetWindowRegion(false, true); } - - if (direct_manipulation_helper_) - direct_manipulation_helper_->SetBounds(bounds_in_pixels); } void HWNDMessageHandler::CheckAndHandleBackgroundFullscreenOnMonitor( diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index 5d5765c1928..11c76b89e38 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -41,9 +41,6 @@ class AXSystemCaretWin; class InputMethod; class TextInputClient; class ViewProp; -namespace win { -class DirectManipulationHelper; -} // namespace win } // namespace ui namespace views { @@ -261,13 +258,25 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, WPARAM w_param, LPARAM l_param, bool* handled) override; - LRESULT HandleNcHitTestMessage(unsigned int message, WPARAM w_param, LPARAM l_param, bool* handled) override; - void HandleParentChanged() override; + void ApplyPinchZoomScale(float scale) override; + void ApplyPinchZoomBegin() override; + void ApplyPinchZoomEnd() override; + void ApplyPanGestureScroll(int scroll_x, int scroll_y) override; + void ApplyPanGestureFling(int scroll_x, int scroll_y) override; + void ApplyPanGestureScrollBegin(int scroll_x, int scroll_y) override; + void ApplyPanGestureScrollEnd() override; + void ApplyPanGestureFlingBegin() override; + void ApplyPanGestureFlingEnd() override; + + void ApplyPanGestureEvent(int scroll_x, + int scroll_y, + ui::EventMomentumPhase momentum_phase, + ui::ScrollEventPhase phase); // Returns the auto-hide edges of the appbar. See // ViewsDelegate::GetAppbarAutohideEdges() for details. If the edges change, @@ -728,12 +737,6 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, // Some assistive software need to track the location of the caret. std::unique_ptr<ui::AXSystemCaretWin> ax_system_caret_; - // This class provides functionality to register the legacy window as a - // Direct Manipulation consumer. This allows us to support smooth scroll - // in Chrome on Windows 10. - std::unique_ptr<ui::win::DirectManipulationHelper> - direct_manipulation_helper_; - // The location where the user clicked on the caption. We cache this when we // receive the WM_NCLBUTTONDOWN message. We use this in the subsequent // WM_NCMOUSEMOVE message to see if the mouse actually moved. @@ -757,6 +760,10 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, // not WM_TOUCH events. bool pointer_events_for_touch_; + // True if we enable feature kPrecisionTouchpadScrollPhase. Indicate we will + // report the scroll phase information or not. + bool precision_touchpad_scroll_phase_enabled_; + // This is a map of the HMONITOR to full screeen window instance. It is safe // to keep a raw pointer to the HWNDMessageHandler instance as we track the // window destruction and ensure that the map is cleaned up. diff --git a/chromium/ui/views/win/hwnd_message_handler_delegate.h b/chromium/ui/views/win/hwnd_message_handler_delegate.h index d13f807e4a7..1b2d98a8573 100644 --- a/chromium/ui/views/win/hwnd_message_handler_delegate.h +++ b/chromium/ui/views/win/hwnd_message_handler_delegate.h @@ -186,7 +186,7 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // Called when a mouse event is received. Returns true if the event was // handled by the delegate. - virtual bool HandleMouseEvent(const ui::MouseEvent& event) = 0; + virtual bool HandleMouseEvent(ui::MouseEvent* event) = 0; // Called when a pointer event is received. Returns true if the event was // handled by the delegate. @@ -197,7 +197,7 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { virtual void HandleKeyEvent(ui::KeyEvent* event) = 0; // Called when a touch event is received. - virtual void HandleTouchEvent(const ui::TouchEvent& event) = 0; + virtual void HandleTouchEvent(ui::TouchEvent* event) = 0; // Called when an IME message needs to be processed by the delegate. Returns // true if the event was handled and no default processing should be @@ -241,7 +241,11 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // Called when a scroll event is received. Returns true if the event was // handled by the delegate. - virtual bool HandleScrollEvent(const ui::ScrollEvent& event) = 0; + virtual bool HandleScrollEvent(ui::ScrollEvent* event) = 0; + + // Called when a gesture event is received. Returns true if the event was + // handled by the delegate. + virtual bool HandleGestureEvent(ui::GestureEvent* event) = 0; // Called when the window size is about to change. virtual void HandleWindowSizeChanging() = 0; diff --git a/chromium/ui/views/win/pen_event_processor.cc b/chromium/ui/views/win/pen_event_processor.cc index 17f51705029..5de8173f343 100644 --- a/chromium/ui/views/win/pen_event_processor.cc +++ b/chromium/ui/views/win/pen_event_processor.cc @@ -4,7 +4,6 @@ #include "pen_event_processor.h" -#include "base/memory/ptr_util.h" #include "base/time/time.h" #include "ui/events/event_utils.h" diff --git a/chromium/ui/views/word_lookup_client.h b/chromium/ui/views/word_lookup_client.h index d9c200f5566..15b9c795df6 100644 --- a/chromium/ui/views/word_lookup_client.h +++ b/chromium/ui/views/word_lookup_client.h @@ -22,9 +22,13 @@ class VIEWS_EXPORT WordLookupClient { // displayed at the point, returns a nearby word. |baseline_point| should // correspond to the baseline point of the leftmost glyph of the |word| in the // view's coordinates. Returns false, if no word can be retrieved. - virtual bool GetDecoratedWordAtPoint(const gfx::Point& point, - gfx::DecoratedText* decorated_word, - gfx::Point* baseline_point) = 0; + virtual bool GetWordLookupDataAtPoint(const gfx::Point& point, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) = 0; + + virtual bool GetWordLookupDataFromSelection( + gfx::DecoratedText* decorated_text, + gfx::Point* baseline_point) = 0; protected: virtual ~WordLookupClient() {} diff --git a/chromium/ui/web_dialogs/BUILD.gn b/chromium/ui/web_dialogs/BUILD.gn index 63923b1f1ed..54a3320caef 100644 --- a/chromium/ui/web_dialogs/BUILD.gn +++ b/chromium/ui/web_dialogs/BUILD.gn @@ -24,11 +24,12 @@ jumbo_component("web_dialogs") { "//content/public/common", "//skia", "//ui/base", + "//ui/webui", "//url", ] if (!is_ios) { - deps += [ "//third_party/WebKit/public:blink_headers" ] + deps += [ "//third_party/blink/public:blink_headers" ] } } diff --git a/chromium/ui/web_dialogs/DEPS b/chromium/ui/web_dialogs/DEPS index d988b2ff7e0..6bcf66b60ac 100644 --- a/chromium/ui/web_dialogs/DEPS +++ b/chromium/ui/web_dialogs/DEPS @@ -1,6 +1,7 @@ include_rules = [ "+content/public", - "+third_party/WebKit/public/platform/WebGestureEvent.h", + "+third_party/blink/public/platform/web_gesture_event.h", "+ui/base", "+ui/gfx", + "+ui/webui", ] diff --git a/chromium/ui/web_dialogs/web_dialog_ui.cc b/chromium/ui/web_dialogs/web_dialog_ui.cc index 5b72e0c04f9..9b1d765ce2d 100644 --- a/chromium/ui/web_dialogs/web_dialog_ui.cc +++ b/chromium/ui/web_dialogs/web_dialog_ui.cc @@ -39,37 +39,31 @@ class WebDialogDelegateUserData : public base::SupportsUserData::Data { } // namespace -WebDialogUI::WebDialogUI(content::WebUI* web_ui) - : WebUIController(web_ui) { -} - -WebDialogUI::~WebDialogUI() { - // Don't unregister our user data. During the teardown of the WebContents, - // this will be deleted, but the WebContents will already be destroyed. - // - // This object is owned indirectly by the WebContents. WebUIs can change, so - // it's scary if this WebUI is changed out and replaced with something else, - // since the user data will still point to the old delegate. But the delegate - // is itself the owner of the WebContents for a dialog so will be in scope, - // and the HTML dialogs won't swap WebUIs anyway since they don't navigate. -} - -void WebDialogUI::CloseDialog(const base::ListValue* args) { - OnDialogClosed(args); -} - // static -void WebDialogUI::SetDelegate(content::WebContents* web_contents, - WebDialogDelegate* delegate) { +void WebDialogUIBase::SetDelegate(content::WebContents* web_contents, + WebDialogDelegate* delegate) { web_contents->SetUserData( &kWebDialogDelegateUserDataKey, std::make_unique<WebDialogDelegateUserData>(delegate)); } -//////////////////////////////////////////////////////////////////////////////// -// Private: +WebDialogUIBase::WebDialogUIBase(content::WebUI* web_ui) : web_ui_(web_ui) {} + +// Don't unregister our user data. During the teardown of the WebContents, this +// will be deleted, but the WebContents will already be destroyed. +// +// This object is owned indirectly by the WebContents. WebUIs can change, so +// it's scary if this WebUI is changed out and replaced with something else, +// since the user data will still point to the old delegate. But the delegate is +// itself the owner of the WebContents for a dialog so will be in scope, and the +// HTML dialogs won't swap WebUIs anyway since they don't navigate. +WebDialogUIBase::~WebDialogUIBase() = default; + +void WebDialogUIBase::CloseDialog(const base::ListValue* args) { + OnDialogClosed(args); +} -WebDialogDelegate* WebDialogUI::GetDelegate( +WebDialogDelegate* WebDialogUIBase::GetDelegate( content::WebContents* web_contents) { WebDialogDelegateUserData* user_data = static_cast<WebDialogDelegateUserData*>( @@ -78,17 +72,18 @@ WebDialogDelegate* WebDialogUI::GetDelegate( return user_data ? user_data->delegate() : NULL; } - -void WebDialogUI::RenderFrameCreated(RenderFrameHost* render_frame_host) { +void WebDialogUIBase::HandleRenderFrameCreated( + RenderFrameHost* render_frame_host) { // Hook up the javascript function calls, also known as chrome.send("foo") // calls in the HTML, to the actual C++ functions. - web_ui()->RegisterMessageCallback("dialogClose", - base::Bind(&WebDialogUI::OnDialogClosed, base::Unretained(this))); + web_ui_->RegisterMessageCallback( + "dialogClose", base::BindRepeating(&WebDialogUIBase::OnDialogClosed, + base::Unretained(this))); // Pass the arguments to the renderer supplied by the delegate. std::string dialog_args; std::vector<WebUIMessageHandler*> handlers; - WebDialogDelegate* delegate = GetDelegate(web_ui()->GetWebContents()); + WebDialogDelegate* delegate = GetDelegate(web_ui_->GetWebContents()); if (delegate) { dialog_args = delegate->GetDialogArgs(); delegate->GetWebUIMessageHandlers(&handlers); @@ -96,17 +91,17 @@ void WebDialogUI::RenderFrameCreated(RenderFrameHost* render_frame_host) { content::RenderViewHost* render_view_host = render_frame_host->GetRenderViewHost(); - if (0 != (web_ui()->GetBindings() & content::BINDINGS_POLICY_WEB_UI)) + if (0 != (web_ui_->GetBindings() & content::BINDINGS_POLICY_WEB_UI)) render_view_host->SetWebUIProperty("dialogArguments", dialog_args); for (WebUIMessageHandler* handler : handlers) - web_ui()->AddMessageHandler(base::WrapUnique(handler)); + web_ui_->AddMessageHandler(base::WrapUnique(handler)); if (delegate) - delegate->OnDialogShown(web_ui(), render_view_host); + delegate->OnDialogShown(web_ui_, render_view_host); } -void WebDialogUI::OnDialogClosed(const base::ListValue* args) { - WebDialogDelegate* delegate = GetDelegate(web_ui()->GetWebContents()); +void WebDialogUIBase::OnDialogClosed(const base::ListValue* args) { + WebDialogDelegate* delegate = GetDelegate(web_ui_->GetWebContents()); if (delegate) { std::string json_retval; if (args && !args->empty() && !args->GetString(0, &json_retval)) @@ -116,4 +111,13 @@ void WebDialogUI::OnDialogClosed(const base::ListValue* args) { } } +WebDialogUI::WebDialogUI(content::WebUI* web_ui) + : WebDialogUIBase(web_ui), content::WebUIController(web_ui) {} + +WebDialogUI::~WebDialogUI() = default; + +void WebDialogUI::RenderFrameCreated(RenderFrameHost* render_frame_host) { + HandleRenderFrameCreated(render_frame_host); +} + } // namespace ui diff --git a/chromium/ui/web_dialogs/web_dialog_ui.h b/chromium/ui/web_dialogs/web_dialog_ui.h index 001de1956e7..8c1c726a2be 100644 --- a/chromium/ui/web_dialogs/web_dialog_ui.h +++ b/chromium/ui/web_dialogs/web_dialog_ui.h @@ -15,6 +15,7 @@ #include "content/public/browser/web_ui_controller.h" #include "ui/base/ui_base_types.h" #include "ui/web_dialogs/web_dialogs_export.h" +#include "ui/webui/mojo_web_ui_controller.h" #include "url/gurl.h" namespace content { @@ -25,6 +26,35 @@ namespace ui { class WebDialogDelegate; +class WEB_DIALOGS_EXPORT WebDialogUIBase { + public: + // Sets the delegate on the WebContents. + static void SetDelegate(content::WebContents* web_contents, + WebDialogDelegate* delegate); + + WebDialogUIBase(content::WebUI* web_ui); + + // Close the dialog, passing the specified arguments to the close handler. + void CloseDialog(const base::ListValue* args); + + protected: + virtual ~WebDialogUIBase(); + + // Prepares |render_frame_host| to host a dialog. + void HandleRenderFrameCreated(content::RenderFrameHost* render_frame_host); + + private: + // Gets the delegate for the WebContent set with SetDelegate. + static WebDialogDelegate* GetDelegate(content::WebContents* web_contents); + + // JS message handler. + void OnDialogClosed(const base::ListValue* args); + + content::WebUI* web_ui_; + + DISALLOW_COPY_AND_ASSIGN(WebDialogUIBase); +}; + // Displays file URL contents inside a modal web dialog. // // This application really should not use WebContents + WebUI. It should instead @@ -36,42 +66,43 @@ class WebDialogDelegate; // the dialog to pass its delegate to the Web UI without having nasty accessors // on the WebContents. The correct design using RVH directly would avoid all of // this. -class WEB_DIALOGS_EXPORT WebDialogUI : public content::WebUIController { +class WEB_DIALOGS_EXPORT WebDialogUI : public WebDialogUIBase, + public content::WebUIController { public: - struct WebDialogParams { - // The URL for the content that will be loaded in the dialog. - GURL url; - // Width of the dialog. - int width; - // Height of the dialog. - int height; - // The JSON input to pass to the dialog when showing it. - std::string json_input; - }; - // When created, the delegate should already be set as user data on the // WebContents. explicit WebDialogUI(content::WebUI* web_ui); ~WebDialogUI() override; - // Close the dialog, passing the specified arguments to the close handler. - void CloseDialog(const base::ListValue* args); - - // Sets the delegate on the WebContents. - static void SetDelegate(content::WebContents* web_contents, - WebDialogDelegate* delegate); - private: - // WebUIController + // content::WebUIController: void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; - // Gets the delegate for the WebContent set with SetDelegate. - static WebDialogDelegate* GetDelegate(content::WebContents* web_contents); + DISALLOW_COPY_AND_ASSIGN(WebDialogUI); +}; - // JS message handler. - void OnDialogClosed(const base::ListValue* args); +// Displays file URL contents inside a modal web dialog while also enabling +// Mojo calls to be made from within the dialog. +template <typename Interface> +class WEB_DIALOGS_EXPORT MojoWebDialogUI + : public WebDialogUIBase, + public MojoWebUIController<Interface> { + public: + // When created, the delegate should already be set as user data on the + // WebContents. + explicit MojoWebDialogUI(content::WebUI* web_ui) + : WebDialogUIBase(web_ui), MojoWebUIController<Interface>(web_ui) {} + ~MojoWebDialogUI() override {} - DISALLOW_COPY_AND_ASSIGN(WebDialogUI); + private: + // content::WebUIController: + void RenderFrameCreated( + content::RenderFrameHost* render_frame_host) override { + MojoWebUIControllerBase::RenderFrameCreated(render_frame_host); + HandleRenderFrameCreated(render_frame_host); + } + + DISALLOW_COPY_AND_ASSIGN(MojoWebDialogUI); }; } // namespace ui diff --git a/chromium/ui/web_dialogs/web_dialog_web_contents_delegate.cc b/chromium/ui/web_dialogs/web_dialog_web_contents_delegate.cc index 59f1658492f..758974b6279 100644 --- a/chromium/ui/web_dialogs/web_dialog_web_contents_delegate.cc +++ b/chromium/ui/web_dialogs/web_dialog_web_contents_delegate.cc @@ -6,7 +6,7 @@ #include "base/logging.h" #include "content/public/browser/web_contents.h" -#include "third_party/WebKit/public/platform/WebGestureEvent.h" +#include "third_party/blink/public/platform/web_gesture_event.h" using content::BrowserContext; using content::OpenURLParams; diff --git a/chromium/ui/webui/BUILD.gn b/chromium/ui/webui/BUILD.gn new file mode 100644 index 00000000000..7757ffde500 --- /dev/null +++ b/chromium/ui/webui/BUILD.gn @@ -0,0 +1,17 @@ +# 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. + +static_library("webui") { + sources = [ + "mojo_web_ui_controller.cc", + "mojo_web_ui_controller.h", + ] + + deps = [ + "//base", + "//content/public/browser", + "//mojo/common", + "//services/service_manager/public/cpp", + ] +} diff --git a/chromium/ui/webui/DEPS b/chromium/ui/webui/DEPS index 69b82adcf09..27c4e74db70 100644 --- a/chromium/ui/webui/DEPS +++ b/chromium/ui/webui/DEPS @@ -1,5 +1,8 @@ include_rules = [ + "+content/public", + "+mojo/public/cpp/system/core.h", "+net", + "+services/service_manager/public/cpp/binder_registry.h", "+ui/base", "+ui/gfx", ] diff --git a/chromium/ui/webui/PLATFORM_OWNERS b/chromium/ui/webui/PLATFORM_OWNERS index a4e5de4c6f9..e685160dc50 100644 --- a/chromium/ui/webui/PLATFORM_OWNERS +++ b/chromium/ui/webui/PLATFORM_OWNERS @@ -5,5 +5,6 @@ dpapad@chromium.org dschuyler@chromium.org michaelpg@chromium.org pam@chromium.org +stevenjb@chromium.org tommycli@chromium.org xiyuan@chromium.org diff --git a/chromium/ui/webui/mojo_web_ui_controller.cc b/chromium/ui/webui/mojo_web_ui_controller.cc new file mode 100644 index 00000000000..9005100a47c --- /dev/null +++ b/chromium/ui/webui/mojo_web_ui_controller.cc @@ -0,0 +1,21 @@ +// 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/webui/mojo_web_ui_controller.h" + +#include "content/public/common/bindings_policy.h" + +namespace ui { + +MojoWebUIControllerBase::MojoWebUIControllerBase(content::WebUI* contents) + : content::WebUIController(contents) {} + +MojoWebUIControllerBase::~MojoWebUIControllerBase() = default; + +void MojoWebUIControllerBase::RenderFrameCreated( + content::RenderFrameHost* render_frame_host) { + render_frame_host->AllowBindings(content::BINDINGS_POLICY_WEB_UI); +} + +} // namespace ui diff --git a/chromium/ui/webui/mojo_web_ui_controller.h b/chromium/ui/webui/mojo_web_ui_controller.h new file mode 100644 index 00000000000..26f200f8b51 --- /dev/null +++ b/chromium/ui/webui/mojo_web_ui_controller.h @@ -0,0 +1,78 @@ +// 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_WEBUI_MOJO_WEB_UI_CONTROLLER_H_ +#define UI_WEBUI_MOJO_WEB_UI_CONTROLLER_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_ui.h" +#include "content/public/browser/web_ui_controller.h" +#include "mojo/public/cpp/system/core.h" +#include "services/service_manager/public/cpp/binder_registry.h" + +namespace ui { + +class MojoWebUIControllerBase : public content::WebUIController { + public: + explicit MojoWebUIControllerBase(content::WebUI* contents); + ~MojoWebUIControllerBase() override; + + // content::WebUIController overrides: + void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; + + private: + DISALLOW_COPY_AND_ASSIGN(MojoWebUIControllerBase); +}; + +// MojoWebUIController is intended for web ui pages that use mojo. It is +// expected that subclasses will do two things: +// . In the constructor invoke AddMojoResourcePath() to register the bindings +// files, eg: +// AddMojoResourcePath("chrome/browser/ui/webui/omnibox/omnibox.mojom", +// IDR_OMNIBOX_MOJO_JS); +// . Override BindUIHandler() to create and bind the implementation of the +// bindings. +template <typename Interface> +class MojoWebUIController : public MojoWebUIControllerBase, + public content::WebContentsObserver { + public: + explicit MojoWebUIController(content::WebUI* contents) + : MojoWebUIControllerBase(contents), + content::WebContentsObserver(contents->GetWebContents()), + weak_factory_(this) { + registry_.AddInterface<Interface>(base::Bind( + &MojoWebUIController::BindUIHandler, base::Unretained(this))); + } + ~MojoWebUIController() override {} + + // content::WebContentsObserver implementation. + void OnInterfaceRequestFromFrame( + content::RenderFrameHost* render_frame_host, + const std::string& interface_name, + mojo::ScopedMessagePipeHandle* interface_pipe) override { + // Right now, this is expected to be called only for main frames. + DCHECK(!render_frame_host->GetParent()); + registry_.TryBindInterface(interface_name, interface_pipe); + } + + protected: + // Invoked to create the specific bindings implementation. + virtual void BindUIHandler(mojo::InterfaceRequest<Interface> request) = 0; + + private: + service_manager::BinderRegistry registry_; + + base::WeakPtrFactory<MojoWebUIController> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MojoWebUIController); +}; + +} // namespace ui + +#endif // UI_WEBUI_MOJO_WEB_UI_CONTROLLER_H_ diff --git a/chromium/ui/webui/resources/BUILD.gn b/chromium/ui/webui/resources/BUILD.gn new file mode 100644 index 00000000000..d7050d52ba8 --- /dev/null +++ b/chromium/ui/webui/resources/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +group("closure_compile") { + deps = [ + "cr_components:closure_compile", + "cr_elements:closure_compile", + "js:closure_compile", + ] +} diff --git a/chromium/ui/webui/resources/PRESUBMIT.py b/chromium/ui/webui/resources/PRESUBMIT.py index 26bc5ec895e..04c5e4e2bf8 100644 --- a/chromium/ui/webui/resources/PRESUBMIT.py +++ b/chromium/ui/webui/resources/PRESUBMIT.py @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import os + def PostUploadHook(cl, change, output_api): return output_api.EnsureCQIncludeTrybotsAreAdded( cl, @@ -18,6 +20,30 @@ def CheckChangeOnUpload(input_api, output_api): def CheckChangeOnCommit(input_api, output_api): return _CommonChecks(input_api, output_api) +# For every modified gyp file, warn if the corresponding GN file is not updated. +def _CheckForGNUpdate(input_api, output_api): + gyp_folders = set() + for f in input_api.AffectedFiles(): + local_path = f.LocalPath() + if local_path.endswith('compiled_resources2.gyp'): + gyp_folders.add(os.path.dirname(local_path)) + + for f in input_api.AffectedFiles(): + local_path = f.LocalPath() + dir_name = os.path.dirname(local_path) + if local_path.endswith('BUILD.gn') and dir_name in gyp_folders: + gyp_folders.remove(dir_name) + + if not gyp_folders: + return [] + + return [output_api.PresubmitPromptWarning(""" +You may have forgotten to update the BUILD.gn Closure Compilation for the +following folders: +""" + "\n".join(["- " + x for x in gyp_folders]) + """ + +Ping calamity@ or check go/closure-compile-gn for more details. +""")] def _CheckForTranslations(input_api, output_api): shared_keywords = ['i18n('] @@ -60,6 +86,7 @@ translation from the place using the shared code. For an example: see def _CommonChecks(input_api, output_api): results = [] results += _CheckForTranslations(input_api, output_api) + results += _CheckForGNUpdate(input_api, output_api) results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api, check_js=True) try: diff --git a/chromium/ui/webui/resources/cr_components/BUILD.gn b/chromium/ui/webui/resources/cr_components/BUILD.gn new file mode 100644 index 00000000000..f3d71a3a372 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/BUILD.gn @@ -0,0 +1,12 @@ +# 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") + +group("closure_compile") { + deps = [ + "certificate_manager:closure_compile", + "chromeos:closure_compile", + ] +} diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/BUILD.gn b/chromium/ui/webui/resources/cr_components/certificate_manager/BUILD.gn new file mode 100644 index 00000000000..2c8a161a813 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/BUILD.gn @@ -0,0 +1,128 @@ +# 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 = [ + ":ca_trust_edit_dialog", + ":certificate_delete_confirmation_dialog", + ":certificate_entry", + ":certificate_list", + ":certificate_manager", + ":certificate_manager_types", + ":certificate_password_decryption_dialog", + ":certificate_password_encryption_dialog", + ":certificate_subentry", + ":certificates_browser_proxy", + ":certificates_error_dialog", + ] +} + +js_library("ca_trust_edit_dialog") { + deps = [ + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js:load_time_data", + ] +} + +js_library("certificate_delete_confirmation_dialog") { + deps = [ + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js:load_time_data", + ] +} + +js_library("certificate_entry") { + deps = [ + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("certificate_list") { + deps = [ + ":certificate_manager_types", + ":certificate_subentry", + ":certificates_browser_proxy", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("certificate_manager") { + deps = [ + ":certificate_list", + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:load_time_data", + "//ui/webui/resources/js:web_ui_listener_behavior", + "//ui/webui/resources/js/cr/ui:focus_without_ink", + ] +} + +js_library("certificate_manager_types") { + deps = [ + ":certificates_browser_proxy", + ] +} + +js_library("certificate_password_decryption_dialog") { + deps = [ + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("certificate_password_encryption_dialog") { + deps = [ + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("certificate_subentry") { + deps = [ + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu", + "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("certificates_browser_proxy") { + deps = [ + "//ui/webui/resources/js:cr", + ] +} + +js_library("certificates_error_dialog") { + deps = [ + ":certificate_manager_types", + ":certificates_browser_proxy", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js:load_time_data", + ] +} diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html index 16e035ec313..388faf5d32e 100644 --- a/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html @@ -23,7 +23,7 @@ } </style> - <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <cr-dialog id="dialog" close-text="[[i18n('close')]]"> <div slot="title"> [[i18n('certificateManagerCaTrustEditDialogTitle')]] </div> @@ -51,7 +51,7 @@ [[i18n('ok')]] </paper-button> </div> - </dialog> + </cr-dialog> </template> <script src="ca_trust_edit_dialog.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html index fbf253c3a5d..73d8209664f 100644 --- a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html @@ -9,7 +9,7 @@ <dom-module id="certificate-delete-confirmation-dialog"> <template> <style include="certificate-shared"></style> - <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <cr-dialog id="dialog" close-text="[[i18n('close')]]"> <div slot="title"> [[getTitleText_(model, certificateType)]] </div> @@ -24,7 +24,7 @@ [[i18n('ok')]] </paper-button> </div> - </dialog> + </cr-dialog> </template> <script src="certificate_delete_confirmation_dialog.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html index 850da130e7f..664a410da38 100644 --- a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html @@ -10,7 +10,7 @@ <dom-module id="certificate-password-decryption-dialog"> <template> <style include="certificate-shared"></style> - <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <cr-dialog id="dialog" close-text="[[i18n('close')]]"> <div slot="title"> [[i18n('certificateManagerDecryptPasswordTitle')]] </div> @@ -28,7 +28,7 @@ [[i18n('ok')]] </paper-button> </div> - </dialog> + </cr-dialog> </template> <script src="certificate_password_decryption_dialog.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html index f17b2825fef..a3ad6fd22d4 100644 --- a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html @@ -14,7 +14,7 @@ margin-bottom: 20px; } </style> - <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <cr-dialog id="dialog" close-text="[[i18n('close')]]"> <div slot="title"> [[i18n('certificateManagerEncryptPasswordTitle')]] </div> @@ -38,7 +38,7 @@ [[i18n('ok')]] </paper-button> </div> - </dialog> + </cr-dialog> </template> <script src="certificate_password_encryption_dialog.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html index 423a18a8e50..96310bc8b8a 100644 --- a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html @@ -32,31 +32,35 @@ [[i18n('certificateManagerUntrusted')]] </div> <div class="name">[[model.name]]</div> - <button is="paper-icon-button-light" class="icon-more-vert" id="dots" - title="[[i18n('moreActions')]]" on-tap="onDotsTap_"></button> - <template is="cr-lazy-render" id="menu"> - <dialog is="cr-action-menu"> - <button slot="item" class="dropdown-item" id="view" - on-tap="onViewTap_"> - [[i18n('certificateManagerView')]] - </button> - <button slot="item" class="dropdown-item" id="edit" - hidden$="[[!canEdit_(certificateType, model)]]" - on-tap="onEditTap_"> - [[i18n('edit')]] - </button> - <button slot="item" class="dropdown-item" id="export" - hidden$="[[!canExport_(certificateType, model)]]" - on-tap="onExportTap_"> - [[i18n('certificateManagerExport')]] - </button> - <button slot="item" class="dropdown-item" id="delete" - hidden$="[[!canDelete_(model)]]" - on-tap="onDeleteTap_"> - [[i18n('certificateManagerDelete')]] - </button> - </dialog> - </template> + <paper-icon-button-light class="icon-more-vert"> + <button id="dots" title="[[i18n('moreActions')]]" on-tap="onDotsTap_"> + </button> + </paper-icon-button-light> + <cr-lazy-render id="menu"> + <template> + <cr-action-menu> + <button slot="item" class="dropdown-item" id="view" + on-tap="onViewTap_"> + [[i18n('certificateManagerView')]] + </button> + <button slot="item" class="dropdown-item" id="edit" + hidden$="[[!canEdit_(certificateType, model)]]" + on-tap="onEditTap_"> + [[i18n('edit')]] + </button> + <button slot="item" class="dropdown-item" id="export" + hidden$="[[!canExport_(certificateType, model)]]" + on-tap="onExportTap_"> + [[i18n('certificateManagerExport')]] + </button> + <button slot="item" class="dropdown-item" id="delete" + hidden$="[[!canDelete_(model)]]" + on-tap="onDeleteTap_"> + [[i18n('certificateManagerDelete')]] + </button> + </cr-action-menu> + </template> + </cr-lazy-render> <div> </template> <script src="certificate_subentry.js"></script> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js index f2bc22e51fc..8467bd7c15f 100644 --- a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js @@ -139,7 +139,7 @@ Polymer({ /** @private */ closePopupMenu_: function() { - this.$$('dialog[is=cr-action-menu]').close(); + this.$$('cr-action-menu').close(); }, /** @private */ diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html index 2c0bb746765..ae1d5c587a9 100644 --- a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html @@ -8,7 +8,7 @@ <dom-module id="certificates-error-dialog"> <template> <style include="certificate-shared"></style> - <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <cr-dialog id="dialog" close-text="[[i18n('close')]]"> <div slot="title">[[model.title]]</div> <div slot="body"> <div>[[model.description]]</div> @@ -23,7 +23,7 @@ [[i18n('ok')]] </paper-button> </div> - </dialog> + </cr-dialog> </template> <script src="certificates_error_dialog.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn new file mode 100644 index 00000000000..45863c8c1cd --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +group("closure_compile") { + deps = [ + ":chromeos_resources", + "network:closure_compile", + "quick_unlock:closure_compile", + ] +} + +js_type_check("chromeos_resources") { + deps = [ + ":bluetooth_dialog", + ] +} + +js_library("bluetooth_dialog") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/iron-resizable-behavior:iron-resizable-behavior-extracted", + "//third_party/polymer/v1_0/components-chromium/paper-input:paper-input-extracted", + "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] + externs_list = [ + "$externs_path/bluetooth.js", + "$externs_path/bluetooth_private.js", + ] + extra_sources = [ + "$interfaces_path/bluetooth_interface.js", + "$interfaces_path/bluetooth_private_interface.js", + ] +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html index a71d029f68a..e42cfe1e2b1 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html +++ b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html @@ -12,8 +12,8 @@ <dom-module id="bluetooth-dialog"> <template> <style include="cr-hidden-style iron-flex"> - dialog { - @apply --bluetooth-dialog-style; + cr-dialog { + --cr-dialog-native: var(--bluetooth-dialog-style); } #pairing { @@ -73,7 +73,7 @@ } </style> <!-- TODO(stevenjb/dschuyler): Find a solution to support i18n{} here --> - <dialog is="cr-dialog" id="dialog" no-cancel="[[noCancel]]" + <cr-dialog id="dialog" no-cancel="[[noCancel]]" close-text="[[i18n('close')]]" on-cancel="onDialogCanceled_" on-closed="onDialogCanceled_"> <div slot="title">[[dialogTitle]]</div> @@ -136,7 +136,7 @@ <paper-button on-tap="close">[[i18n('ok')]]</paper-button> </template> </div> - </dialog> + </cr-dialog> </template> <script src="bluetooth_dialog.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js index b545b3536a9..386e2dae7a5 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js +++ b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js @@ -422,9 +422,9 @@ Polymer({ (pairing == PairingEventType.DISPLAY_PASSKEY || pairing == PairingEventType.KEYS_ENTERED || pairing == PairingEventType.CONFIRM_PASSKEY)) { - var passkeyString = String(this.pairingEvent_.passkey); - if (index < passkeyString.length) - digit = passkeyString[index]; + var passkeyString = + String(this.pairingEvent_.passkey).padStart(this.digits_.length, '0'); + digit = passkeyString[index]; } return digit; }, diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn new file mode 100644 index 00000000000..b64b9364a6a --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":network_apnlist", + ":network_choose_mobile", + ":network_config", + ":network_config_input", + ":network_config_select", + ":network_ip_config", + ":network_nameservers", + ":network_property_list", + ":network_proxy", + ":network_proxy_exclusions", + ":network_proxy_input", + ":network_siminfo", + ] +} + +js_library("network_apnlist") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("network_choose_mobile") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:i18n_behavior", + ] + externs_list = [ "$externs_path/networking_private.js" ] + extra_sources = [ "$interfaces_path/networking_private_interface.js" ] +} + +js_library("network_config") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:i18n_behavior", + ] + externs_list = [ "$externs_path/networking_private.js" ] + extra_sources = [ "$interfaces_path/networking_private_interface.js" ] +} + +js_library("network_config_input") { + deps = [ + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("network_config_select") { + deps = [ + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:i18n_behavior", + ] + externs_list = [ "$externs_path/networking_private.js" ] + extra_sources = [ "$interfaces_path/networking_private_interface.js" ] +} + +js_library("network_ip_config") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("network_nameservers") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("network_property_list") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("network_proxy") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("network_proxy_input") { + deps = [ + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/js:i18n_behavior", + ] +} + +js_library("network_proxy_exclusions") { +} + +js_library("network_siminfo") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/paper-input:paper-input-extracted", + "//ui/webui/resources/cr_elements/chromeos/network:cr_onc_types", + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:i18n_behavior", + "//ui/webui/resources/js/cr/ui:focus_without_ink", + ] + extra_sources = [ "$interfaces_path/networking_private_interface.js" ] +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js index c4e5034e13d..eb882f59f78 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js @@ -82,7 +82,7 @@ Polymer({ }, /** @const */ - DefaultAccessPointName: 'none', + DefaultAccessPointName: 'NONE', /** * Polymer networkProperties changed method. @@ -202,7 +202,12 @@ Polymer({ * @private */ onOtherApnChange_: function(event) { - this.set('otherApn_.' + event.detail.field, event.detail.value); + // TODO(benchan/stevenjb): Move this to shill or + // onc_translator_onc_to_shill.cc. + var value = (event.detail.field == 'AccessPointName') ? + event.detail.value.toUpperCase() : + event.detail.value; + this.set('otherApn_.' + event.detail.field, value); // Don't send a change event for 'Other' until the 'Save' button is tapped. }, 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 32c5b74d9e7..a7f29f7e318 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 @@ -54,10 +54,10 @@ onc-prefix="VPN.Type" disabled="[[hasGuid_(guid)]]"> </network-config-select> <template is="dom-if" if="[[!showVpn_.OpenVPN]]"> - <network-config-input label="[[i18n('OncVPN-Username')]]" + <network-config-input label="[[i18n('OncVPN-L2TP-Username')]]" value="{{configProperties_.VPN.L2TP.Username}}"> </network-config-input> - <network-config-input label="[[i18n('OncVPN-Password')]]" + <network-config-input label="[[i18n('OncVPN-L2TP-Password')]]" value="{{configProperties_.VPN.L2TP.Password}}" password> </network-config-input> <network-config-input label="[[i18n('OncVPN-IPsec-Group')]]" @@ -70,10 +70,10 @@ </template> </template> <template is="dom-if" if="[[showVpn_.OpenVPN]]"> - <network-config-input label="[[i18n('OncVPN-Username')]]" + <network-config-input label="[[i18n('OncVPN-OpenVPN-Username')]]" value="{{configProperties_.VPN.OpenVPN.Username}}"> </network-config-input> - <network-config-input label="[[i18n('OncVPN-Password')]]" + <network-config-input label="[[i18n('OncVPN-OpenVPN-Password')]]" value="{{configProperties_.VPN.OpenVPN.Password}}" password> </network-config-input> <network-config-input label="[[i18n('OncVPN-OpenVPN-OTP')]]" diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html index d23f9ec5c4b..2ed533dd539 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config_input.html @@ -22,7 +22,7 @@ width: var(--network-control-margin); } - #iconDiv button { + #iconDiv paper-icon-button-light { margin: 0; } </style> @@ -36,11 +36,11 @@ </paper-input-container> <div id="iconDiv"> <template is="dom-if" if="[[password]]"> - <button is="paper-icon-button-light" - on-tap="onShowPasswordTap_" - class$="[[getIconClass_(showPassword)]]" - title="[[getShowPasswordTitle_(showPassword)]]"> - </button> + <paper-icon-button-light class$="[[getIconClass_(showPassword)]]"> + <button on-tap="onShowPasswordTap_" + title="[[getShowPasswordTitle_(showPassword)]]"> + </button> + </paper-icon-button-light> </template> </div> </div> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js index 4260815ca7f..b7e07cc1c72 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js @@ -43,15 +43,12 @@ Polymer({ /** * The currently visible IP Config property dictionary. The 'RoutingPrefix' * property is a human-readable mask instead of a prefix length. - * @private {?{ + * @private {{ * ipv4: (!CrOnc.IPConfigUIProperties|undefined), * ipv6: (!CrOnc.IPConfigUIProperties|undefined) - * }} + * }|undefined} */ - ipConfig_: { - type: Object, - value: null, - }, + ipConfig_: Object, /** * Array of properties to pass to the property list. @@ -107,7 +104,7 @@ Polymer({ } this.ipConfig_ = {ipv4: ipv4, ipv6: ipv6}; } else { - this.ipConfig_ = null; + this.ipConfig_ = undefined; } }, diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html index 534ce962de2..dab56078818 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html @@ -29,36 +29,33 @@ filter="[[computeFilter_(prefix, propertyDict, editFieldTypes)]]"> <div class="property-box single-column two-line stretch"> <!-- Property label --> - <div>[[getPropertyLabel_(item, prefix)]]</div> + <div class="layout horizontal center"> + <div>[[getPropertyLabel_(item, prefix)]]</div> + <template is="dom-if" restamp + if="[[isEditTypeAny_(item, editFieldTypes)]]"> + <cr-policy-network-indicator + property="[[getProperty_(item, propertyDict)]]"> + </cr-policy-network-indicator> + </template> + </div> <!-- Uneditable property value --> - <div class="layout horizontal" - hidden="[[isEditable_(item, '', propertyDict, editFieldTypes)]]"> + <template is="dom-if" restamp + if="[[!isEditable_(item, propertyDict, editFieldTypes)]]"> <div class="secondary"> [[getPropertyValue_(item, prefix, propertyDict)]] </div> - <cr-policy-network-indicator - property="[[getProperty_(item, propertyDict)]]"> - </cr-policy-network-indicator> - </div> + </template> <!-- Editable String property value --> - <template is="dom-if" if="[[isEditable_( - item, 'String', propertyDict, editFieldTypes)]]"> + <template is="dom-if" restamp + if="[[isEditTypeInput_(item, propertyDict, editFieldTypes)]]"> <paper-input-container no-label-float> <input id="[[item]]" is="iron-input" slot="input" + type="[[getEditInputType_(item, editFieldTypes)]]" value="[[getPropertyValue_(item, prefix, propertyDict)]]" on-change="onValueChange_"> </paper-input-container> </template> - <!-- Editable Password property value --> - <template is="dom-if" if="[[isEditable_( - item, 'Password', propertyDict, editFieldTypes)]]"> - <paper-input-container no-label-float> - <input id="[[item]]" is="iron-input" type="password" slot="input" - value="[[getPropertyValue_(item, prefix, propertyDict)]]" - on-change="onValueChange_"> - </paper-input-container> - </template> - <!-- TODO(stevenjb): Support other types. --> + <!-- TODO(stevenjb): Support other types (number, boolean)? --> </div> </template> </template> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js index f333386cb78..3362979cf20 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js @@ -34,6 +34,8 @@ Polymer({ * Edit type of editable fields. May contain a property for any field in * |fields|. Other properties will be ignored. Property values can be: * 'String' - A text input will be displayed. + * 'StringArray' - A text input will be displayed that expects a comma + * separated list of strings. * 'Password' - A string with input type = password. * TODO(stevenjb): Support types with custom validation, e.g. IPAddress. * TODO(stevenjb): Support 'Number'. @@ -64,17 +66,17 @@ Polymer({ onValueChange_: function(event) { if (!this.propertyDict) return; - var field = event.target.id; - var curValue = this.get(field, this.propertyDict); - if (typeof curValue == 'object') { + var key = event.target.id; + var curValue = this.get(key, this.propertyDict); + if (typeof curValue == 'object' && !Array.isArray(curValue)) { // Extract the property from an ONC managed dictionary. curValue = CrOnc.getActiveValue( /** @type {!CrOnc.ManagedProperty} */ (curValue)); } - var newValue = event.target.value; + var newValue = this.getValueFromEditField_(key, event.target.value); if (newValue == curValue) return; - this.fire('property-change', {field: field, value: newValue}); + this.fire('property-change', {field: key, value: newValue}); }, /** @@ -119,19 +121,68 @@ Polymer({ /** * @param {string} key The property key. - * @param {string} type The field type. * @param {!Object} propertyDict - * @param {!Object} editFieldTypes * @return {boolean} * @private */ - isEditable_: function(key, type, propertyDict, editFieldTypes) { + isPropertyEditable_: function(key, propertyDict) { var property = /** @type {!CrOnc.ManagedProperty|undefined} */ ( this.get(key, propertyDict)); - if (this.isNetworkPolicyEnforced(property)) + if (property === undefined) { + // Unspecified properties in policy configurations are not user + // modifiable. https://crbug.com/819837. + var source = propertyDict.Source; + return source != 'UserPolicy' && source != 'DevicePolicy'; + } + return !this.isNetworkPolicyEnforced(property); + }, + + /** + * @param {string} key The property key. + * @param {!Object} editFieldTypes + * @return {boolean} + * @private + */ + isEditTypeAny_: function(key, editFieldTypes) { + return editFieldTypes[key] !== undefined; + }, + + /** + * @param {string} key The property key. + * @param {!Object} propertyDict + * @param {!Object} editFieldTypes + * @return {boolean} + * @private + */ + isEditTypeInput_: function(key, propertyDict, editFieldTypes) { + if (!this.isPropertyEditable_(key, propertyDict)) return false; var editType = editFieldTypes[key]; - return editType !== undefined && (type == '' || editType == type); + return editType == 'String' || editType == 'StringArray' || + editType == 'Password'; + }, + + /** + * @param {string} key The property key. + * @param {!Object} editFieldTypes + * @return {string} + * @private + */ + getEditInputType_: function(key, editFieldTypes) { + return editFieldTypes[key] == 'Password' ? 'password' : 'text'; + }, + + /** + * @param {string} key The property key. + * @param {!Object} propertyDict + * @param {!Object} editFieldTypes + * @return {boolean} + * @private + */ + isEditable_: function(key, propertyDict, editFieldTypes) { + if (!this.isPropertyEditable_(key, propertyDict)) + return false; + return this.isEditTypeAny_(key, editFieldTypes); }, /** @@ -141,7 +192,13 @@ Polymer({ * @private */ getProperty_: function(key, propertyDict) { - return this.get(key, propertyDict); + var property = this.get(key, propertyDict); + if (property === undefined && propertyDict.Source) { + // Provide an empty property object with the network policy source. + // See https://crbug.com/819837 for more info. + return {Effective: propertyDict.Source}; + } + return property; }, /** @@ -155,16 +212,20 @@ Polymer({ var value = this.get(key, propertyDict); if (value === undefined) return ''; - if (typeof value == 'object') { + if (typeof value == 'object' && !Array.isArray(value)) { // Extract the property from an ONC managed dictionary value = CrOnc.getActiveValue(/** @type {!CrOnc.ManagedProperty} */ (value)); } + if (Array.isArray(value)) + return value.join(', '); + var customValue = this.getCustomPropertyValue_(key, value); if (customValue) return customValue; if (typeof value == 'number' || typeof value == 'boolean') return value.toString(); + assert(typeof value == 'string'); var valueStr = /** @type {string} */ (value); var oncKey = 'Onc' + prefix + key; @@ -176,6 +237,20 @@ Polymer({ }, /** + * Converts edit field values to the correct edit type. + * @param {string} key The property key. + * @param {*} fieldValue The value from the field. + * @return {*} + * @private + */ + getValueFromEditField_(key, fieldValue) { + var editType = this.editFieldTypes[key]; + if (editType == 'StringArray') + return fieldValue.toString().split(/, */); + return fieldValue; + }, + + /** * @param {string} key The property key. * @param {*} value The property value. * @return {string} The text to display for the property value. If the key 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 7dd7aeb8fe8..400ca41a008 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 @@ -208,7 +208,8 @@ Polymer({ // Set the Web Proxy Auto Discovery URL. var ipv4 = CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4); - this.WPAD_ = (ipv4 && ipv4.WebProxyAutoDiscoveryUrl) || ''; + this.WPAD_ = (ipv4 && ipv4.WebProxyAutoDiscoveryUrl) || + this.i18n('networkProxyWpadNone'); this.setProxyAsync_(proxy); }, diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html index 45101bfbc39..0cff8dd8bec 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html @@ -17,6 +17,10 @@ <dom-module id="network-siminfo"> <template> <style include="network-shared iron-flex"> + :host { + cursor: default + } + iron-icon { -webkit-margin-end: 10px; } @@ -43,6 +47,7 @@ paper-toggle-button { -webkit-margin-start: var(--cr-button-edge-spacing); + @apply --cr-actionable; } </style> @@ -85,7 +90,7 @@ </div> <!-- Enter PIN dialog --> - <dialog is="cr-dialog" id="enterPinDialog" close-text="[[i18n('close')]]" + <cr-dialog id="enterPinDialog" close-text="[[i18n('close')]]" on-cancel="onEnterPinDialogCancel_" on-close="onEnterPinDialogClose_"> <div slot="title">[[i18n('networkSimEnterPinTitle')]]</div> @@ -104,10 +109,10 @@ [[i18n('networkSimEnter')]] </paper-button> </div> - </dialog> + </cr-dialog> <!-- Change PIN dialog --> - <dialog is="cr-dialog" id="changePinDialog" close-text="[[i18n('close')]]" + <cr-dialog id="changePinDialog" close-text="[[i18n('close')]]" on-close="onChangePinDialogClose_"> <div slot="title">[[i18n('networkSimChangePinTitle')]]</div> <div slot="body"> @@ -131,10 +136,10 @@ [[i18n('networkSimChange')]] </paper-button> </div> - </dialog> + </cr-dialog> <!-- Unlock PIN dialog --> - <dialog is="cr-dialog" id="unlockPinDialog" close-text="[[i18n('close')]]" + <cr-dialog id="unlockPinDialog" close-text="[[i18n('close')]]" on-close="onUnlockPinDialogClose_"> <div slot="title">[[i18n('networkSimLockedTitle')]]</div> <div slot="body"> @@ -152,10 +157,10 @@ [[i18n('networkSimUnlock')]] </paper-button> </div> - </dialog> + </cr-dialog> <!-- Unlock PUK dialog --> - <dialog is="cr-dialog" id="unlockPukDialog" close-text="[[i18n('close')]]" + <cr-dialog id="unlockPukDialog" close-text="[[i18n('close')]]" on-close="onUnlockPinDialogClose_"> <div slot="title">[[i18n('networkSimLockedTitle')]]</div> <div slot="body"> @@ -185,7 +190,7 @@ [[i18n('networkSimUnlock')]] </paper-button> </div> - </dialog> + </cr-dialog> </template> <script src="network_siminfo.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js index 668656ec4c4..0a4cfa1fb9d 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js @@ -5,7 +5,6 @@ /** * @fileoverview Polymer element for displaying and modifying cellular sim info. */ -(function() { /** @enum {string} */ var ErrorType = { @@ -17,6 +16,8 @@ var ErrorType = { INVALID_PUK: 'invalid-puk' }; +(function() { + var PIN_MIN_LENGTH = 4; var PUK_MIN_LENGTH = 8; var TOGGLE_DEBOUNCE_MS = 500; 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 new file mode 100644 index 00000000000..c027d4cac4f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn @@ -0,0 +1,19 @@ +# 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("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":pin_keyboard", + ] +} + +js_library("pin_keyboard") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/paper-button:paper-button-extracted", + "//third_party/polymer/v1_0/components-chromium/paper-input:paper-input-extracted", + "//ui/webui/resources/js:i18n_behavior", + ] +} 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 ad554340880..5e069a548b8 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 @@ -56,11 +56,24 @@ margin-bottom: 6px; } - .backspace-button-container { + #backspaceButton { + color: var(--pin-keyboard-backspace-color, #000); + left: 0; + opacity: var(--pin-keyboard-backspace-opacity, --dark-primary-opacity); + padding: 14px; + position: absolute; + top: 0; + } + + #backspaceButton[disabled] { + opacity: 0.34; + } + + #backspaceButtonContainer { position: relative; } - .backspace-button-container paper-ripple { + #backspaceButtonContainer paper-ripple { left: var(--pin-keyboard-backspace-paper-ripple-offset, 0); top: var(--pin-keyboard-backspace-paper-ripple-offset, 0); } @@ -84,19 +97,6 @@ @apply --pin-keyboard-digit-button; } - .digit-button.backspace-button { - color: var(--pin-keyboard-backspace-color, #000); - left: 0; - opacity: var(--pin-keyboard-backspace-opacity, --dark-primary-opacity); - padding: 14px; - position: absolute; - top: 0; - } - - .digit-button.backspace-button:not([has-content]) { - opacity: 0.34; - } - .digit-button inner-text { display: flex; flex-direction: column; @@ -169,7 +169,7 @@ } </style> - <div id="root" on-contextmenu="onContextMenu_"> + <div id="root" on-contextmenu="onContextMenu_" on-tap="focusInput_"> <div id="pinInputDiv" class="row"> <paper-input id="pinInput" type="password" no-label-float value="[[value]]" @@ -247,9 +247,9 @@ <inner-text class="letter">+</inner-text> <paper-ripple> </paper-button> - <div class="backspace-button-container"> - <paper-icon-button class="digit-button backspace-button" - has-content$="[[hasInput_(value)]]" + <div id="backspaceButtonContainer"> + <paper-icon-button id="backspaceButton" class="digit-button" + disabled$="[[!hasInput_(value)]]" icon="pin-keyboard:backspace" on-pointerdown="onBackspacePointerDown_" on-pointerout="clearAndReset_" 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 2ebcaaf4724..8ffadaa42bd 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 @@ -197,6 +197,17 @@ Polymer({ }, /** + * Transfers focus to the input. Called when a non button element on the + * PIN button area is clicked to prevent focus from leaving the input. + */ + focusInput_: function() { + // Focus the input and place the selected region to its exact previous + // location, as this function will not be called by something that will also + // modify the input value. + this.focus(this.selectionStart_, this.selectionEnd_); + }, + + /** * Called when a keypad number has been tapped. * @param {Event} event The event object. * @private @@ -372,7 +383,7 @@ Polymer({ * @private */ hasInput_: function(value) { - return value.length > 0; + return value.length > 0 && this.selectionStart_ > 0; }, /** diff --git a/chromium/ui/webui/resources/cr_elements/BUILD.gn b/chromium/ui/webui/resources/cr_elements/BUILD.gn new file mode 100644 index 00000000000..b87727d5ef5 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +group("closure_compile") { + deps = [ + ":cr_elements_resources", + "chromeos/cr_picture:closure_compile", + "chromeos/network:closure_compile", + "cr_action_menu:closure_compile", + "cr_dialog:closure_compile", + "cr_drawer:closure_compile", + "cr_expand_button:closure_compile", + "cr_link_row:closure_compile", + "cr_profile_avatar_selector:closure_compile", + "cr_toast:closure_compile", + "cr_toggle:closure_compile", + "policy:closure_compile", + ] +} + +js_type_check("cr_elements_resources") { + deps = [ + ":cr_container_shadow_behavior", + ":cr_scrollable_behavior", + ] +} + +js_library("cr_scrollable_behavior") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/iron-list:iron-list-extracted", + ] +} + +js_library("cr_container_shadow_behavior") { +} diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn index e3bae6770d0..cad6349a0aa 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn +++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/BUILD.gn @@ -1,9 +1,19 @@ -# Copyright 2017 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. import("//third_party/closure_compiler/compile_js.gni") +js_type_check("closure_compile") { + deps = [ + ":cr_camera", + ":cr_picture_list", + ":cr_picture_pane", + ":cr_picture_types", + ":cr_png_behavior", + ] +} + js_library("cr_camera") { deps = [ ":cr_png_behavior", @@ -23,6 +33,7 @@ js_library("cr_picture_pane") { deps = [ ":cr_camera", ":cr_picture_types", + ":cr_png_behavior", ] } diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html index 15d10cfbe0d..9a5c63cb177 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html +++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html @@ -147,18 +147,22 @@ <!-- Empty div for even 'space-between' justification --> </div> <div> - <button is="paper-icon-button-light" id="takePhoto" tabindex="1" - title="[[getTakePhotoLabel_(videomode, takePhotoLabel, - captureVideoLabel)]]" on-tap="takePhoto" - disabled="[[!cameraOnline_]]"> - </button> + <paper-icon-button-light> + <button id="takePhoto" tabindex="1" + title="[[getTakePhotoLabel_(videomode, takePhotoLabel, + captureVideoLabel)]]" on-tap="takePhoto" + disabled="[[!cameraOnline_]]"> + </button> + </paper-icon-button-light> </div> <div> - <button is="paper-icon-button-light" id="switchMode" tabindex="2" - title="[[getSwitchModeLabel_(videomode, switchModeToCameraLabel, - switchModeToVideoLabel)]]" on-tap="onTapSwitchMode_" - disabled="[[!cameraOnline_]]" hidden="[[!videoModeEnabled]]"> - </button> + <paper-icon-button-light hidden="[[!videoModeEnabled]]"> + <button id="switchMode" tabindex="2" + title="[[getSwitchModeLabel_(videomode, switchModeToCameraLabel, + switchModeToVideoLabel)]]" on-tap="onTapSwitchMode_" + disabled="[[!cameraOnline_]]"> + </button> + </paper-icon-button-light> </div> </div> </template> diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html index 4abd85b329f..607ac714d98 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html +++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html @@ -67,10 +67,12 @@ <img id="image" alt="[[previewAltText]]" src="[[getImgSrc_(imageUrl)]]" data-show-discard$="[[showDiscard_(imageType)]]"> <div id="discard" hidden="[[!showDiscard_(imageType)]]"> - <button is="paper-icon-button-light" id="discardImage" - class="icon-delete-white" title="[[discardImageLabel]]" - on-tap="onTapDiscardImage_"> - </button> + <paper-icon-button-light class="icon-delete-white"> + <button id="discardImage" + title="[[discardImageLabel]]" + on-tap="onTapDiscardImage_"> + </button> + </paper-icon-button-light> </div> </div> <template is="dom-if" if="[[cameraActive_]]"> diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/BUILD.gn b/chromium/ui/webui/resources/cr_elements/chromeos/network/BUILD.gn new file mode 100644 index 00000000000..78a6ed2b6bf --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":cr_network_icon", + ":cr_network_icon_externs", + ":cr_network_list", + ":cr_network_list_item", + ":cr_network_list_types", + ":cr_network_select", + ":cr_onc_types", + ] +} + +js_library("cr_network_icon") { + deps = [ + ":cr_onc_types", + "//ui/webui/resources/js:assert", + ] +} + +js_library("cr_network_icon_externs") { +} + +js_library("cr_network_list") { + deps = [ + ":cr_network_list_types", + ":cr_onc_types", + "//ui/webui/resources/cr_elements:cr_scrollable_behavior", + ] +} + +js_library("cr_network_list_item") { + deps = [ + ":cr_network_list_types", + ":cr_onc_types", + "//ui/webui/resources/cr_elements/policy:cr_policy_network_behavior", + "//ui/webui/resources/js:assert", + ] +} + +js_library("cr_network_list_types") { + deps = [ + ":cr_onc_types", + ] +} + +js_library("cr_network_select") { + deps = [ + ":cr_network_list_types", + ":cr_onc_types", + "//ui/webui/resources/js:util", + ] + externs_list = [ "$externs_path/networking_private.js" ] +} + +js_library("cr_onc_types") { + deps = [ + "//ui/webui/resources/js:util", + ] + externs_list = [ "$externs_path/networking_private.js" ] +} diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js index 4ac2f10fb5a..0b9d6e6341c 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js +++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_icon.js @@ -158,7 +158,7 @@ Polymer({ networkState.ConnectionState == CrOnc.ConnectionState.NOT_CONNECTED) { return false; } - var security = networkState.WiFi.Security; + var security = CrOnc.getStateOrActiveString(networkState.WiFi.Security); return !!security && security != 'None'; }, }); diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html index 8a15cdad6f3..9af9b3a8367 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html +++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.html @@ -81,10 +81,11 @@ <!-- iron-list captures 'enter' so handle it here explicitly. --> <iron-a11y-keys keys="enter" on-keys-pressed="fireShowDetails_"> </iron-a11y-keys> - <button class="subpage-arrow" is="paper-icon-button-light" - on-tap="fireShowDetails_" tabindex$="[[tabindex]]" - aria-label$="[[ariaLabel]]"> - </button> + <paper-icon-button-light class="subpage-arrow"> + <button on-tap="fireShowDetails_" tabindex$="[[tabindex]]" + aria-label$="[[ariaLabel]]"> + </button> + </paper-icon-button-light> </template> </div> </template> 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 26a37dc478f..5b4f6b7326f 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 @@ -153,28 +153,32 @@ Polymer({ this.ensureCellularNetwork_(networkStates); this.networkStateList_ = networkStates; var defaultNetwork; - if (networkStates.length > 0) { - // Handle an edge case where Ethernet is connecting. - if (networkStates.length > 1 && - networkStates[0].ConnectionState == - CrOnc.ConnectionState.CONNECTING && - networkStates[1].ConnectionState == CrOnc.ConnectionState.CONNECTED) { - defaultNetwork = networkStates[1]; - } else { - defaultNetwork = networkStates[0]; + for (var i = 0; i < networkStates.length; ++i) { + var state = networkStates[i]; + if (state.ConnectionState == CrOnc.ConnectionState.CONNECTED) { + defaultNetwork = state; + break; + } + if (state.ConnectionState == CrOnc.ConnectionState.CONNECTING && + !defaultNetwork) { + defaultNetwork = state; + // Do not break here in case a non WiFi network is connecting but a + // WiFi network is connected. + } else if (state.Type == CrOnc.Type.WI_FI) { + break; // Non connecting or connected WiFI networks are always last. } - } else if (!this.defaultNetworkState_) { - return; // No change } - if (defaultNetwork && this.defaultNetworkState_ && - defaultNetwork.GUID == this.defaultNetworkState_.GUID && - defaultNetwork.ConnectionState == - this.defaultNetworkState_.ConnectionState) { + if ((!defaultNetwork && !this.defaultNetworkState_) || + (defaultNetwork && this.defaultNetworkState_ && + defaultNetwork.GUID == this.defaultNetworkState_.GUID && + defaultNetwork.ConnectionState == + this.defaultNetworkState_.ConnectionState)) { return; // No change to network or ConnectionState } - this.defaultNetworkState_ = + this.defaultNetworkState_ = defaultNetwork ? /** @type {!CrOnc.NetworkStateProperties|undefined} */ ( - Object.assign({}, defaultNetwork)); + Object.assign({}, defaultNetwork)) : + undefined; this.fire('default-network-changed', defaultNetwork); }, diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js index b6671b12291..bab6ac84837 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js +++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_onc_types.js @@ -46,7 +46,7 @@ CrOnc.NetworkStateProperties; /** @typedef {chrome.networkingPrivate.ManagedProperties} */ CrOnc.NetworkProperties; -/** @typedef {string|number|boolean|Object|Array<Object>} */ +/** @typedef {string|number|boolean|Array<string>|Object|Array<Object>} */ CrOnc.NetworkPropertyType; /** @@ -227,7 +227,7 @@ CrOnc.getActiveValue = function(property) { if (property == undefined) return undefined; - if (typeof property != 'object') { + if (typeof property != 'object' || Array.isArray(property)) { console.error( 'getActiveValue called on non object: ' + JSON.stringify(property)); return undefined; diff --git a/chromium/ui/webui/resources/cr_elements/cr_action_menu/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_action_menu/BUILD.gn new file mode 100644 index 00000000000..9ff9ceb0943 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_action_menu/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":cr_action_menu", + ] +} + +js_library("cr_action_menu") { + deps = [ + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:util", + "//ui/webui/resources/js/cr/ui:focus_without_ink", + ] + externs_list = [ "$externs_path/pending.js" ] +} 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 70acc097ff2..ef6cca6b2f3 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 @@ -8,16 +8,18 @@ <dom-module id="cr-action-menu"> <template> <style> - :host { + :host dialog { background-color: white; border: none; box-shadow: 0 2px 6px var(--paper-grey-500); margin: 0; outline: none; padding: 8px 0; + + @apply --cr-action-menu-dialog; } - :host::backdrop { + :host dialog::backdrop { background-color: transparent; } @@ -57,9 +59,11 @@ outline: none; } </style> - <div class="item-wrapper" tabindex="-1" role="menu"> - <slot name="item" id="contentNode"></slot> - </div> + <dialog id="dialog" tabindex="0"> + <div class="item-wrapper" tabindex="-1" role="menu"> + <slot name="item" id="contentNode"></slot> + </div> + </dialog> </template> <script src="cr_action_menu.js"></script> </dom-module> 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 31613726bcc..692a1432a0f 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 @@ -117,7 +117,6 @@ function getDefaultShowConfig() { Polymer({ is: 'cr-action-menu', - extends: 'dialog', /** * The element which the action menu will be anchored to. Also the element @@ -146,10 +145,6 @@ Polymer({ /** @private {?ShowAtPositionConfig} */ lastConfig_: null, - hostAttributes: { - tabindex: 0, - }, - properties: { // Setting this flag will make the menu listen for content size changes and // reposition to its anchor accordingly. @@ -157,6 +152,11 @@ Polymer({ type: Boolean, value: false, }, + + open: { + type: Boolean, + value: false, + }, }, listeners: { @@ -170,6 +170,14 @@ Polymer({ this.removeListeners_(); }, + /** + * Exposing internal <dialog> elements for tests. + * @return {!HTMLDialogElement} + */ + getDialog: function() { + return this.$.dialog; + }, + /** @private */ removeListeners_: function() { window.removeEventListener('resize', this.boundClose_); @@ -214,10 +222,10 @@ Polymer({ if (nextOption) { if (!this.hasMousemoveListener_) { this.hasMousemoveListener_ = true; - listenOnce(this, 'mousemove', function(e) { + listenOnce(this, 'mousemove', e => { this.onMouseover_(e); this.hasMousemoveListener_ = false; - }.bind(this)); + }); } nextOption.focus(); } @@ -243,7 +251,7 @@ Polymer({ } while (this != target); // The user moved the mouse off the options. Reset focus to the dialog. - this.focus(); + this.$.dialog.focus(); }, /** @@ -278,11 +286,11 @@ Polymer({ return nextOption; }, - /** @override */ close: function() { // Removing 'resize' and 'popstate' listeners when dialog is closed. this.removeListeners_(); - HTMLDialogElement.prototype.close.call(this); + this.$.dialog.close(); + this.open = false; if (this.anchorElement_) { cr.ui.focusWithoutInk(assert(this.anchorElement_)); this.anchorElement_ = null; @@ -353,7 +361,8 @@ Polymer({ // and so that the dialog is positioned at the top-start corner of the // document. this.resetStyle_(); - this.showModal(); + this.$.dialog.showModal(); + this.open = true; config.top += scrollTop; config.left += scrollLeft; @@ -375,9 +384,9 @@ Polymer({ /** @private */ resetStyle_: function() { - this.style.left = ''; - this.style.right = ''; - this.style.top = '0'; + this.$.dialog.style.left = ''; + this.$.dialog.style.right = ''; + this.$.dialog.style.top = '0'; }, /** @@ -400,20 +409,22 @@ Polymer({ if (rtl) c.anchorAlignmentX *= -1; + const offsetWidth = this.$.dialog.offsetWidth; var menuLeft = getStartPointWithAnchor( - left, right, this.offsetWidth, c.anchorAlignmentX, c.minX, c.maxX); + left, right, offsetWidth, c.anchorAlignmentX, c.minX, c.maxX); if (rtl) { var menuRight = - document.scrollingElement.clientWidth - menuLeft - this.offsetWidth; - this.style.right = menuRight + 'px'; + document.scrollingElement.clientWidth - menuLeft - offsetWidth; + this.$.dialog.style.right = menuRight + 'px'; } else { - this.style.left = menuLeft + 'px'; + this.$.dialog.style.left = menuLeft + 'px'; } var menuTop = getStartPointWithAnchor( - top, bottom, this.offsetHeight, c.anchorAlignmentY, c.minY, c.maxY); - this.style.top = menuTop + 'px'; + top, bottom, this.$.dialog.offsetHeight, c.anchorAlignmentY, c.minY, + c.maxY); + this.$.dialog.style.top = menuTop + 'px'; }, /** @@ -421,7 +432,7 @@ Polymer({ */ addListeners_: function() { this.boundClose_ = this.boundClose_ || function() { - if (this.open) + if (this.$.dialog.open) this.close(); }.bind(this); window.addEventListener('resize', this.boundClose_); @@ -446,7 +457,7 @@ Polymer({ } }); - this.resizeObserver_.observe(this); + this.resizeObserver_.observe(this.$.dialog); } }, }); diff --git a/chromium/ui/webui/resources/cr_elements/cr_dialog/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_dialog/BUILD.gn new file mode 100644 index 00000000000..2394d60fabb --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_dialog/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_dialog", + ] +} + +js_library("cr_dialog") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/paper-icon-button:paper-icon-button-extracted", + "//ui/webui/resources/js:assert", + ] + externs_list = [ "$externs_path/web_animations.js" ] +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html b/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html index 68cb78ff83f..78c091b52ee 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html +++ b/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.html @@ -9,7 +9,7 @@ <dom-module id="cr-dialog"> <template> <style include="cr-hidden-style cr-icons"> - :host { + dialog { --scroll-border: 1px solid var(--paper-grey-300); border: 0; border-radius: 2px; @@ -21,9 +21,10 @@ padding: 0; top: 50%; width: 512px; + @apply --cr-dialog-native; } - :host([open]) #content-wrapper { + dialog[open] #content-wrapper { /* Keep max-height within viewport, and flex content accordingly. */ display: flex; flex-direction: column; @@ -39,7 +40,7 @@ flex-shrink: 0; } - :host::backdrop { + dialog::backdrop { background-color: rgba(0, 0, 0, 0.6); bottom: 0; left: 0; @@ -108,7 +109,7 @@ outline: none; } - #close { + #closeContainer { --layout-inline: { display: flex; }; @@ -116,40 +117,43 @@ -webkit-margin-end: 4px; align-self: flex-start; margin-top: 4px; - padding: 10px; /* Makes the actual icon 16x16. */ @apply --cr-dialog-close-image; } - #close:hover { + #closeContainer:hover { @apply --cr-dialog-close-image-hover; } - #close:active { + #closeContainer:active { @apply --cr-dialog-close-image-active; } </style> - <!-- This wrapper is necessary, such that the "pulse" animation is not - erroneously played when the user clicks on the outer-most scrollbar. --> - <div id="content-wrapper"> - <div class="top-container"> - <div class="title-container" tabindex="-1"> - <slot name="title"></slot> + <dialog id="dialog"> + <!-- This wrapper is necessary, such that the "pulse" animation is not + erroneously played when the user clicks on the outer-most scrollbar. --> + <div id="content-wrapper"> + <div class="top-container"> + <div class="title-container" tabindex="-1"> + <slot name="title"></slot> + </div> + <paper-icon-button-light id="closeContainer" class="icon-clear" + hidden$="[[noCancel]]"> + <button id="close" aria-label$="[[closeText]]" + on-tap="cancel" on-keypress="onCloseKeypress_"> + </button> + </paper-icon-button-light> </div> - <button is="paper-icon-button-light" class="icon-clear" id="close" - aria-label$="[[closeText]]" hidden$="[[noCancel]]" on-tap="cancel" - on-keypress="onCloseKeypress_"> - </button> - </div> - <slot name="header"></slot> - <div class="body-container"> - <span id="bodyTopMarker"></span> - <slot name="body"></slot> - <span id="bodyBottomMarker"></span> + <slot name="header"></slot> + <div class="body-container"> + <span id="bodyTopMarker"></span> + <slot name="body"></slot> + <span id="bodyBottomMarker"></span> + </div> + <slot name="button-container"></slot> + <slot name="footer"></slot> </div> - <slot name="button-container"></slot> - <slot name="footer"></slot> - </div> + </dialog> </template> <script src="cr_dialog.js"></script> </dom-module> 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 59ee2f6e179..554d846387d 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 @@ -6,16 +6,29 @@ * @fileoverview 'cr-dialog' is a component for showing a modal dialog. If the * dialog is closed via close(), a 'close' event is fired. If the dialog is * canceled via cancel(), a 'cancel' event is fired followed by a 'close' event. - * Additionally clients can inspect the dialog's |returnValue| property inside + * + * Additionally clients can get a reference to the internal native <dialog> via + * calling getNative() and inspecting the |returnValue| property inside * the 'close' event listener to determine whether it was canceled or just * closed, where a truthy value means success, and a falsy value means it was * canceled. + * + * Note that <cr-dialog> wrapper itself always has 0x0 dimensions, and + * specifying width/height on <cr-dialog> directly will have no effect on the + * internal native <dialog>. Instead use the --cr-dialog-native mixin to specify + * width/height (as well as other available mixins to style other parts of the + * dialog contents). */ Polymer({ is: 'cr-dialog', - extends: 'dialog', properties: { + open: { + type: Boolean, + value: false, + reflectToAttribute: true, + }, + /** * Alt-text for the dialog close button. */ @@ -63,7 +76,7 @@ Polymer({ // If the active history entry changes (i.e. user clicks back button), // all open dialogs should be cancelled. window.addEventListener('popstate', function() { - if (!this.ignorePopstate && this.open) + if (!this.ignorePopstate && this.$.dialog.open) this.cancel(); }.bind(this)); @@ -77,7 +90,7 @@ Polymer({ /** @override */ attached: function() { var mutationObserverCallback = function() { - if (this.open) + if (this.$.dialog.open) this.addIntersectionObserver_(); else this.removeIntersectionObserver_(); @@ -85,7 +98,7 @@ Polymer({ this.mutationObserver_ = new MutationObserver(mutationObserverCallback); - this.mutationObserver_.observe(this, { + this.mutationObserver_.observe(this.$.dialog, { attributes: true, attributeFilter: ['open'], }); @@ -146,17 +159,20 @@ Polymer({ } }, + showModal: function() { + this.$.dialog.showModal(); + this.open = this.$.dialog.open; + }, + cancel: function() { this.fire('cancel'); - HTMLDialogElement.prototype.close.call(this, ''); + this.$.dialog.close(); + this.open = this.$.dialog.open; }, - /** - * @param {string=} opt_returnValue - * @override - */ - close: function(opt_returnValue) { - HTMLDialogElement.prototype.close.call(this, 'success'); + close: function() { + this.$.dialog.close('success'); + this.open = this.$.dialog.open; }, /** @@ -169,6 +185,16 @@ Polymer({ e.stopPropagation(); }, + /** + * Expose the inner native <dialog> for some rare cases where it needs to be + * directly accessed (for example to programmatically setheight/width, which + * would not work on the wrapper). + * @return {!HTMLDialogElement} + */ + getNative: function() { + return this.$.dialog; + }, + /** @return {!PaperIconButtonElement} */ getCloseButton: function() { return this.$.close; @@ -210,7 +236,7 @@ Polymer({ if (e.button != 0 || e.composedPath()[0].tagName !== 'DIALOG') return; - this.animate( + this.$.dialog.animate( [ {transform: 'scale(1)', offset: 0}, {transform: 'scale(1.02)', offset: 0.4}, diff --git a/chromium/ui/webui/resources/cr_elements/cr_drawer/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_drawer/BUILD.gn new file mode 100644 index 00000000000..5ca559e9de2 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_drawer/BUILD.gn @@ -0,0 +1,14 @@ +# 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_drawer", + ] +} + +js_library("cr_drawer") { +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html index e2e1cb0529e..78548247952 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html +++ b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.html @@ -1,10 +1,11 @@ <link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <dom-module id="cr-drawer"> <template> <style> - :host { + :host dialog { --drawer-width: 256px; --transition-timing: 200ms ease; background-color: #fff; @@ -20,27 +21,27 @@ width: var(--drawer-width); } - :host, + :host dialog, #container { height: 100%; word-break: break-word; } - :host(.opening) { + :host(.opening) dialog { left: 0; } - :host([align=rtl]) { + :host([align=rtl]) dialog { left: auto; right: calc(-1 * var(--drawer-width)); transition: right var(--transition-timing); } - :host(.opening[align=rtl]) { + :host(.opening[align=rtl]) dialog { right: 0; } - :host::backdrop { + :host dialog::backdrop { background: rgba(0, 0, 0, 0.5); bottom: 0; left: 0; @@ -51,7 +52,7 @@ transition: opacity var(--transition-timing); } - :host(.opening)::backdrop { + :host(.opening) dialog::backdrop { opacity: 1; } @@ -70,10 +71,12 @@ overflow: auto; } </style> - <div id="container" on-tap="onContainerTap_"> - <div class="drawer-header" tabindex="-1">[[heading]]</div> - <slot></slot> - </div> + <dialog id="dialog" on-cancel="onDialogCancel_" on-tap="onDialogTap_"> + <div id="container" on-tap="onContainerTap_"> + <div class="drawer-header" tabindex="-1">[[heading]]</div> + <slot></slot> + </div> + </dialog> </template> </dom-module> <script src="cr_drawer.js"></script> diff --git a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js index 44b5180a7b5..cb06e3576ab 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js +++ b/chromium/ui/webui/resources/cr_elements/cr_drawer/cr_drawer.js @@ -4,14 +4,13 @@ Polymer({ is: 'cr-drawer', - extends: 'dialog', properties: { heading: String, - /** Enables notifications for |Dialog.open|. */ open: { type: Boolean, + // Enables 'open-changed' events. notify: true, }, @@ -24,14 +23,12 @@ Polymer({ }, listeners: { - 'cancel': 'onDialogCancel_', - 'tap': 'onDialogTap_', 'transitionend': 'onDialogTransitionEnd_', }, /** Toggles the drawer open and close. */ toggle: function() { - if (this.open) + if (this.$.dialog.open) this.closeDrawer(); else this.openDrawer(); @@ -39,15 +36,16 @@ Polymer({ /** Shows drawer and slides it into view. */ openDrawer: function() { - if (!this.open) { - this.showModal(); + if (!this.$.dialog.open) { + this.$.dialog.showModal(); + this.open = true; this.classList.add('opening'); } }, /** Slides the drawer away, then closes it after the transition has ended. */ closeDrawer: function() { - if (this.open) { + if (this.$.dialog.open) { this.classList.remove('opening'); this.classList.add('closing'); } @@ -88,7 +86,8 @@ Polymer({ onDialogTransitionEnd_: function() { if (this.classList.contains('closing')) { this.classList.remove('closing'); - this.close(); + this.$.dialog.close(); + this.open = false; } }, }); diff --git a/chromium/ui/webui/resources/cr_elements/cr_expand_button/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_expand_button/BUILD.gn new file mode 100644 index 00000000000..14021b10ab6 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_expand_button/BUILD.gn @@ -0,0 +1,14 @@ +# 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_expand_button", + ] +} + +js_library("cr_expand_button") { +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html b/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html index e31345821ed..56fccded4dc 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html +++ b/chromium/ui/webui/resources/cr_elements/cr_expand_button/cr_expand_button.html @@ -7,10 +7,11 @@ <template> <style include="cr-icons cr-shared-style"> :host([disabled]) { + opacity: 0.65; pointer-events: none; } - button[is=paper-icon-button-light] { + paper-icon-button-light { @apply --cr-paper-icon-button-margin; } @@ -25,11 +26,12 @@ </style> <div id="outer" on-tap="toggleExpand_"> <div id="label" on-tap="toggleExpand_"><slot></slot></div> - <button is="paper-icon-button-light" class$="[[iconName_(expanded)]]" - toggles active="{{expanded}}" disabled="[[disabled]]" - aria-label$="[[alt]]" aria-pressed$="[[getAriaPressed_(expanded)]]" - tabindex$="[[tabIndex]]"> - </button> + <paper-icon-button-light class$="[[iconName_(expanded)]]"> + <button toggles active="{{expanded}}" disabled="[[disabled]]" + aria-label$="[[alt]]" aria-pressed$="[[getAriaPressed_(expanded)]]" + tabindex$="[[tabIndex]]"> + </button> + </paper-icon-button-light> </div> </template> <script src="cr_expand_button.js"></script> diff --git a/chromium/ui/webui/resources/cr_elements/cr_icons_css.html b/chromium/ui/webui/resources/cr_elements/cr_icons_css.html index f8dc56c8a7f..0db9b898093 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_icons_css.html +++ b/chromium/ui/webui/resources/cr_elements/cr_icons_css.html @@ -4,12 +4,16 @@ <dom-module id="cr-icons"> <template> <style> - :host-context([dir=rtl]) :-webkit-any( - button[is='paper-icon-button-light'], .cr-icon) { + :host-context([dir=rtl]) :-webkit-any(paper-icon-button-light, .cr-icon) { transform: scaleX(-1); /* Invert X: flip on the Y axis (aka mirror). */ } - button[is='paper-icon-button-light'], + paper-icon-button-light paper-ripple { + color: currentColor; + opacity: 0.6; + } + + paper-icon-button-light, .cr-icon { @apply --cr-paper-icon-button-margin; background-position: center; @@ -21,99 +25,80 @@ width: var(--cr-icon-ripple-size); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).no-overlap { + :-webkit-any(paper-icon-button-light, .cr-icon).no-overlap { margin-left: 0; margin-right: 0; } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-arrow-back { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-arrow-back { background-image: url(../images/icon_arrow_back.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-cancel { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-cancel { background-image: url(../images/icon_cancel.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-cancel-toolbar { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-cancel-toolbar { background-image: url(../images/icon_cancel_toolbar.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-clear { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-clear { background-image: url(../images/icon_clear.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-delete-gray { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-delete-gray { background-image: url(../images/icon_delete_gray.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-delete-white { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-delete-white { background-image: url(../images/icon_delete_white.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-expand-less { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-expand-less { background-image: url(../images/icon_expand_less.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-expand-more { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-expand-more { background-image: url(../images/icon_expand_more.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-external { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-external { background-image: url(../images/open_in_new.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-menu-white { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-menu-white { background-image: url(../images/icon_menu_white.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-more-vert { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-more-vert { background-image: url(../images/icon_more_vert.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-refresh { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-refresh { background-image: url(../images/icon_refresh.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-settings { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-settings { background-image: url(../images/icon_settings.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-search { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-search { background-image: url(../images/icon_search.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-arrow-dropdown { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-arrow-dropdown { background-image: url(../images/icon_arrow_dropdown.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).subpage-arrow { + :-webkit-any(paper-icon-button-light, .cr-icon).subpage-arrow { background-image: url(../images/arrow_right.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-visibility { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-visibility { background-image: url(../images/icon_visibility.svg); } - :-webkit-any(button[is='paper-icon-button-light'], - .cr-icon).icon-visibility-off { + :-webkit-any(paper-icon-button-light, .cr-icon).icon-visibility-off { background-image: url(../images/icon_visibility_off.svg); } </style> diff --git a/chromium/ui/webui/resources/cr_elements/cr_lazy_render/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_lazy_render/BUILD.gn new file mode 100644 index 00000000000..e681565722f --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_lazy_render/BUILD.gn @@ -0,0 +1,14 @@ +# 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_lazy_render", + ] +} + +js_library("cr_lazy_render") { +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html b/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html index 9dffa4c9557..be956c79fa7 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html +++ b/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.html @@ -1,3 +1,8 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<script src="cr_lazy_render.js"></script> +<dom-module id="cr-lazy-render"> + <template> + <slot></slot> + </template> + <script src="cr_lazy_render.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js b/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js index 8fac4f477e2..48fd9acb414 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js +++ b/chromium/ui/webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js @@ -7,16 +7,20 @@ * cr-lazy-render is a simple variant of dom-if designed for lazy rendering * of elements that are accessed imperatively. * Usage: - * <template is="cr-lazy-render" id="menu"> - * <heavy-menu></heavy-menu> - * </template> + * <cr-lazy-render id="menu"> + * <template> + * <heavy-menu></heavy-menu> + * </template> + * </cr-lazy-render> * * this.$.menu.get().show(); + * + * TODO(calamity): Use Polymer.Templatize instead of inheriting the + * Polymer.Templatizer behavior once Polymer 2.0 is available. */ Polymer({ is: 'cr-lazy-render', - extends: 'template', behaviors: [Polymer.Templatizer], @@ -43,8 +47,9 @@ Polymer({ /** @private */ render_: function() { - if (!this.ctor) - this.templatize(this); + var template = this.getContentChildren()[0]; + if (!template.ctor) + this.templatize(template); var parentNode = this.parentNode; if (parentNode && !this.child_) { var instance = this.stamp({}); diff --git a/chromium/ui/webui/resources/cr_elements/cr_link_row/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_link_row/BUILD.gn new file mode 100644 index 00000000000..d487f4132e0 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":cr_link_row", + ] +} + +js_library("cr_link_row") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/iron-behaviors:iron-button-state-extracted", + "//third_party/polymer/v1_0/components-chromium/paper-behaviors:paper-ripple-behavior-extracted", + ] +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html index 97bb3cc66a7..f1beac343f1 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html +++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html @@ -12,12 +12,16 @@ :host { align-items: center; align-self: stretch; + display: block; + flex: 1; + } + + :host > button { background: none; border: none; color: inherit; cursor: pointer; display: flex; - flex: 1; font-family: inherit; font-size: 100%; /* Specifically for Mac OSX, harmless elsewhere. */ line-height: 154%; /* 20px. */ @@ -25,6 +29,7 @@ min-height: var(--cr-section-min-height); outline: none; padding: 0 var(--cr-section-padding); + width: 100%; } :host([disabled]) { @@ -70,15 +75,17 @@ font-weight: 400; } </style> - <div id="outer" noSubLabel$="[[!subLabel]]"> - <div id="labelWrapper" hidden="[[!label]]"> - <div id="label" class="label">[[label]]</div> - <div id="subLabel" class="secondary label">[[subLabel]]</div> - </div> - <slot></slot> - <div id="icon" class$="cr-icon [[iconClass]]" aria-hidden="true"> + <button disabled="[[disabled]]"> + <div id="outer" noSubLabel$="[[!subLabel]]"> + <div id="labelWrapper" hidden="[[!label]]"> + <div id="label" class="label">[[label]]</div> + <div id="subLabel" class="secondary label">[[subLabel]]</div> + </div> + <slot></slot> + <div id="icon" class$="cr-icon [[iconClass]]" aria-hidden="true"> + </div> </div> - </div> + </button> </template> <script src="cr_link_row.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js index c8f16a67b1d..b94008121cb 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js +++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js @@ -12,7 +12,6 @@ */ Polymer({ is: 'cr-link-row', - extends: 'button', behaviors: [Polymer.PaperRippleBehavior], @@ -26,6 +25,11 @@ Polymer({ /* Value used for noSubLabel attribute. */ value: '', }, + + disabled: { + type: Boolean, + reflectToAttribute: true, + }, }, listeners: { diff --git a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/BUILD.gn new file mode 100644 index 00000000000..d86f6b5d8dd --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/BUILD.gn @@ -0,0 +1,26 @@ +# 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_profile_avatar_selector", + ":cr_profile_avatar_selector_grid", + ] +} + +js_library("cr_profile_avatar_selector") { + deps = [ + ":cr_profile_avatar_selector_grid", + "//ui/webui/resources/js:icon", + ] +} + +js_library("cr_profile_avatar_selector_grid") { + deps = [ + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:util", + ] +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html index bc13ad1d5d6..3a075c1e755 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html +++ b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html @@ -43,7 +43,8 @@ <cr-profile-avatar-selector-grid id="avatar-grid" ignore-modified-key-events="[[ignoreModifiedKeyEvents]]"> <template is="dom-repeat" items="[[avatars]]"> - <paper-button class="avatar" title="[[item.label]]" + <paper-button class$="avatar [[getSelectedClass_(item.selected)]]" + title="[[item.label]]" style$="background-image: [[getIconImageSet_(item.url)]]" on-tap="onAvatarTap_"> </paper-button> diff --git a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js index 2716e934e6a..ea4098ddd68 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js +++ b/chromium/ui/webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js @@ -10,7 +10,8 @@ /** * @typedef {{url: string, * label: string, - * isGaiaAvatar: (boolean|undefined)}} + * isGaiaAvatar: (boolean|undefined), + * selected: (boolean|undefined)}} */ let AvatarIcon; @@ -47,6 +48,13 @@ Polymer({ }, }, + /** @private */ + getSelectedClass_: function(isSelected) { + // TODO(dpapad): Rename 'iron-selected' to 'selected' now that this CSS + // class is not assigned by any iron-* behavior. + return isSelected ? 'iron-selected' : ''; + }, + /** * @param {string} iconUrl * @return {string} A CSS image-set for multiple scale factors. @@ -61,13 +69,14 @@ Polymer({ * @private */ onAvatarTap_: function(e) { - // TODO(dpapad): Rename 'iron-selected' to 'selected' now that this CSS - // class is not assigned by any iron-* behavior. + // Manual selection for profile creation if (this.selectedAvatarElement_) this.selectedAvatarElement_.classList.remove('iron-selected'); - this.selectedAvatarElement_ = /** @type {!HTMLElement} */ (e.target); this.selectedAvatarElement_.classList.add('iron-selected'); + + // |selectedAvatar| is set to pass back selection to the owner of this + // component. this.selectedAvatar = /** @type {!{model: {item: !AvatarIcon}}} */ (e).model.item; }, diff --git a/chromium/ui/webui/resources/cr_elements/cr_search_field/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_search_field/BUILD.gn new file mode 100644 index 00000000000..481c4e60be4 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_search_field/BUILD.gn @@ -0,0 +1,17 @@ +# 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_search_field_behavior", + ] +} + +js_library("cr_search_field_behavior") { + deps = [ + "//ui/webui/resources/js:assert", + ] +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_toast/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_toast/BUILD.gn new file mode 100644 index 00000000000..1d1fcf225d0 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_toast/BUILD.gn @@ -0,0 +1,14 @@ +# 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_toast", + ] +} + +js_library("cr_toast") { +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.html b/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.html index f0aa56a5972..91346b54060 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.html +++ b/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.html @@ -4,33 +4,31 @@ <template> <style> :host { - align-items: var(--cr-toast-align-items, center); - background-color: var(--cr-toast-background-color, #323232); - border-radius: var(--cr-toast-border-radius, 4px); - bottom: var(--cr-toast-bottom, 0); - box-shadow: var(--cr-toast-box-shadow, 0 2px 4px 0 rgba(0, 0, 0, 0.28)); - box-sizing: var(--cr-toast-box-sizing, border-box); - display: var(--cr-toast-display, flex); - margin: var(--cr-toast-margin, 24px); - max-width: var(--cr-toast-max-width, 568px); - min-height: var(--cr-toast-min-height, 52px); - min-width: var(--cr-toast-min-width, 288px); - opacity: var(--cr-toast-opacity-closed, 0); - padding: var(--cr-toast-padding, 0 24px); - position: var(--cr-toast-position, fixed); - transform: var(--cr-toast-transform-closed, translateY(100px)); - transition: - opacity var(--cr-toast-transition-time-opacity, 300ms), - transform var(--cr-toast-transition-time-transform, 300ms), - visibility var(--cr-toast-transition-time-visibility, 300ms); - visibility: var(--cr-toast-visibility-closed, hidden); - white-space: var(--cr-toast-white-space, nowrap); + align-items: center; + background-color: #323232; + border-radius: 4px; + bottom: 0; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.28); + box-sizing: border-box; + display: flex; + margin: 24px; + max-width: 568px; + min-height: 52px; + min-width: 288px; + opacity: 0; + padding: 0 24px; + position: fixed; + transform: translateY(100px); + transition: opacity 300ms, transform 300ms, visibility 300ms; + visibility: hidden; + white-space: nowrap; + z-index: 1; } :host([open]) { - opacity: var(--cr-toast-opacity-open, 1); - transform: var(--cr-toast-transform-open, translateY(0)); - visibility: var(--cr-toast-visibility-open, visible); + opacity: 1; + transform: translateY(0); + visibility: visible; } </style> <slot></slot> diff --git a/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.js b/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.js index bf26149880a..a2bd8d7d24c 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.js +++ b/chromium/ui/webui/resources/cr_elements/cr_toast/cr_toast.js @@ -20,24 +20,63 @@ Polymer({ }, }, + observers: ['resetAutoHide_(duration, open)'], + /** @private {number|null} */ hideTimeoutId_: null, - show: function() { - this.open = true; + /** + * Cancels existing auto-hide, and sets up new auto-hide. + * @private + */ + resetAutoHide_: function() { + if (this.hideTimeoutId_ != null) { + window.clearTimeout(this.hideTimeoutId_); + this.hideTimeoutId_ = null; + } + + if (this.open && this.duration != 0) { + this.hideTimeoutId_ = window.setTimeout(() => { + this.open = false; + }, this.duration); + } + }, - if (!this.duration) - return; + /** + * Shows the toast and auto-hides after |this.duration| milliseconds has + * passed. If the toast is currently being shown, any preexisting auto-hide + * is cancelled and replaced with a new auto-hide. + * + * If |this.duration| is set to 0, the toast will remain open indefinitely. + * The caller is responsible for hiding the toast. + * + * When |duration| is passed in the non-negative number, |this.duration| + * is updated to that value. + * @param {number=} duration + */ + show: function(duration) { + // |this.resetAutoHide_| is called whenever |this.duration| or |this.open| + // is changed. If neither is changed, we will still need to reset auto-hide. + let shouldResetAutoHide = true; - if (this.hideTimeoutId_ != null) - window.clearTimeout(this.hideTimeoutId_); + if (typeof(duration) != 'undefined' && duration >= 0 && + this.duration != duration) { + this.duration = duration; + shouldResetAutoHide = false; + } - this.hideTimeoutId_ = window.setTimeout(() => { - this.hide(); - this.hideTimeoutId_ = null; - }, this.duration); + if (!this.open) { + this.open = true; + shouldResetAutoHide = false; + } + + if (shouldResetAutoHide) + this.resetAutoHide_(); }, + /** + * Hides the toast. + */ hide: function() { this.open = false; }, diff --git a/chromium/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_toggle/BUILD.gn new file mode 100644 index 00000000000..f484f7463d3 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_toggle/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_toggle", + ] +} + +js_library("cr_toggle") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/iron-behaviors:iron-button-state-extracted", + "//third_party/polymer/v1_0/components-chromium/paper-behaviors:paper-ripple-behavior-extracted", + ] + externs_list = [ "$externs_path/pending.js" ] +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js b/chromium/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js index 4cf3d472c74..d4d38a789a0 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js +++ b/chromium/ui/webui/resources/cr_elements/cr_toggle/cr_toggle.js @@ -5,7 +5,7 @@ /** * @fileoverview 'cr-toggle' is a component for showing an on/off switch. It * fires a 'change' event *only* when its state changes as a result of a user - * interaction. Besides just tapping the element, its state can be changed by + * interaction. Besides just clicking the element, its state can be changed by * dragging (pointerdown+pointermove) the element towards the desired direction. */ Polymer({ @@ -40,7 +40,7 @@ Polymer({ listeners: { 'pointerdown': 'onPointerDown_', 'pointerup': 'onPointerUp_', - 'tap': 'onTap_', + 'click': 'onTap_', 'keypress': 'onKeyPress_', 'focus': 'onFocus_', 'blur': 'onBlur_', @@ -58,7 +58,7 @@ Polymer({ /** * Whether the state of the toggle has already taken into account by - * |pointeremove| handlers. Used in the 'tap' handler. + * |pointeremove| handlers. Used in the 'click' handler. * @private {boolean} */ handledInPointerMove_: false, @@ -136,28 +136,29 @@ Polymer({ /** @private */ onTap_: function(e) { - // Prevent |tap| event from bubbling. It can cause parents of this elements - // to erroneously re-toggle this control. + // Prevent |click| event from bubbling. It can cause parents of this + // elements to erroneously re-toggle this control. e.stopPropagation(); + e.preventDefault(); // User gesture has already been taken care of inside |pointermove| // handlers, Do nothing here. if (this.handledInPointerMove_) return; - // If no pointermove event fired, then user just tapped on the + // If no pointermove event fired, then user just clicked on the // toggle button and therefore it should be toggled. this.toggleState_(false); }, /** - * Whether the host of this element should handle a 'tap' event it received, - * to be used when tapping on the parent is supposed to toggle the cr-toggle. + * Whether the host of this element should handle a 'click' event it received, + * to be used when clicking on the parent is supposed to toggle the cr-toggle. * * This is necessary to avoid a corner case when pointerdown is initiated * in cr-toggle, but pointerup happens outside the bounds of cr-toggle, which - * ends up firing a 'tap' event on the parent (see context at crbug.com/689158 - * and crbug.com/768555). + * ends up firing a 'click' event on the parent (see context at + * crbug.com/689158 and crbug.com/768555). * @param {!Event} e * @return {boolean} */ diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_toolbar/BUILD.gn new file mode 100644 index 00000000000..6cefa20d292 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":cr_toolbar", + ":cr_toolbar_search_field", + ":cr_toolbar_selection_overlay", + ] +} + +js_library("cr_toolbar_search_field") { + deps = [ + "//ui/webui/resources/cr_elements/cr_search_field:cr_search_field_behavior", + ] +} + +js_library("cr_toolbar_selection_overlay") { + deps = [ + "//third_party/polymer/v1_0/components-chromium/paper-button:paper-button-extracted", + ] +} + +js_library("cr_toolbar") { + deps = [ + ":cr_toolbar_search_field", + ] + externs_list = [ "$externs_path/web_animations.js" ] +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html index 55caed47dda..f1467416180 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html +++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar.html @@ -40,12 +40,10 @@ display: flex; } - #menuButton { + #menuButtonContainer { height: 32px; - margin-bottom: 6px; - margin-top: 6px; min-width: 32px; - padding: 6px; + padding: 6px 0; width: 32px; } @@ -130,12 +128,14 @@ <div id="leftSpacer"> <!-- Note: showing #menuPromo relies on this dom-if being [restamp]. --> <template is="dom-if" if="[[showMenu]]" restamp> - <button id="menuButton" is="paper-icon-button-light" - class="icon-menu-white no-overlap" - on-tap="onMenuTap_" - title="[[titleIfNotShowMenuPromo_(menuLabel, showMenuPromo)]]" - aria-label$="[[menuLabel]]"> - </button> + <paper-icon-button-light id="menuButtonContainer" + class="icon-menu-white no-overlap"> + <button id="menuButton" + on-tap="onMenuTap_" + title="[[titleIfNotShowMenuPromo_(menuLabel, showMenuPromo)]]" + aria-label$="[[menuLabel]]"> + </button> + </paper-icon-button-light> <template is="dom-if" if="[[showMenuPromo]]"> <div id="menuPromo" role="tooltip"> [[menuPromo]] 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 063e1fd2902..71f389bac92 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 @@ -22,7 +22,7 @@ display: none !important; } - button[is='paper-icon-button-light'], + paper-icon-button-light, paper-icon-button { height: 32px; margin: 6px; @@ -153,9 +153,10 @@ autofocus> </div> <template is="dom-if" if="[[hasSearchText]]"> - <button is="paper-icon-button-light" class="icon-cancel-toolbar" - id="clearSearch" title="[[clearLabel]]" on-tap="clearSearch_"> - </button> + <paper-icon-button-light class="icon-cancel-toolbar"> + <button id="clearSearch" title="[[clearLabel]]" on-tap="clearSearch_"> + </button> + </paper-icon-button-light> </template> </template> <script src="cr_toolbar_search_field.js"></script> diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html index 3138f473a77..28d494e8b2d 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html +++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_selection_overlay.html @@ -1,11 +1,12 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <dom-module id="cr-toolbar-selection-overlay"> <template> - <style> + <style include="cr-icons"> :host { -webkit-padding-start: var(--cr-toolbar-field-margin, 0); display: flex; @@ -35,7 +36,7 @@ flex: 1; } - button { + paper-icon-button-light { -webkit-margin-end: 24px; -webkit-margin-start: 2px; height: 36px; @@ -48,11 +49,11 @@ } </style> <div id="overlay-content"> - <button is="paper-icon-button-light" - on-tap="onClearSelectionTap_" - title="[[cancelLabel]]"> - <iron-icon icon="cr:clear"></iron-icon> - </button> + <paper-icon-button-light> + <button on-tap="onClearSelectionTap_" title="[[cancelLabel]]"> + <iron-icon icon="cr:clear"></iron-icon> + </button> + </paper-icon-button-light> <div id="number-selected">[[selectionLabel]]</div> <paper-button on-tap="onClearSelectionTap_"> [[cancelLabel]] diff --git a/chromium/ui/webui/resources/cr_elements/icons.html b/chromium/ui/webui/resources/cr_elements/icons.html index ccff3013f93..b55baf0f48f 100644 --- a/chromium/ui/webui/resources/cr_elements/icons.html +++ b/chromium/ui/webui/resources/cr_elements/icons.html @@ -51,6 +51,7 @@ blurry at 20 px). Please use 20 px icons when available. <g id="person"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path></g> <g id="print"><path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"></path></g> <g id="search"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path></g> + <g id="security"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"></path></g> <if expr="chromeos"> <g id="sim-card-alert"><path d="M18 2h-8L4.02 8 4 20c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5 15h-2v-2h2v2zm0-4h-2V8h2v5z"></path></g> <g id="sim-lock"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"></path></g> diff --git a/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn b/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn new file mode 100644 index 00000000000..244f3a615eb --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":cr_policy_indicator", + ":cr_policy_indicator_behavior", + ":cr_policy_network_behavior", + ":cr_policy_network_indicator", + ":cr_policy_pref_behavior", + ":cr_policy_pref_indicator", + ":cr_tooltip_icon", + ] +} + +js_library("cr_policy_indicator") { + deps = [ + ":cr_policy_indicator_behavior", + "//ui/webui/resources/js:assert", + ] +} + +js_library("cr_policy_indicator_behavior") { + deps = [ + "//ui/webui/resources/js:assert", + ] +} + +js_library("cr_policy_pref_behavior") { + deps = [ + ":cr_policy_indicator_behavior", + ] + externs_list = [ "$externs_path/settings_private.js" ] +} + +js_library("cr_policy_pref_indicator") { + deps = [ + ":cr_policy_indicator_behavior", + ] + externs_list = [ "$externs_path/settings_private.js" ] +} + +js_library("cr_policy_network_behavior") { + deps = [ + ":cr_policy_indicator_behavior", + "../chromeos/network:cr_onc_types", + ] +} + +js_library("cr_policy_network_indicator") { + deps = [ + ":cr_policy_indicator_behavior", + ":cr_policy_network_behavior", + "../chromeos/network:cr_onc_types", + ] +} + +js_library("cr_tooltip_icon") { +} diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js index e8b2bba0d98..5c3ad39d71f 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.js @@ -13,6 +13,7 @@ * Chrome OS only strings may be undefined. * @type {{ * controlledSettingExtension: string, + * controlledSettingExtensionWithoutName: string, * controlledSettingPolicy: string, * controlledSettingRecommendedMatches: string, * controlledSettingRecommendedDiffers: string, @@ -120,7 +121,9 @@ var CrPolicyIndicatorBehavior = { return ''; // Tooltips may not be defined, e.g. in OOBE. switch (type) { case CrPolicyIndicatorType.EXTENSION: - return CrPolicyStrings.controlledSettingExtension.replace('$1', name); + return name.length > 0 ? + CrPolicyStrings.controlledSettingExtension.replace('$1', name) : + CrPolicyStrings.controlledSettingExtensionWithoutName; case CrPolicyIndicatorType.PRIMARY_USER: return CrPolicyStrings.controlledSettingShared.replace('$1', name); case CrPolicyIndicatorType.OWNER: diff --git a/chromium/ui/webui/resources/images/fingerprint_failed.svg b/chromium/ui/webui/resources/images/icon_error_outline.svg index 314ee0c6686..314ee0c6686 100644 --- a/chromium/ui/webui/resources/images/fingerprint_failed.svg +++ b/chromium/ui/webui/resources/images/icon_error_outline.svg diff --git a/chromium/ui/webui/resources/js/BUILD.gn b/chromium/ui/webui/resources/js/BUILD.gn index 75cf392f0b3..b9d148cc795 100644 --- a/chromium/ui/webui/resources/js/BUILD.gn +++ b/chromium/ui/webui/resources/js/BUILD.gn @@ -1,9 +1,37 @@ -# Copyright 2017 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. import("//third_party/closure_compiler/compile_js.gni") +group("closure_compile") { + deps = [ + ":js_resources", + "cr:closure_compile", + "cr/ui:closure_compile", + ] +} + +js_type_check("js_resources") { + deps = [ + ":action_link", + ":assert", + ":cr", + ":event_tracker", + ":i18n_behavior", + ":i18n_template", + ":i18n_template_no_process", + ":icon", + ":load_time_data", + ":parse_html_subset", + ":promise_resolver", + ":search_highlight_utils", + ":util", + ":web_ui_listener_behavior", + ":webui_listener_tracker", + ] +} + js_library("action_link") { } @@ -27,6 +55,12 @@ js_library("webui_listener_tracker") { ] } +js_library("search_highlight_utils") { + deps = [ + ":cr", + ] +} + js_library("icon") { deps = [ ":cr", @@ -49,7 +83,6 @@ js_library("i18n_template") { js_library("i18n_behavior") { deps = [ ":load_time_data", - ":parse_html_subset", ] } diff --git a/chromium/ui/webui/resources/js/cr/BUILD.gn b/chromium/ui/webui/resources/js/cr/BUILD.gn new file mode 100644 index 00000000000..f78507863f2 --- /dev/null +++ b/chromium/ui/webui/resources/js/cr/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":event_target", + ":ui", + ] +} + +js_library("event_target") { + deps = [ + "..:cr", + ] +} + +js_library("ui") { + deps = [ + "..:cr", + ] +} diff --git a/chromium/ui/webui/resources/js/cr/ui/BUILD.gn b/chromium/ui/webui/resources/js/cr/ui/BUILD.gn new file mode 100644 index 00000000000..70393b3b5d2 --- /dev/null +++ b/chromium/ui/webui/resources/js/cr/ui/BUILD.gn @@ -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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":alert_overlay", + ":array_data_model", + ":autocomplete_list", + ":command", + ":context_menu_button", + ":context_menu_handler", + ":dialogs", + ":drag_wrapper", + ":focus_grid", + ":focus_manager", + ":focus_outline_manager", + ":focus_row", + ":focus_without_ink", + ":grid", + ":list", + ":list_item", + ":list_selection_controller", + ":list_selection_model", + ":list_single_selection_model", + ":menu", + ":menu_button", + ":menu_item", + ":node_utils", + ":overlay", + ":position_util", + ":splitter", + ":table", + ":tree", + ] +} + +js_library("alert_overlay") { + deps = [ + "../..:cr", + "../..:util", + ] +} + +js_library("array_data_model") { + deps = [ + "..:event_target", + "../..:cr", + ] +} + +js_library("autocomplete_list") { + deps = [ + ":list", + ":list_single_selection_model", + ":position_util", + ] +} + +js_library("command") { + deps = [ + "..:ui", + "../..:cr", + ] +} + +js_library("context_menu_button") { + deps = [ + ":menu_button", + ] +} + +js_library("context_menu_handler") { + deps = [ + ":menu", + ":menu_button", + ":position_util", + "..:event_target", + "..:ui", + "../..:cr", + ] +} + +js_library("dialogs") { + deps = [ + "../..:cr", + ] +} + +js_library("drag_wrapper") { + deps = [ + "../..:assert", + "../..:cr", + ] +} + +js_library("focus_grid") { + deps = [ + ":focus_row", + "../..:assert", + "../..:cr", + "../..:event_tracker", + ] +} + +js_library("focus_manager") { + deps = [ + "../..:cr", + ] +} + +js_library("focus_outline_manager") { + deps = [ + "../..:cr", + ] +} + +js_library("focus_row") { + deps = [ + "../..:assert", + "../..:cr", + "../..:event_tracker", + "../..:util", + ] +} + +js_library("focus_without_ink") { + deps = [ + "..:ui", + "../..:cr", + ] +} + +js_library("grid") { + deps = [ + ":list", + ] +} + +js_library("list") { + deps = [ + ":array_data_model", + ":list_item", + ":list_selection_controller", + ":list_selection_model", + ] +} + +js_library("list_item") { + deps = [ + "../..:cr", + ] +} + +js_library("list_selection_controller") { + deps = [ + ":list_selection_model", + "../..:cr", + ] +} + +js_library("list_selection_model") { + deps = [ + "..:event_target", + "../..:cr", + ] +} + +js_library("list_single_selection_model") { + deps = [ + "..:event_target", + "../..:cr", + ] +} + +js_library("menu_button") { + deps = [ + ":menu", + ":menu_item", + ":position_util", + "..:ui", + "../..:assert", + "../..:cr", + "../..:event_tracker", + ] +} + +js_library("menu_item") { + deps = [ + ":command", + "..:ui", + "../..:cr", + "../..:load_time_data", + ] +} + +js_library("menu") { + deps = [ + ":menu_item", + "..:ui", + "../..:assert", + "../..:cr", + ] +} + +js_library("node_utils") { + deps = [ + "../..:cr", + ] +} + +js_library("overlay") { + deps = [ + "../..:cr", + "../..:util", + ] +} + +js_library("position_util") { + deps = [ + "../..:cr", + ] +} + +js_library("splitter") { + deps = [ + "..:ui", + "../..:cr", + ] +} + +js_library("table") { + deps = [ + ":list", + ":list_single_selection_model", + "table:table_column_model", + "table:table_header", + "table:table_list", + ] +} + +js_library("tree") { + deps = [ + "..:ui", + "../..:cr", + "../..:util", + ] +} diff --git a/chromium/ui/webui/resources/js/cr/ui/autocomplete_list.js b/chromium/ui/webui/resources/js/cr/ui/autocomplete_list.js index ccd9400608d..db05e102c36 100644 --- a/chromium/ui/webui/resources/js/cr/ui/autocomplete_list.js +++ b/chromium/ui/webui/resources/js/cr/ui/autocomplete_list.js @@ -28,7 +28,7 @@ cr.define('cr.ui', function() { */ AutocompleteListItem.decorate = function(el) { el.__proto__ = AutocompleteListItem.prototype; - el.decorate(); + /** @type {AutocompleteListItem} */ (el).decorate(); }; AutocompleteListItem.prototype = { diff --git a/chromium/ui/webui/resources/js/cr/ui/grid.js b/chromium/ui/webui/resources/js/cr/ui/grid.js index a71bf6b0e96..dc17e3c3f1c 100644 --- a/chromium/ui/webui/resources/js/cr/ui/grid.js +++ b/chromium/ui/webui/resources/js/cr/ui/grid.js @@ -245,7 +245,7 @@ cr.define('cr.ui', function() { var firstIndex = this.autoExpands ? 0 : this.getIndexForListOffset_(scrollTop); var columns = this.columns; - var count = this.autoExpands_ ? + var count = this.autoExpands ? this.dataModel.length : Math.max( columns * (Math.ceil(clientHeight / itemHeight) + 1), diff --git a/chromium/ui/webui/resources/js/cr/ui/list.js b/chromium/ui/webui/resources/js/cr/ui/list.js index 3bb4ca554b4..6cf92762cf4 100644 --- a/chromium/ui/webui/resources/js/cr/ui/list.js +++ b/chromium/ui/webui/resources/js/cr/ui/list.js @@ -7,6 +7,20 @@ // require: list_selection_controller.js // require: list_item.js +cr.exportPath('cr.ui'); + +/** + * @typedef {{ + * height: number, + * marginBottom: number, + * marginLeft: number, + * marginRight: number, + * marginTop: number, + * width: number + * }} + */ +cr.ui.Size; + /** * @fileoverview This implements a list control. */ @@ -53,8 +67,7 @@ cr.define('cr.ui', function() { * is needed. Note that lead item is allowed to have a different height, to * accommodate lists where a single item at a time can be expanded to show * more detail. - * @type {?{height: number, marginTop: number, marginBottom: number, - * width: number, marginLeft: number, marginRight: number}} + * @type {?cr.ui.Size} * @private */ measured_: null, @@ -347,7 +360,7 @@ cr.define('cr.ui', function() { /** * @return {number} The height of default item, measuring it if necessary. - * @private + * @protected */ getDefaultItemHeight_: function() { return this.getDefaultItemSize_().height; @@ -375,9 +388,9 @@ cr.define('cr.ui', function() { }, /** - * @return {{height: number, width: number}} The height and width - * of default item, measuring it if necessary. - * @private + * @return {!cr.ui.Size} The height and width of default item, measuring it + * if necessary. + * @protected */ getDefaultItemSize_: function() { if (!this.measured_ || !this.measured_.height) { @@ -392,11 +405,8 @@ cr.define('cr.ui', function() { * @param {cr.ui.ListItem=} opt_item The list item to use to do the * measuring. If this is not provided an item will be created based on * the first value in the model. - * @return {{height: number, marginTop: number, marginBottom: number, - * width: number, marginLeft: number, marginRight: number}} - * The height and width of the item, taking - * margins into account, and the top, bottom, left and right margins - * themselves. + * @return {!cr.ui.Size} The height and width of the item, taking margins + * into account, and the top, bottom, left and right margins themselves. */ measureItem: function(opt_item) { var dataModel = this.dataModel; @@ -878,7 +888,7 @@ cr.define('cr.ui', function() { * @param {number} offset The y offset in pixels to get the index of. * @return {number} The index of the list item. Returns the list size if * given offset exceeds the height of list. - * @private + * @protected */ getIndexForListOffset_: function(offset) { var itemHeight = this.getDefaultItemHeight_(); @@ -923,7 +933,7 @@ cr.define('cr.ui', function() { * @param {number} startIndex The index of the first visible item. * @param {number} endOffset The y offset in pixels of the end of the list. * @return {number} The number of list items visible. - * @private + * @protected */ countItemsInRange_: function(startIndex, endOffset) { var endIndex = this.getIndexForListOffset_(endOffset); diff --git a/chromium/ui/webui/resources/js/cr/ui/table/BUILD.gn b/chromium/ui/webui/resources/js/cr/ui/table/BUILD.gn new file mode 100644 index 00000000000..95a7d1685a3 --- /dev/null +++ b/chromium/ui/webui/resources/js/cr/ui/table/BUILD.gn @@ -0,0 +1,42 @@ +# 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_library("table_column") { + deps = [ + "../..:event_target", + "../../..:cr", + ] +} + +js_library("table_column_model") { + deps = [ + ":table_column", + "../../..:cr", + ] +} + +js_library("table_header") { + deps = [ + ":table_splitter", + "../../..:cr", + ] +} + +js_library("table_list") { + deps = [ + ":table_column_model", + "..:list", + "../../..:cr", + ] +} + +js_library("table_splitter") { + deps = [ + ":table_column_model", + "..:splitter", + "../../..:cr", + ] +} diff --git a/chromium/ui/webui/resources/js/cr/ui/table/table_header.js b/chromium/ui/webui/resources/js/cr/ui/table/table_header.js index 1422010a28a..602245d07a2 100644 --- a/chromium/ui/webui/resources/js/cr/ui/table/table_header.js +++ b/chromium/ui/webui/resources/js/cr/ui/table/table_header.js @@ -201,7 +201,8 @@ cr.define('cr.ui.table', function() { var minDistance = TableHeader.TOUCH_DRAG_AREA_WIDTH; var candidate; - var splitters = this.querySelectorAll('.table-header-splitter'); + var splitters = /** @type {NodeList<cr.ui.TableSplitter>} */ ( + this.querySelectorAll('.table-header-splitter')); for (var i = 0; i < splitters.length; i++) { var r = splitters[i].getBoundingClientRect(); if (clientX <= r.left && r.left - clientX <= minDistance) { diff --git a/chromium/ui/webui/resources/polymer_resources.grdp b/chromium/ui/webui/resources/polymer_resources.grdp index 6f4a7f9d3f0..46f529a4be2 100644 --- a/chromium/ui/webui/resources/polymer_resources.grdp +++ b/chromium/ui/webui/resources/polymer_resources.grdp @@ -784,22 +784,10 @@ file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/default-theme.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_POLYMER_1_0_PAPER_STYLES_ELEMENT_STYLES_PAPER_ITEM_STYLES_HTML" - file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-item-styles.html" - type="chrome_html" - compress="gzip" /> <structure name="IDR_POLYMER_1_0_PAPER_STYLES_ELEMENT_STYLES_PAPER_MATERIAL_STYLES_HTML" file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/element-styles/paper-material-styles.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_POLYMER_1_0_PAPER_STYLES_PAPER_STYLES_CLASSES_HTML" - file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles-classes.html" - type="chrome_html" - compress="gzip" /> - <structure name="IDR_POLYMER_1_0_PAPER_STYLES_PAPER_STYLES_HTML" - file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/paper-styles.html" - type="chrome_html" - compress="gzip" /> <structure name="IDR_POLYMER_1_0_PAPER_STYLES_SHADOW_HTML" file="../../../third_party/polymer/v1_0/components-chromium/paper-styles/shadow.html" type="chrome_html" diff --git a/chromium/ui/webui/webui_features.gni b/chromium/ui/webui/webui_features.gni new file mode 100644 index 00000000000..c20d7c39446 --- /dev/null +++ b/chromium/ui/webui/webui_features.gni @@ -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. + +declare_args() { + # Optimize parts of Chrome's UI written with web technologies (HTML/CSS/JS) + # for runtime performance purposes. This does more work at compile time for + # speed benefits at runtime (so we skip in debug builds). + optimize_webui = !is_debug + + # Enable closure type-checking for Chrome's web technology-based UI. This + # enables the webui_closure_compile target which does a no-op without this + # flag enabled. Requires Java. + closure_compile = is_chromeos || is_linux || is_android +} diff --git a/chromium/ui/wm/BUILD.gn b/chromium/ui/wm/BUILD.gn index 7b832f66a3c..0b0e0f3bcd2 100644 --- a/chromium/ui/wm/BUILD.gn +++ b/chromium/ui/wm/BUILD.gn @@ -32,8 +32,6 @@ jumbo_component("wm") { "core/focus_rules.h", "core/native_cursor_manager.h", "core/native_cursor_manager_delegate.h", - "core/shadow.cc", - "core/shadow.h", "core/shadow_controller.cc", "core/shadow_controller.h", "core/shadow_types.cc", @@ -71,6 +69,7 @@ jumbo_component("wm") { "//ui/base", "//ui/base/ime", "//ui/compositor", + "//ui/compositor_extra", "//ui/display", "//ui/events", "//ui/events:events_base", @@ -129,7 +128,6 @@ test("wm_unittests") { "core/easy_resize_window_targeter_unittest.cc", "core/focus_controller_unittest.cc", "core/shadow_controller_unittest.cc", - "core/shadow_unittest.cc", "core/transient_window_manager_unittest.cc", "core/transient_window_stacking_client_unittest.cc", "core/visibility_controller_unittest.cc", @@ -143,13 +141,14 @@ test("wm_unittests") { ":wm", "//base", "//base/test:test_support", - "//mojo/edk/system", + "//mojo/edk", "//skia", "//testing/gtest", "//ui/aura:test_support", "//ui/base:test_support", "//ui/base/ime", "//ui/compositor:test_support", + "//ui/compositor_extra", "//ui/events:test_support", "//ui/events/platform", "//ui/gfx", diff --git a/chromium/ui/wm/core/DEPS b/chromium/ui/wm/core/DEPS index 73f5eb127aa..3d4d8af8767 100644 --- a/chromium/ui/wm/core/DEPS +++ b/chromium/ui/wm/core/DEPS @@ -11,6 +11,7 @@ include_rules = [ "+ui/base/ui_base_paths.h", "+ui/base/ui_base_types.h", "+ui/compositor", + "+ui/compositor_extra", "+ui/events", "+ui/gfx", "+ui/resources/grit/ui_resources.h", diff --git a/chromium/ui/wm/core/easy_resize_window_targeter_unittest.cc b/chromium/ui/wm/core/easy_resize_window_targeter_unittest.cc index 092a17b5b68..a8cb4edf258 100644 --- a/chromium/ui/wm/core/easy_resize_window_targeter_unittest.cc +++ b/chromium/ui/wm/core/easy_resize_window_targeter_unittest.cc @@ -20,15 +20,6 @@ class TestEasyResizeWindowTargeter : public EasyResizeWindowTargeter { public: explicit TestEasyResizeWindowTargeter(aura::Window* window) : EasyResizeWindowTargeter(window, gfx::Insets(), gfx::Insets()) {} - ~TestEasyResizeWindowTargeter() override = default; - - void SetInsets(const gfx::Insets& mouse_extend, - const gfx::Insets& touch_extend) { - EasyResizeWindowTargeter::SetInsets(mouse_extend, touch_extend); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TestEasyResizeWindowTargeter); }; } // namespace diff --git a/chromium/ui/wm/core/focus_controller_unittest.cc b/chromium/ui/wm/core/focus_controller_unittest.cc index 2bab4acc933..684cdea9d3a 100644 --- a/chromium/ui/wm/core/focus_controller_unittest.cc +++ b/chromium/ui/wm/core/focus_controller_unittest.cc @@ -7,7 +7,6 @@ #include <map> #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/default_capture_client.h" #include "ui/aura/client/focus_change_observer.h" @@ -954,7 +953,8 @@ class FocusControllerMouseEventTest : public FocusControllerDirectTestBase { EXPECT_EQ(NULL, GetActiveWindow()); aura::Window* w1 = root_window()->GetChildById(1); SimpleEventHandler handler; - root_window()->PrependPreTargetHandler(&handler); + root_window()->AddPreTargetHandler(&handler, + ui::EventTarget::Priority::kSystem); ui::test::EventGenerator generator(root_window(), w1); generator.ClickLeftButton(); EXPECT_EQ(NULL, GetActiveWindow()); diff --git a/chromium/ui/wm/core/shadow_controller.cc b/chromium/ui/wm/core/shadow_controller.cc index 875ed5e9b14..4e2033eb2e3 100644 --- a/chromium/ui/wm/core/shadow_controller.cc +++ b/chromium/ui/wm/core/shadow_controller.cc @@ -19,15 +19,15 @@ #include "ui/base/class_property.h" #include "ui/base/ui_base_types.h" #include "ui/compositor/layer.h" -#include "ui/wm/core/shadow.h" +#include "ui/compositor_extra/shadow.h" #include "ui/wm/core/shadow_types.h" #include "ui/wm/core/window_util.h" #include "ui/wm/public/activation_client.h" using std::make_pair; -DEFINE_UI_CLASS_PROPERTY_TYPE(::wm::Shadow*); -DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(::wm::Shadow, kShadowLayerKey, nullptr); +DEFINE_UI_CLASS_PROPERTY_TYPE(ui::Shadow*); +DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(ui::Shadow, kShadowLayerKey, nullptr); namespace wm { @@ -205,7 +205,7 @@ void ShadowController::Impl::OnWindowBoundsChanged( const gfx::Rect& old_bounds, const gfx::Rect& new_bounds, ui::PropertyChangeReason reason) { - Shadow* shadow = GetShadowForWindow(window); + ui::Shadow* shadow = GetShadowForWindow(window); if (shadow) shadow->SetContentBounds(gfx::Rect(new_bounds.size())); } @@ -219,12 +219,12 @@ void ShadowController::Impl::OnWindowActivated(ActivationReason reason, aura::Window* gained_active, aura::Window* lost_active) { if (gained_active) { - Shadow* shadow = GetShadowForWindow(gained_active); + ui::Shadow* shadow = GetShadowForWindow(gained_active); if (shadow) shadow->SetElevation(GetShadowElevationForActiveState(gained_active)); } if (lost_active) { - Shadow* shadow = GetShadowForWindow(lost_active); + ui::Shadow* shadow = GetShadowForWindow(lost_active); if (shadow && GetShadowElevationConvertDefault(lost_active) == kShadowElevationInactiveWindow) { shadow->SetElevation( @@ -248,7 +248,7 @@ bool ShadowController::Impl::ShouldShowShadowForWindow( void ShadowController::Impl::HandlePossibleShadowVisibilityChange( aura::Window* window) { const bool should_show = ShouldShowShadowForWindow(window); - Shadow* shadow = GetShadowForWindow(window); + ui::Shadow* shadow = GetShadowForWindow(window); if (shadow) { shadow->SetElevation(GetShadowElevationForActiveState(window)); shadow->layer()->SetVisible(should_show); @@ -259,7 +259,7 @@ void ShadowController::Impl::HandlePossibleShadowVisibilityChange( void ShadowController::Impl::CreateShadowForWindow(aura::Window* window) { DCHECK(!window->IsRootWindow()); - Shadow* shadow = new Shadow(); + ui::Shadow* shadow = new ui::Shadow(); window->SetProperty(kShadowLayerKey, shadow); int corner_radius = window->GetProperty(aura::client::kWindowCornerRadiusKey); @@ -286,7 +286,7 @@ ShadowController::Impl::~Impl() { // ShadowController ------------------------------------------------------------ -Shadow* ShadowController::GetShadowForWindow(aura::Window* window) { +ui::Shadow* ShadowController::GetShadowForWindow(aura::Window* window) { return window->GetProperty(kShadowLayerKey); } diff --git a/chromium/ui/wm/core/shadow_controller.h b/chromium/ui/wm/core/shadow_controller.h index f9c3bee5336..5cdc329a0c2 100644 --- a/chromium/ui/wm/core/shadow_controller.h +++ b/chromium/ui/wm/core/shadow_controller.h @@ -17,10 +17,13 @@ namespace aura { class Window; } +namespace ui { +class Shadow; +} + namespace wm { class ActivationClient; -class Shadow; // ShadowController observes changes to windows and creates and updates drop // shadows as needed. ShadowController itself is light weight and per @@ -29,7 +32,7 @@ class Shadow; class WM_CORE_EXPORT ShadowController : public ActivationChangeObserver { public: // Returns the shadow for the |window|, or NULL if no shadow exists. - static Shadow* GetShadowForWindow(aura::Window* window); + static ui::Shadow* GetShadowForWindow(aura::Window* window); explicit ShadowController(ActivationClient* activation_client); ~ShadowController() override; diff --git a/chromium/ui/wm/core/shadow_controller_unittest.cc b/chromium/ui/wm/core/shadow_controller_unittest.cc index c3af5692265..e9ad4d87c7b 100644 --- a/chromium/ui/wm/core/shadow_controller_unittest.cc +++ b/chromium/ui/wm/core/shadow_controller_unittest.cc @@ -15,8 +15,8 @@ #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/compositor/layer.h" +#include "ui/compositor_extra/shadow.h" #include "ui/wm/core/default_activation_client.h" -#include "ui/wm/core/shadow.h" #include "ui/wm/core/shadow_types.h" #include "ui/wm/core/window_util.h" #include "ui/wm/public/activation_client.h" @@ -66,7 +66,7 @@ TEST_F(ShadowControllerTest, Shadow) { EXPECT_FALSE(ShadowController::GetShadowForWindow(window.get())); window->Show(); - const Shadow* shadow = ShadowController::GetShadowForWindow(window.get()); + const ui::Shadow* shadow = ShadowController::GetShadowForWindow(window.get()); ASSERT_TRUE(shadow != NULL); EXPECT_TRUE(shadow->layer()->visible()); @@ -99,7 +99,7 @@ TEST_F(ShadowControllerTest, ShadowBounds) { // When the shadow is first created, it should use the window's size (but // remain at the origin, since it's a child of the window's layer). SetShadowElevation(window.get(), kShadowElevationInactiveWindow); - const Shadow* shadow = ShadowController::GetShadowForWindow(window.get()); + const ui::Shadow* shadow = ShadowController::GetShadowForWindow(window.get()); ASSERT_TRUE(shadow != NULL); EXPECT_EQ(gfx::Rect(kOldBounds.size()).ToString(), shadow->content_bounds().ToString()); @@ -122,7 +122,7 @@ TEST_F(ShadowControllerTest, ShadowStyle) { ActivateWindow(window1.get()); // window1 is active, so style should have active appearance. - Shadow* shadow1 = ShadowController::GetShadowForWindow(window1.get()); + ui::Shadow* shadow1 = ShadowController::GetShadowForWindow(window1.get()); ASSERT_TRUE(shadow1 != NULL); EXPECT_EQ(kShadowElevationActiveWindow, shadow1->desired_elevation()); @@ -136,7 +136,7 @@ TEST_F(ShadowControllerTest, ShadowStyle) { ActivateWindow(window2.get()); // window1 is now inactive, so shadow should go inactive. - Shadow* shadow2 = ShadowController::GetShadowForWindow(window2.get()); + ui::Shadow* shadow2 = ShadowController::GetShadowForWindow(window2.get()); ASSERT_TRUE(shadow2 != NULL); EXPECT_EQ(kShadowElevationInactiveWindow, shadow1->desired_elevation()); EXPECT_EQ(kShadowElevationActiveWindow, shadow2->desired_elevation()); @@ -150,7 +150,7 @@ TEST_F(ShadowControllerTest, ShowState) { ParentWindow(window.get()); window->Show(); - Shadow* shadow = ShadowController::GetShadowForWindow(window.get()); + ui::Shadow* shadow = ShadowController::GetShadowForWindow(window.get()); ASSERT_TRUE(shadow != NULL); EXPECT_EQ(kShadowElevationInactiveWindow, shadow->desired_elevation()); @@ -173,7 +173,7 @@ TEST_F(ShadowControllerTest, SmallShadowsForTooltipsAndMenus) { tooltip_window->SetBounds(gfx::Rect(10, 20, 300, 400)); tooltip_window->Show(); - Shadow* tooltip_shadow = + ui::Shadow* tooltip_shadow = ShadowController::GetShadowForWindow(tooltip_window.get()); ASSERT_TRUE(tooltip_shadow != NULL); EXPECT_EQ(kShadowElevationMenuOrTooltip, tooltip_shadow->desired_elevation()); @@ -185,7 +185,7 @@ TEST_F(ShadowControllerTest, SmallShadowsForTooltipsAndMenus) { menu_window->SetBounds(gfx::Rect(10, 20, 300, 400)); menu_window->Show(); - Shadow* menu_shadow = + ui::Shadow* menu_shadow = ShadowController::GetShadowForWindow(tooltip_window.get()); ASSERT_TRUE(menu_shadow != NULL); EXPECT_EQ(kShadowElevationMenuOrTooltip, menu_shadow->desired_elevation()); @@ -203,7 +203,7 @@ TEST_F(ShadowControllerTest, TransientParentKeepsActiveShadow) { ActivateWindow(window1.get()); // window1 is active, so style should have active appearance. - Shadow* shadow1 = ShadowController::GetShadowForWindow(window1.get()); + ui::Shadow* shadow1 = ShadowController::GetShadowForWindow(window1.get()); ASSERT_TRUE(shadow1 != NULL); EXPECT_EQ(kShadowElevationActiveWindow, shadow1->desired_elevation()); diff --git a/chromium/ui/wm/core/window_animations.cc b/chromium/ui/wm/core/window_animations.cc index d8781f7afc0..af1a9b1368f 100644 --- a/chromium/ui/wm/core/window_animations.cc +++ b/chromium/ui/wm/core/window_animations.cc @@ -14,7 +14,6 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" diff --git a/chromium/ui/wm/core/window_util.cc b/chromium/ui/wm/core/window_util.cc index d8cbfdc9e6a..7b00a00b95a 100644 --- a/chromium/ui/wm/core/window_util.cc +++ b/chromium/ui/wm/core/window_util.cc @@ -4,7 +4,6 @@ #include "ui/wm/core/window_util.h" -#include "base/memory/ptr_util.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" #include "ui/compositor/layer.h" diff --git a/chromium/ui/wm/core/wm_state.cc b/chromium/ui/wm/core/wm_state.cc index a68006d0934..89410d8241f 100644 --- a/chromium/ui/wm/core/wm_state.cc +++ b/chromium/ui/wm/core/wm_state.cc @@ -4,7 +4,6 @@ #include "ui/wm/core/wm_state.h" -#include "base/memory/ptr_util.h" #include "ui/events/platform/platform_event_source.h" #include "ui/wm/core/capture_controller.h" #include "ui/wm/core/transient_window_controller.h" |