diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-20 10:33:36 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-22 11:45:12 +0000 |
commit | be59a35641616a4cf23c4a13fa0632624b021c1b (patch) | |
tree | 9da183258bdf9cc413f7562079d25ace6955467f /chromium/ui | |
parent | d702e4b6a64574e97fc7df8fe3238cde70242080 (diff) | |
download | qtwebengine-chromium-be59a35641616a4cf23c4a13fa0632624b021c1b.tar.gz |
BASELINE: Update Chromium to 62.0.3202.101
Change-Id: I2d5eca8117600df6d331f6166ab24d943d9814ac
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/ui')
1090 files changed, 26979 insertions, 14012 deletions
diff --git a/chromium/ui/accelerated_widget_mac/display_link_mac.cc b/chromium/ui/accelerated_widget_mac/display_link_mac.cc index a1d5b730965..f74c509812f 100644 --- a/chromium/ui/accelerated_widget_mac/display_link_mac.cc +++ b/chromium/ui/accelerated_widget_mac/display_link_mac.cc @@ -8,6 +8,7 @@ #include "base/location.h" #include "base/logging.h" +#include "base/numerics/safe_conversions.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" @@ -153,10 +154,18 @@ void DisplayLinkMac::Tick(const CVTimeStamp& cv_time) { return; } - timebase_ = base::TimeTicks::FromInternalValue( - cv_time.hostTime / 1000); - interval_ = base::TimeDelta::FromMicroseconds( - 1000000 * static_cast<int64_t>(numerator) / denominator); + base::CheckedNumeric<int64_t> interval_us(base::Time::kMicrosecondsPerSecond); + interval_us *= numerator; + interval_us /= denominator; + if (!interval_us.IsValid()) { + LOG(DFATAL) << "Bailing due to overflow: " + << base::Time::kMicrosecondsPerSecond << " * " << numerator + << " / " << denominator; + return; + } + + timebase_ = base::TimeTicks::FromMachAbsoluteTime(cv_time.hostTime); + interval_ = base::TimeDelta::FromMicroseconds(interval_us.ValueOrDie()); timebase_and_interval_valid_ = true; // Don't restart the display link for 10 seconds. @@ -218,4 +227,3 @@ base::LazyInstance<DisplayLinkMac::DisplayMap>::DestructorAtExit DisplayLinkMac::display_map_ = LAZY_INSTANCE_INITIALIZER; } // ui - diff --git a/chromium/ui/accelerated_widget_mac/io_surface_context.mm b/chromium/ui/accelerated_widget_mac/io_surface_context.mm index 31840130656..2823342185f 100644 --- a/chromium/ui/accelerated_widget_mac/io_surface_context.mm +++ b/chromium/ui/accelerated_widget_mac/io_surface_context.mm @@ -11,6 +11,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/trace_event/trace_event.h" +#include "ui/gl/gl_context.h" #include "ui/gl/gl_switches.h" #include "ui/gl/gpu_switching_manager.h" @@ -48,7 +49,7 @@ IOSurfaceContext::Get(Type type) { std::vector<CGLPixelFormatAttribute> attribs; attribs.push_back(kCGLPFADepthSize); attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); - if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) + if (gl::GLContext::SwitchableGPUsSupported()) attribs.push_back(kCGLPFAAllowOfflineRenderers); attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); GLint number_virtual_screens = 0; diff --git a/chromium/ui/accessibility/BUILD.gn b/chromium/ui/accessibility/BUILD.gn index 324df01891f..e0ebf1a3ba9 100644 --- a/chromium/ui/accessibility/BUILD.gn +++ b/chromium/ui/accessibility/BUILD.gn @@ -18,6 +18,8 @@ component("accessibility") { sources = [ "ax_action_data.cc", "ax_action_data.h", + "ax_event_generator.cc", + "ax_event_generator.h", "ax_export.h", "ax_host_delegate.cc", "ax_host_delegate.h", @@ -51,6 +53,8 @@ component("accessibility") { "ax_tree_update.h", "platform/ax_android_constants.cc", "platform/ax_android_constants.h", + "platform/ax_platform_node.cc", + "platform/ax_platform_node.h", "platform/ax_platform_unique_id.cc", "platform/ax_platform_unique_id.h", "platform/ax_snapshot_node_android_platform.cc", @@ -59,8 +63,6 @@ component("accessibility") { if (has_native_accessibility) { sources += [ - "platform/ax_platform_node.cc", - "platform/ax_platform_node.h", "platform/ax_platform_node_base.cc", "platform/ax_platform_node_base.h", "platform/ax_platform_node_delegate.h", @@ -159,6 +161,7 @@ static_library("test_support") { test("accessibility_unittests") { testonly = true sources = [ + "ax_event_generator_unittest.cc", "ax_generated_tree_unittest.cc", "ax_node_position_unittest.cc", "ax_text_utils_unittest.cc", diff --git a/chromium/ui/accessibility/OWNERS b/chromium/ui/accessibility/OWNERS index 6ca07b4968a..84c7ff5d181 100644 --- a/chromium/ui/accessibility/OWNERS +++ b/chromium/ui/accessibility/OWNERS @@ -1,6 +1,7 @@ dmazzoni@chromium.org dtseng@chromium.org aboxhall@chromium.org +nektar@chromium.org # TEAM: chromium-accessibility@chromium.org -# COMPONENT: UI>Accessibility +# COMPONENT: Internals>Accessibility diff --git a/chromium/ui/accessibility/PRESUBMIT.py b/chromium/ui/accessibility/PRESUBMIT.py index 9e1b82937ff..31b9a6ed158 100644 --- a/chromium/ui/accessibility/PRESUBMIT.py +++ b/chromium/ui/accessibility/PRESUBMIT.py @@ -9,6 +9,9 @@ import os, re AX_IDL = 'ui/accessibility/ax_enums.idl' AUTOMATION_IDL = 'chrome/common/extensions/api/automation.idl' +AX_JS_FILE = 'content/browser/resources/accessibility/accessibility.js' +AX_MODE_HEADER = 'ui/accessibility/ax_modes.h' + def InitialLowerCamelCase(unix_name): words = unix_name.split('_') return words[0] + ''.join(word.capitalize() for word in words[1:]) @@ -64,17 +67,31 @@ def CheckMatchingEnum(ax_enums, src = ax_enums[ax_enum_name] dst = automation_enums[automation_enum_name] for value in src: - if InitialLowerCamelCase(value) not in dst: + lower_value = InitialLowerCamelCase(value) + if lower_value in dst: + dst.remove(lower_value) # Any remaining at end are extra and a mismatch. + else: errs.append(output_api.PresubmitError( 'Found %s.%s in %s, but did not find %s.%s in %s' % ( ax_enum_name, value, AX_IDL, automation_enum_name, InitialLowerCamelCase(value), AUTOMATION_IDL))) + # Should be no remaining items + for value in dst: + errs.append(output_api.PresubmitError( + 'Found %s.%s in %s, but did not find %s.%s in %s' % ( + automation_enum_name, value, AUTOMATION_IDL, + ax_enum_name, InitialLowerCamelCase(value), + AX_IDL))) def CheckEnumsMatch(input_api, output_api): repo_root = input_api.change.RepositoryRoot() ax_enums = GetEnumsFromFile(os.path.join(repo_root, AX_IDL)) automation_enums = GetEnumsFromFile(os.path.join(repo_root, AUTOMATION_IDL)) + + # Focused state only exists in automation. + automation_enums['StateType'].remove('focused') + errs = [] CheckMatchingEnum(ax_enums, 'AXRole', automation_enums, 'RoleType', errs, output_api) @@ -88,12 +105,95 @@ def CheckEnumsMatch(input_api, output_api): 'Restriction', errs, output_api) return errs +# Given a full path to c++ header, return an array of the first static +# constexpr defined. (Note there can be more than one defined in a C++ +# header) +def GetConstexprFromFile(fullpath): + values = [] + for line in open(fullpath).readlines(): + # Strip out comments + line = re.sub('//.*', '', line) + + # Look for lines of the form "static constexpr <type> NAME " + m = re.search('static constexpr [\w]+ ([\w]+)', line) + if m: + values.append(m.group(1)) + + return values + +# Given a full path to js file, return the AXMode consts +# defined +def GetAccessibilityModesFromFile(fullpath): + values = [] + inside = False + for line in open(fullpath).readlines(): + # Strip out comments + line = re.sub('//.*', '', line) + + # Look for the block of code that defines AXMode + m = re.search('const AXMode = {', line) + if m: + inside = True + continue + + # Look for a "}" character signifying the end of an enum + if line.find('};') >= 0: + return values + + if not inside: + continue + + m = re.search('([\w]+):', line) + if m: + values.append(m.group(1)) + continue + + # getters + m = re.search('get ([\w]+)\(\)', line) + if m: + values.append(m.group(1)) + return values + +# Make sure that the modes defined in the C++ header match those defined in +# the js file. Note that this doesn't guarantee that the values are the same, +# but does make sure if we add or remove we can signal to the developer that +# they should be aware that this dependency exists. +def CheckModesMatch(input_api, output_api): + errs = [] + repo_root = input_api.change.RepositoryRoot() + + ax_modes_in_header = GetConstexprFromFile( + os.path.join(repo_root,AX_MODE_HEADER)) + ax_modes_in_js = GetAccessibilityModesFromFile( + os.path.join(repo_root, AX_JS_FILE)) + + for value in ax_modes_in_header: + if value not in ax_modes_in_js: + errs.append(output_api.PresubmitError( + 'Found %s in %s, but did not find %s in %s' % ( + value, AX_MODE_HEADER, value, AX_JS_FILE))) + return errs + def CheckChangeOnUpload(input_api, output_api): - if AX_IDL not in input_api.LocalPaths(): - return [] - return CheckEnumsMatch(input_api, output_api) + errs = [] + for path in input_api.LocalPaths(): + path = path.replace('\\', '/') + if AX_IDL == path: + errs.extend(CheckEnumsMatch(input_api, output_api)) + + if AX_MODE_HEADER == path: + errs.extend(CheckModesMatch(input_api, output_api)) + + return errs def CheckChangeOnCommit(input_api, output_api): - if AX_IDL not in input_api.LocalPaths(): - return [] - return CheckEnumsMatch(input_api, output_api) + errs = [] + for path in input_api.LocalPaths(): + path = path.replace('\\', '/') + if AX_IDL == path: + errs.extend(CheckEnumsMatch(input_api, output_api)) + + if AX_MODE_HEADER == path: + errs.extend(CheckModesMatch(input_api, output_api)) + + return errs diff --git a/chromium/ui/accessibility/ax_action_data.cc b/chromium/ui/accessibility/ax_action_data.cc index 7769b27951c..b5da425bf25 100644 --- a/chromium/ui/accessibility/ax_action_data.cc +++ b/chromium/ui/accessibility/ax_action_data.cc @@ -17,7 +17,9 @@ namespace ui { AXActionData::AXActionData() : action(AX_ACTION_NONE), + target_tree_id(-1), target_node_id(-1), + request_id(-1), flags(0), anchor_node_id(-1), anchor_offset(-1), diff --git a/chromium/ui/accessibility/ax_action_data.h b/chromium/ui/accessibility/ax_action_data.h index a80f4a5b7e6..2993449066c 100644 --- a/chromium/ui/accessibility/ax_action_data.h +++ b/chromium/ui/accessibility/ax_action_data.h @@ -31,9 +31,18 @@ struct AX_EXPORT AXActionData { // The action to take. AXAction action; + // The ID of the tree that this action should be performed on. + int target_tree_id; + + // The source extension id (if any) of this action. + std::string source_extension_id; + // The ID of the node that this action should be performed on. int target_node_id; + // The request id of this action tracked by the client. + int request_id; + // Use enums from AXActionFlags int flags; diff --git a/chromium/ui/accessibility/ax_enums.idl b/chromium/ui/accessibility/ax_enums.idl index 428c4c7e056..2492065931b 100644 --- a/chromium/ui/accessibility/ax_enums.idl +++ b/chromium/ui/accessibility/ax_enums.idl @@ -3,8 +3,7 @@ // found in the LICENSE file. // TODO(nektar): Migrate entire file to Mojoq. -// These should be kept in sync with third_party/WebKit/public/web/WebAXEnums.h -// until the Chromium and Blink trees are merged. +// Must also be kept in sync with chrome/common/extensions/api/automation.idl. [camel_case_enum_to_string=true] namespace ui { // For new entries to the following four enums, also add to @@ -87,8 +86,6 @@ audio, banner, blockquote, - // TODO(nektar): Remove busy_indicator because it's used nowhere. - busy_indicator, button, button_drop_down, // Not used on Web. canvas, @@ -128,7 +125,6 @@ iframe, iframe_presentational, ignored, - image_map_link, image_map, image, inline_text_box, @@ -159,7 +155,6 @@ meter, navigation, note, - outline, pane, paragraph, pop_up_button, @@ -173,11 +168,8 @@ row_header, row, ruby, - ruler, svg_root, - scroll_area, scroll_bar, - seamless_web_area, search, search_box, slider, @@ -188,7 +180,6 @@ static_text, status, switch, - tab_group, tab_list, tab_panel, tab, @@ -213,7 +204,6 @@ }; enum AXState { - busy, collapsed, default, editable, @@ -264,10 +254,24 @@ // Increment a slider or range control by one step value. increment, + // Load inline text boxes for this subtree, providing information + // about word boundaries, line layout, and individual character + // bounding boxes. + load_inline_text_boxes, + // Delete any selected text in the control's text value and // insert |AXActionData::value| in its place, like when typing or pasting. replace_selected_text, + // Scrolls by approximately one screen in a specific direction. Should be + // called on a node that has scrollable boolean set to true. + scroll_backward, + scroll_forward, + scroll_up, + scroll_down, + scroll_left, + scroll_right, + // Scroll any scrollable containers to make the target object visible // on the screen. Optionally pass a subfocus rect in // AXActionData.target_rect. @@ -277,7 +281,6 @@ // global screen coordinates. Pass a point in AXActionData.target_point. scroll_to_point, - set_accessibility_focus, set_scroll_offset, set_selection, @@ -462,11 +465,16 @@ }; [cpp_enum_prefix_override="ax_attr"] enum AXBoolAttribute { + // Generic busy state, does not have to be on a live region. + busy, + // The object is at the root of an editable field, such as a content + // editable. + editable_root, + // Live region attributes. container_live_atomic, container_live_busy, live_atomic, - live_busy, // If a dialog box is marked as explicitly modal modal, @@ -476,7 +484,10 @@ update_location_only, // Set on a canvas element if it has fallback content. - canvas_has_fallback + canvas_has_fallback, + + // Indicates this node is scrollable (Android only). + scrollable }; [cpp_enum_prefix_override="ax_attr"] enum AXIntListAttribute { @@ -566,7 +577,24 @@ text_match_active_suggestion = 20, spelling_text_match_active_suggestion = 21, grammar_text_match_active_suggestion = 22, - spelling_grammar_text_match_active_suggestion = 23 + spelling_grammar_text_match_active_suggestion = 23, + suggestion = 32, + spelling_suggestion = 33, + grammar_suggestion = 34, + spelling_grammar_suggestion = 35, + text_match_suggestion = 36, + spelling_text_match_suggestion = 37, + grammar_text_match_suggestion = 38, + spelling_grammar_text_match_suggestion = 39, + // We again skip over DocumentMarker::MarkerType::Composition = 8 here + active_suggestion_suggestion = 48, + spelling_active_suggestion_suggestion = 49, + grammar_active_suggestion_suggestion = 50, + spelling_grammar_active_suggestion_suggestion = 51, + text_match_active_suggestion_suggestion = 52, + spelling_text_match_active_suggestion_suggestion = 53, + grammar_text_match_active_suggestion_suggestion = 54, + spelling_grammar_text_match_active_suggestion_suggestion = 55 }; enum AXTextDirection { diff --git a/chromium/ui/accessibility/ax_event_generator.cc b/chromium/ui/accessibility/ax_event_generator.cc new file mode 100644 index 00000000000..dfe82d7b254 --- /dev/null +++ b/chromium/ui/accessibility/ax_event_generator.cc @@ -0,0 +1,305 @@ +// 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/accessibility/ax_event_generator.h" + +#include "ui/accessibility/ax_node.h" +#include "ui/accessibility/ax_role_properties.h" + +namespace ui { + +AXEventGenerator::TargetedEvent::TargetedEvent(ui::AXNode* node, Event event) + : node(node), event(event) {} + +AXEventGenerator::Iterator::Iterator( + const std::map<AXNode*, std::set<Event>>& map, + const std::map<AXNode*, std::set<Event>>::const_iterator& head) + : map_(map), map_iter_(head) { + if (map_iter_ != map.end()) + set_iter_ = map_iter_->second.begin(); +} + +AXEventGenerator::Iterator::Iterator(const AXEventGenerator::Iterator& other) = + default; + +bool AXEventGenerator::Iterator::operator!=( + const AXEventGenerator::Iterator& rhs) const { + return map_iter_ != rhs.map_iter_ || + (map_iter_ != map_.end() && set_iter_ != rhs.set_iter_); +} + +AXEventGenerator::Iterator& AXEventGenerator::Iterator::operator++() { + if (map_iter_ == map_.end()) + return *this; + + set_iter_++; + while (map_iter_ != map_.end() && set_iter_ == map_iter_->second.end()) { + map_iter_++; + if (map_iter_ != map_.end()) + set_iter_ = map_iter_->second.begin(); + } + + return *this; +} + +AXEventGenerator::TargetedEvent AXEventGenerator::Iterator::operator*() const { + DCHECK(map_iter_ != map_.end() && set_iter_ != map_iter_->second.end()); + return AXEventGenerator::TargetedEvent(map_iter_->first, *set_iter_); +} + +AXEventGenerator::AXEventGenerator(AXTree* tree) : tree_(tree) { + DCHECK(tree_); + tree_->SetDelegate(this); +} + +AXEventGenerator::~AXEventGenerator() { + DCHECK(tree_); + tree_->SetDelegate(nullptr); +} + +void AXEventGenerator::Clear() { + tree_events_.clear(); +} + +void AXEventGenerator::AddEvent(ui::AXNode* node, + AXEventGenerator::Event event) { + tree_events_[node].insert(event); +} + +void AXEventGenerator::OnNodeDataWillChange(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) { + DCHECK_EQ(tree_, tree); + // Fire CHILDREN_CHANGED events when the list of children updates. + // Internally we store inline text box nodes as children of a static text + // node, which enables us to determine character bounds and line layout. + // We don't expose those to platform APIs, though, so suppress + // CHILDREN_CHANGED events on static text nodes. + if (new_node_data.child_ids != old_node_data.child_ids && + new_node_data.role != ui::AX_ROLE_STATIC_TEXT) { + AXNode* node = tree_->GetFromId(new_node_data.id); + tree_events_[node].insert(Event::CHILDREN_CHANGED); + } +} + +void AXEventGenerator::OnRoleChanged(AXTree* tree, + AXNode* node, + AXRole old_role, + AXRole new_role) { + DCHECK_EQ(tree_, tree); + AddEvent(node, Event::ROLE_CHANGED); +} + +void AXEventGenerator::OnStateChanged(AXTree* tree, + AXNode* node, + AXState state, + bool new_value) { + DCHECK_EQ(tree_, tree); + + AddEvent(node, Event::STATE_CHANGED); + if (state == ui::AX_STATE_EXPANDED) { + AddEvent(node, new_value ? Event::EXPANDED : Event::COLLAPSED); + if (node->data().role == ui::AX_ROLE_ROW || + node->data().role == ui::AX_ROLE_TREE_ITEM) { + ui::AXNode* container = node; + while (container && !ui::IsRowContainer(container->data().role)) + container = container->parent(); + if (container) + AddEvent(container, Event::ROW_COUNT_CHANGED); + } + } + if (state == ui::AX_STATE_SELECTED) { + 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); + } +} + +void AXEventGenerator::OnStringAttributeChanged(AXTree* tree, + AXNode* node, + AXStringAttribute attr, + const std::string& old_value, + const std::string& new_value) { + DCHECK_EQ(tree_, tree); + + switch (attr) { + case ui::AX_ATTR_NAME: + AddEvent(node, Event::NAME_CHANGED); + break; + case ui::AX_ATTR_DESCRIPTION: + AddEvent(node, Event::DESCRIPTION_CHANGED); + break; + case ui::AX_ATTR_VALUE: + AddEvent(node, Event::VALUE_CHANGED); + break; + case ui::AX_ATTR_ARIA_INVALID_VALUE: + AddEvent(node, Event::INVALID_STATUS_CHANGED); + break; + case ui::AX_ATTR_LIVE_STATUS: + if (node->data().role != ui::AX_ROLE_ALERT) + AddEvent(node, Event::LIVE_REGION_CREATED); + break; + default: + AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED); + break; + } +} + +void AXEventGenerator::OnIntAttributeChanged(AXTree* tree, + AXNode* node, + AXIntAttribute attr, + int32_t old_value, + int32_t new_value) { + DCHECK_EQ(tree_, tree); + + switch (attr) { + case ui::AX_ATTR_ACTIVEDESCENDANT_ID: + AddEvent(node, Event::ACTIVE_DESCENDANT_CHANGED); + break; + case ui::AX_ATTR_CHECKED_STATE: + AddEvent(node, Event::CHECKED_STATE_CHANGED); + break; + case ui::AX_ATTR_INVALID_STATE: + AddEvent(node, Event::INVALID_STATUS_CHANGED); + break; + case ui::AX_ATTR_RESTRICTION: + AddEvent(node, Event::STATE_CHANGED); + break; + case ui::AX_ATTR_SCROLL_X: + case ui::AX_ATTR_SCROLL_Y: + AddEvent(node, Event::SCROLL_POSITION_CHANGED); + break; + default: + AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED); + break; + } +} + +void AXEventGenerator::OnFloatAttributeChanged(AXTree* tree, + AXNode* node, + AXFloatAttribute attr, + float old_value, + float new_value) { + DCHECK_EQ(tree_, tree); + + if (attr == ui::AX_ATTR_VALUE_FOR_RANGE) + AddEvent(node, Event::VALUE_CHANGED); + else + AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED); +} + +void AXEventGenerator::OnBoolAttributeChanged(AXTree* tree, + AXNode* node, + AXBoolAttribute attr, + bool new_value) { + DCHECK_EQ(tree_, tree); + + AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED); +} + +void AXEventGenerator::OnIntListAttributeChanged( + AXTree* tree, + AXNode* node, + AXIntListAttribute attr, + const std::vector<int32_t>& old_value, + const std::vector<int32_t>& new_value) { + DCHECK_EQ(tree_, tree); + AddEvent(node, Event::OTHER_ATTRIBUTE_CHANGED); +} + +void AXEventGenerator::OnStringListAttributeChanged( + AXTree* tree, + AXNode* node, + AXStringListAttribute attr, + const std::vector<std::string>& old_value, + const std::vector<std::string>& new_value) { + DCHECK_EQ(tree_, tree); +} + +void AXEventGenerator::OnTreeDataChanged(AXTree* tree, + const ui::AXTreeData& old_tree_data, + const ui::AXTreeData& new_tree_data) { + DCHECK_EQ(tree_, tree); + + if (new_tree_data.loaded && !old_tree_data.loaded) + AddEvent(tree->root(), Event::LOAD_COMPLETE); + if (new_tree_data.sel_anchor_object_id != + old_tree_data.sel_anchor_object_id || + new_tree_data.sel_anchor_offset != old_tree_data.sel_anchor_offset || + new_tree_data.sel_anchor_affinity != old_tree_data.sel_anchor_affinity || + new_tree_data.sel_focus_object_id != old_tree_data.sel_focus_object_id || + new_tree_data.sel_focus_offset != old_tree_data.sel_focus_offset || + new_tree_data.sel_focus_affinity != old_tree_data.sel_focus_affinity) { + AddEvent(tree->root(), Event::DOCUMENT_SELECTION_CHANGED); + } + if (new_tree_data.title != old_tree_data.title) + AddEvent(tree->root(), Event::DOCUMENT_TITLE_CHANGED); +} + +void AXEventGenerator::OnNodeWillBeDeleted(AXTree* tree, AXNode* node) { + DCHECK_EQ(tree_, tree); + tree_events_.erase(node); +} + +void AXEventGenerator::OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) { + DCHECK_EQ(tree_, tree); +} + +void AXEventGenerator::OnNodeWillBeReparented(AXTree* tree, AXNode* node) { + DCHECK_EQ(tree_, tree); +} + +void AXEventGenerator::OnSubtreeWillBeReparented(AXTree* tree, AXNode* node) { + DCHECK_EQ(tree_, tree); +} + +void AXEventGenerator::OnNodeCreated(AXTree* tree, AXNode* node) { + DCHECK_EQ(tree_, tree); +} + +void AXEventGenerator::OnNodeReparented(AXTree* tree, AXNode* node) { + DCHECK_EQ(tree_, tree); +} + +void AXEventGenerator::OnNodeChanged(AXTree* tree, AXNode* node) { + DCHECK_EQ(tree_, tree); +} + +void AXEventGenerator::OnAtomicUpdateFinished( + AXTree* tree, + bool root_changed, + const std::vector<Change>& changes) { + DCHECK_EQ(tree_, tree); + + if (root_changed && tree->data().loaded) + AddEvent(tree->root(), Event::LOAD_COMPLETE); + + for (const auto& change : changes) { + if ((change.type == NODE_CREATED || change.type == SUBTREE_CREATED) && + change.node->data().HasStringAttribute(ui::AX_ATTR_LIVE_STATUS)) { + if (change.node->data().role == ui::AX_ROLE_ALERT) + AddEvent(change.node, Event::ALERT); + else + AddEvent(change.node, Event::LIVE_REGION_CREATED); + continue; + } + if (change.node->data().HasStringAttribute( + ui::AX_ATTR_CONTAINER_LIVE_STATUS)) { + if (!change.node->data().GetStringAttribute(ui::AX_ATTR_NAME).empty()) + AddEvent(change.node, Event::LIVE_REGION_NODE_CHANGED); + ui::AXNode* live_root = change.node; + while (live_root && + !live_root->data().HasStringAttribute(ui::AX_ATTR_LIVE_STATUS)) + live_root = live_root->parent(); + if (live_root) + AddEvent(live_root, Event::LIVE_REGION_CHANGED); + } + } +} + +} // namespace ui diff --git a/chromium/ui/accessibility/ax_event_generator.h b/chromium/ui/accessibility/ax_event_generator.h new file mode 100644 index 00000000000..ee4996858f0 --- /dev/null +++ b/chromium/ui/accessibility/ax_event_generator.h @@ -0,0 +1,161 @@ +// 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_ACCESSIBILITY_AX_EVENT_GENERATOR_H_ +#define UI_ACCESSIBILITY_AX_EVENT_GENERATOR_H_ + +#include <map> +#include <set> +#include <vector> + +#include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_tree.h" + +namespace ui { + +// Subclass of AXTreeDelegate that automatically generates AXEvents to fire +// based on changes to an accessibility tree. Every platform +// tends to want different events, so this class lets each platform +// handle the events it wants and ignore the others. +class AX_EXPORT AXEventGenerator : public AXTreeDelegate { + public: + enum class Event : int32_t { + ACTIVE_DESCENDANT_CHANGED, + ALERT, + CHECKED_STATE_CHANGED, + CHILDREN_CHANGED, + COLLAPSED, + DESCRIPTION_CHANGED, + DOCUMENT_SELECTION_CHANGED, + DOCUMENT_TITLE_CHANGED, + EXPANDED, + INVALID_STATUS_CHANGED, + LIVE_REGION_CHANGED, + LIVE_REGION_CREATED, + LIVE_REGION_NODE_CHANGED, + LOAD_COMPLETE, + NAME_CHANGED, + OTHER_ATTRIBUTE_CHANGED, + ROLE_CHANGED, + ROW_COUNT_CHANGED, + SCROLL_POSITION_CHANGED, + SELECTED_CHANGED, + SELECTED_CHILDREN_CHANGED, + STATE_CHANGED, + VALUE_CHANGED, + }; + + struct TargetedEvent { + TargetedEvent(ui::AXNode* node, Event event); + ui::AXNode* node; + Event event; + }; + + class AX_EXPORT Iterator + : public std::iterator<std::input_iterator_tag, TargetedEvent> { + public: + Iterator(const std::map<AXNode*, std::set<Event>>& map, + const std::map<AXNode*, std::set<Event>>::const_iterator& head); + Iterator(const Iterator& other); + + bool operator!=(const Iterator& rhs) const; + Iterator& operator++(); + TargetedEvent operator*() const; + + private: + const std::map<AXNode*, std::set<Event>>& map_; + std::map<AXNode*, std::set<Event>>::const_iterator map_iter_; + std::set<Event>::const_iterator set_iter_; + }; + + // Automatically registers itself as the delegate of |tree| and + // clears it on desctruction. |tree| must be valid for the lifetime + // of this object. + AXEventGenerator(AXTree* tree); + + ~AXEventGenerator() override; + + Iterator begin() const { + return Iterator(tree_events_, tree_events_.begin()); + } + Iterator end() const { return Iterator(tree_events_, tree_events_.end()); } + + // Clear any previously added events. + void Clear(); + + // This is called automatically based on changes to the tree observed + // by AXTreeDelegate, but you can also call it directly to add events + // and retrieve them later. + // + // Note that events are organized by node and then by event id to + // efficiently remove duplicates, so events won't be retrieved in the + // same order they were added. + void AddEvent(ui::AXNode* node, Event event); + + protected: + // AXTreeDelegate overrides. + void OnNodeDataWillChange(AXTree* tree, + const AXNodeData& old_node_data, + const AXNodeData& new_node_data) override; + void OnRoleChanged(AXTree* tree, + AXNode* node, + AXRole old_role, + AXRole new_role) override; + void OnStateChanged(AXTree* tree, + AXNode* node, + AXState state, + bool new_value) override; + void OnStringAttributeChanged(AXTree* tree, + AXNode* node, + AXStringAttribute attr, + const std::string& old_value, + const std::string& new_value) override; + void OnIntAttributeChanged(AXTree* tree, + AXNode* node, + AXIntAttribute attr, + int32_t old_value, + int32_t new_value) override; + void OnFloatAttributeChanged(AXTree* tree, + AXNode* node, + AXFloatAttribute attr, + float old_value, + float new_value) override; + void OnBoolAttributeChanged(AXTree* tree, + AXNode* node, + AXBoolAttribute attr, + bool new_value) override; + void OnIntListAttributeChanged( + AXTree* tree, + AXNode* node, + AXIntListAttribute attr, + const std::vector<int32_t>& old_value, + const std::vector<int32_t>& new_value) override; + void OnStringListAttributeChanged( + AXTree* tree, + AXNode* node, + AXStringListAttribute attr, + const std::vector<std::string>& old_value, + const std::vector<std::string>& new_value) override; + void OnTreeDataChanged(AXTree* tree, + const ui::AXTreeData& old_data, + const ui::AXTreeData& new_data) override; + void OnNodeWillBeDeleted(AXTree* tree, AXNode* node) override; + void OnSubtreeWillBeDeleted(AXTree* tree, AXNode* node) override; + void OnNodeWillBeReparented(AXTree* tree, AXNode* node) override; + void OnSubtreeWillBeReparented(AXTree* tree, AXNode* node) override; + void OnNodeCreated(AXTree* tree, AXNode* node) override; + void OnNodeReparented(AXTree* tree, AXNode* node) override; + void OnNodeChanged(AXTree* tree, AXNode* node) override; + void OnAtomicUpdateFinished(AXTree* tree, + bool root_changed, + const std::vector<Change>& changes) override; + + private: + AXTree* tree_; // Not owned. + std::map<AXNode*, std::set<Event>> tree_events_; +}; + +} // namespace ui + +#endif // UI_ACCESSIBILITY_AX_EVENT_GENERATOR_H_ diff --git a/chromium/ui/accessibility/ax_event_generator_unittest.cc b/chromium/ui/accessibility/ax_event_generator_unittest.cc new file mode 100644 index 00000000000..c9bbb07ccf0 --- /dev/null +++ b/chromium/ui/accessibility/ax_event_generator_unittest.cc @@ -0,0 +1,567 @@ +// 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/accessibility/ax_event_generator.h" + +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/ax_node.h" +#include "ui/accessibility/ax_serializable_tree.h" +#include "ui/accessibility/ax_tree_serializer.h" + +namespace ui { + +namespace { + +std::string DumpEvents(AXEventGenerator* generator) { + std::vector<std::string> event_strs; + for (auto targeted_event : *generator) { + const char* event_name; + switch (targeted_event.event) { + case AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED: + event_name = "ACTIVE_DESCENDANT_CHANGED"; + break; + case AXEventGenerator::Event::ALERT: + event_name = "ALERT"; + break; + case AXEventGenerator::Event::CHECKED_STATE_CHANGED: + event_name = "CHECKED_STATE_CHANGED"; + break; + case AXEventGenerator::Event::CHILDREN_CHANGED: + event_name = "CHILDREN_CHANGED"; + break; + case AXEventGenerator::Event::COLLAPSED: + event_name = "COLLAPSED"; + break; + case AXEventGenerator::Event::DESCRIPTION_CHANGED: + event_name = "DESCRIPTION_CHANGED"; + break; + case AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED: + event_name = "DOCUMENT_SELECTION_CHANGED"; + break; + case AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED: + event_name = "DOCUMENT_TITLE_CHANGED"; + break; + case AXEventGenerator::Event::EXPANDED: + event_name = "EXPANDED"; + break; + case AXEventGenerator::Event::INVALID_STATUS_CHANGED: + event_name = "INVALID_STATUS_CHANGED"; + break; + case AXEventGenerator::Event::LIVE_REGION_CHANGED: + event_name = "LIVE_REGION_CHANGED"; + break; + case AXEventGenerator::Event::LIVE_REGION_CREATED: + event_name = "LIVE_REGION_CREATED"; + break; + case AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED: + event_name = "LIVE_REGION_NODE_CHANGED"; + break; + case AXEventGenerator::Event::LOAD_COMPLETE: + event_name = "LOAD_COMPLETE"; + break; + case AXEventGenerator::Event::NAME_CHANGED: + event_name = "NAME_CHANGED"; + break; + case AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED: + event_name = "OTHER_ATTRIBUTE_CHANGED"; + break; + case AXEventGenerator::Event::ROLE_CHANGED: + event_name = "ROLE_CHANGED"; + break; + case AXEventGenerator::Event::ROW_COUNT_CHANGED: + event_name = "ROW_COUNT_CHANGED"; + break; + case AXEventGenerator::Event::SCROLL_POSITION_CHANGED: + event_name = "SCROLL_POSITION_CHANGED"; + break; + case AXEventGenerator::Event::SELECTED_CHANGED: + event_name = "SELECTED_CHANGED"; + break; + case AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED: + event_name = "SELECTED_CHILDREN_CHANGED"; + break; + case AXEventGenerator::Event::STATE_CHANGED: + event_name = "STATE_CHANGED"; + break; + case AXEventGenerator::Event::VALUE_CHANGED: + event_name = "VALUE_CHANGED"; + break; + default: + NOTREACHED(); + event_name = "UNKNOWN"; + break; + } + event_strs.push_back( + base::StringPrintf("%s on %d", event_name, targeted_event.node->id())); + } + + // The order of events is arbitrary, so just sort the strings + // alphabetically to make the test output predictable. + std::sort(event_strs.begin(), event_strs.end()); + + return base::JoinString(event_strs, ", "); +} + +} // namespace + +TEST(AXEventGeneratorTest, LoadCompleteSameTree) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.has_tree_data = true; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate load_complete_update = initial_state; + load_complete_update.tree_data.loaded = true; + EXPECT_TRUE(tree.Unserialize(load_complete_update)); + EXPECT_EQ("LOAD_COMPLETE on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, LoadCompleteNewTree) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.has_tree_data = true; + initial_state.tree_data.loaded = true; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate load_complete_update; + load_complete_update.root_id = 2; + load_complete_update.nodes.resize(1); + load_complete_update.nodes[0].id = 2; + load_complete_update.has_tree_data = true; + load_complete_update.tree_data.loaded = true; + EXPECT_TRUE(tree.Unserialize(load_complete_update)); + EXPECT_EQ("LOAD_COMPLETE on 2", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, DocumentSelectionChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.has_tree_data = true; + initial_state.tree_data.sel_focus_object_id = 1; + initial_state.tree_data.sel_focus_offset = 1; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.tree_data.sel_focus_offset = 2; + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("DOCUMENT_SELECTION_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, DocumentTitleChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.has_tree_data = true; + initial_state.tree_data.title = "Before"; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.tree_data.title = "After"; + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("DOCUMENT_TITLE_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, ExpandedAndRowCount) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(4); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ui::AX_ROLE_ROOT_WEB_AREA; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[0].child_ids.push_back(4); + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ui::AX_ROLE_TABLE; + initial_state.nodes[1].child_ids.push_back(3); + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ui::AX_ROLE_ROW; + initial_state.nodes[3].id = 4; + initial_state.nodes[3].role = ui::AX_ROLE_POP_UP_BUTTON; + initial_state.nodes[3].AddState(ui::AX_STATE_EXPANDED); + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[2].AddState(ui::AX_STATE_EXPANDED); + update.nodes[3].state = 0; + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ( + "COLLAPSED on 4, " + "EXPANDED on 3, " + "ROW_COUNT_CHANGED on 2, " + "STATE_CHANGED on 3, " + "STATE_CHANGED on 4", + DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, SelectedAndSelectedChildren) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(4); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ui::AX_ROLE_ROOT_WEB_AREA; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[0].child_ids.push_back(4); + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ui::AX_ROLE_MENU; + initial_state.nodes[1].child_ids.push_back(3); + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ui::AX_ROLE_MENU_ITEM; + initial_state.nodes[3].id = 4; + initial_state.nodes[3].role = ui::AX_ROLE_LIST_BOX_OPTION; + initial_state.nodes[3].AddState(ui::AX_STATE_SELECTED); + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[2].AddState(ui::AX_STATE_SELECTED); + update.nodes[3].state = 0; + 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", + DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, StringValueChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ui::AX_ROLE_TEXT_FIELD; + initial_state.nodes[0].AddStringAttribute(ui::AX_ATTR_VALUE, "Before"); + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].string_attributes.clear(); + update.nodes[0].AddStringAttribute(ui::AX_ATTR_VALUE, "After"); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("VALUE_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, FloatValueChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ui::AX_ROLE_SLIDER; + initial_state.nodes[0].AddFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, 1.0); + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].float_attributes.clear(); + update.nodes[0].AddFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, 2.0); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("VALUE_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, InvalidStatusChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ui::AX_ROLE_TEXT_FIELD; + initial_state.nodes[0].AddStringAttribute(ui::AX_ATTR_VALUE, "Text"); + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].AddIntAttribute(ui::AX_ATTR_INVALID_STATE, + ui::AX_INVALID_STATE_SPELLING); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("INVALID_STATUS_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, AddLiveRegionAttribute) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].AddStringAttribute(ui::AX_ATTR_LIVE_STATUS, "polite"); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("LIVE_REGION_CREATED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, CheckedStateChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ui::AX_ROLE_CHECK_BOX; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].AddIntAttribute(ui::AX_ATTR_CHECKED_STATE, + ui::AX_CHECKED_STATE_TRUE); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("CHECKED_STATE_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, ActiveDescendantChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(3); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].role = ui::AX_ROLE_LIST_BOX; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[0].child_ids.push_back(3); + initial_state.nodes[0].AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, 2); + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ui::AX_ROLE_LIST_BOX_OPTION; + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ui::AX_ROLE_LIST_BOX_OPTION; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].int_attributes.clear(); + update.nodes[0].AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, 3); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("ACTIVE_DESCENDANT_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, CreateAlertAndLiveRegion) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes.resize(3); + update.nodes[0].child_ids.push_back(2); + update.nodes[0].child_ids.push_back(3); + update.nodes[1].id = 2; + update.nodes[1].AddStringAttribute(ui::AX_ATTR_LIVE_STATUS, "polite"); + update.nodes[2].id = 3; + update.nodes[2].role = ui::AX_ROLE_ALERT; + update.nodes[2].AddStringAttribute(ui::AX_ATTR_LIVE_STATUS, "polite"); + + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ( + "ALERT on 3, " + "CHILDREN_CHANGED on 1, " + "LIVE_REGION_CREATED on 2", + DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, LiveRegionChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(3); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].AddStringAttribute(ui::AX_ATTR_LIVE_STATUS, "polite"); + initial_state.nodes[0].AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_STATUS, + "polite"); + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[0].child_ids.push_back(3); + initial_state.nodes[1].id = 2; + initial_state.nodes[1].role = ui::AX_ROLE_STATIC_TEXT; + initial_state.nodes[1].AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_STATUS, + "polite"); + initial_state.nodes[1].AddStringAttribute(ui::AX_ATTR_NAME, "Before 1"); + initial_state.nodes[2].id = 3; + initial_state.nodes[2].role = ui::AX_ROLE_STATIC_TEXT; + initial_state.nodes[2].AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_STATUS, + "polite"); + initial_state.nodes[2].AddStringAttribute(ui::AX_ATTR_NAME, "Before 2"); + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[1].string_attributes.clear(); + update.nodes[1].AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_STATUS, + "polite"); + update.nodes[1].AddStringAttribute(ui::AX_ATTR_NAME, "After 1"); + update.nodes[2].string_attributes.clear(); + update.nodes[2].AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_STATUS, + "polite"); + update.nodes[2].AddStringAttribute(ui::AX_ATTR_NAME, "After 2"); + + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ( + "LIVE_REGION_CHANGED on 1, " + "LIVE_REGION_NODE_CHANGED on 2, " + "LIVE_REGION_NODE_CHANGED on 3, " + "NAME_CHANGED on 2, " + "NAME_CHANGED on 3", + DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, AddChild) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(2); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[1].id = 2; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes.resize(3); + update.nodes[0].child_ids.push_back(3); + update.nodes[2].id = 3; + + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("CHILDREN_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, RemoveChild) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(3); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[0].child_ids.push_back(3); + initial_state.nodes[1].id = 2; + initial_state.nodes[2].id = 3; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes.resize(2); + update.nodes[0].child_ids.clear(); + update.nodes[0].child_ids.push_back(2); + + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("CHILDREN_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, ReorderChildren) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(3); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[0].child_ids.push_back(3); + initial_state.nodes[1].id = 2; + initial_state.nodes[2].id = 3; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].child_ids.clear(); + update.nodes[0].child_ids.push_back(3); + update.nodes[0].child_ids.push_back(2); + + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("CHILDREN_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, ScrollPositionChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].AddIntAttribute(ui::AX_ATTR_SCROLL_Y, 10); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("SCROLL_POSITION_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, OtherAttributeChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(6); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].child_ids.push_back(2); + initial_state.nodes[0].child_ids.push_back(3); + initial_state.nodes[0].child_ids.push_back(4); + initial_state.nodes[0].child_ids.push_back(5); + initial_state.nodes[0].child_ids.push_back(6); + initial_state.nodes[1].id = 2; + initial_state.nodes[2].id = 3; + initial_state.nodes[3].id = 4; + initial_state.nodes[4].id = 5; + initial_state.nodes[5].id = 6; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[1].AddStringAttribute(ui::AX_ATTR_LANGUAGE, "de"); + update.nodes[2].AddIntAttribute(ui::AX_ATTR_ARIA_CELL_COLUMN_INDEX, 7); + update.nodes[3].AddFloatAttribute(ui::AX_ATTR_FONT_SIZE, 12.0f); + update.nodes[4].AddBoolAttribute(ui::AX_ATTR_MODAL, true); + std::vector<int> ids = {2}; + update.nodes[5].AddIntListAttribute(ui::AX_ATTR_CONTROLS_IDS, ids); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ( + "OTHER_ATTRIBUTE_CHANGED on 2, " + "OTHER_ATTRIBUTE_CHANGED on 3, " + "OTHER_ATTRIBUTE_CHANGED on 4, " + "OTHER_ATTRIBUTE_CHANGED on 5, " + "OTHER_ATTRIBUTE_CHANGED on 6", + DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, NameChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].AddStringAttribute(ui::AX_ATTR_NAME, "Hello"); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("NAME_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, DescriptionChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].AddStringAttribute(ui::AX_ATTR_DESCRIPTION, "Hello"); + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("DESCRIPTION_CHANGED on 1", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, RoleChanged) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate update = initial_state; + update.nodes[0].role = ui::AX_ROLE_CHECK_BOX; + EXPECT_TRUE(tree.Unserialize(update)); + EXPECT_EQ("ROLE_CHANGED on 1", DumpEvents(&event_generator)); +} + +} // namespace ui diff --git a/chromium/ui/accessibility/ax_mode_observer.h b/chromium/ui/accessibility/ax_mode_observer.h new file mode 100644 index 00000000000..c1fa63c9129 --- /dev/null +++ b/chromium/ui/accessibility/ax_mode_observer.h @@ -0,0 +1,23 @@ +// 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_ACCESSIBILITY_PLATFORM_AX_MODE_OBSERVER_H_ +#define UI_ACCESSIBILITY_PLATFORM_AX_MODE_OBSERVER_H_ + +#include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_modes.h" + +namespace ui { + +class AX_EXPORT AXModeObserver { + public: + virtual ~AXModeObserver() {} + + // Notifies when accessibility mode changes. + virtual void OnAXModeAdded(ui::AXMode mode) = 0; +}; + +} // namespace ui + +#endif // UI_ACCESSIBILITY_PLATFORM_AX_MODE_OBSERVER_H_
\ No newline at end of file diff --git a/chromium/ui/accessibility/ax_modes.h b/chromium/ui/accessibility/ax_modes.h new file mode 100644 index 00000000000..b6015763e1e --- /dev/null +++ b/chromium/ui/accessibility/ax_modes.h @@ -0,0 +1,92 @@ +// 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_ACCESSIBILITY_PLATFORM_AX_MODE_H_ +#define UI_ACCESSIBILITY_PLATFORM_AX_MODE_H_ + +namespace ui { + +class AXMode { + public: + // Native accessibility APIs, specific to each platform, are enabled. + // When this mode is set that indicates the presence of a third-party + // client accessing Chrome via accessibility APIs. However, unless one + // of the modes below is set, the contents of web pages will not be + // accessible. + static constexpr uint32_t kNativeAPIs = 1 << 0; + + // The renderer process will generate an accessibility tree containing + // basic information about all nodes, including role, name, value, + // state, and location. This is the minimum mode required in order for + // web contents to be accessible, and the remaining modes are meaningless + // unless this one is set. + // + // Note that sometimes this mode will be set when kNativeAPI is not, when the + // content layer embedder is providing accessibility support via some other + // mechanism other than what's implemented in content/browser. + static constexpr uint32_t kWebContents = 1 << 1; + + // The accessibility tree will contain inline text boxes, which are + // necessary to expose information about line breaks and word boundaries. + // Without this mode, you can retrieve the plaintext value of a text field + // but not the information about how it's broken down into lines. + // + // Note that when this mode is off it's still possible to request inline + // text boxes for a specific node on-demand, asynchronously. + static constexpr uint32_t kInlineTextBoxes = 1 << 2; + + // The accessibility tree will contain extra accessibility + // attributes typically only needed by screen readers and other + // assistive technology for blind users. Examples include text style + // attributes, table cell information, live region properties, range + // values, and relationship attributes. + static constexpr uint32_t kScreenReader = 1 << 3; + + // The accessibility tree will contain the HTML tag name and HTML attributes + // for all accessibility nodes that come from web content. + static constexpr uint32_t kHTML = 1 << 4; + + constexpr AXMode() : flags_(0) {} + constexpr AXMode(uint32_t flags) : flags_(flags) {} + + bool has_mode(uint32_t flag) const { return (flags_ & flag) > 0; } + + void set_mode(uint32_t flag, bool value) { + flags_ = value ? (flags_ | flag) : (flags_ & ~flag); + } + + uint32_t mode() const { return flags_; } + + bool operator==(AXMode rhs) const { + if (flags_ == rhs.flags_) + return true; + return false; + } + + bool is_mode_off() const { return flags_ == 0; } + + bool operator!=(AXMode rhs) const { return !(*this == rhs); } + + AXMode& operator|=(const AXMode& rhs) { + flags_ |= rhs.flags_; + return *this; + } + + private: + uint32_t flags_; +}; + +static constexpr AXMode kAXModeWebContentsOnly(AXMode::kWebContents | + AXMode::kInlineTextBoxes | + AXMode::kScreenReader | + AXMode::kHTML); + +static constexpr AXMode kAXModeComplete(AXMode::kNativeAPIs | + AXMode::kWebContents | + AXMode::kInlineTextBoxes | + AXMode::kScreenReader | AXMode::kHTML); + +} // namespace ui + +#endif // UI_ACCESSIBILITY_PLATFORM_AX_MODE_H_ diff --git a/chromium/ui/accessibility/ax_node_data.cc b/chromium/ui/accessibility/ax_node_data.cc index b06b4a29265..5509131d8ae 100644 --- a/chromium/ui/accessibility/ax_node_data.cc +++ b/chromium/ui/accessibility/ax_node_data.cc @@ -35,7 +35,7 @@ std::string StateBitfieldToString(uint32_t state) { std::string str; for (uint32_t i = AX_STATE_NONE + 1; i <= AX_STATE_LAST; ++i) { if (IsFlagSet(state, i)) - str += " " + base::ToUpperASCII(ToString(static_cast<AXState>(i))); + str += " " + base::ToUpperASCII(ui::ToString(static_cast<AXState>(i))); } return str; } @@ -44,7 +44,7 @@ std::string ActionsBitfieldToString(uint32_t actions) { std::string str; for (uint32_t i = AX_ACTION_NONE + 1; i <= AX_ACTION_LAST; ++i) { if (IsFlagSet(actions, i)) { - str += ToString(static_cast<AXAction>(i)); + str += ui::ToString(static_cast<AXAction>(i)); actions = ModifyFlag(actions, i, false); str += actions ? "," : ""; } @@ -525,15 +525,21 @@ void AXNodeData::AddAction(AXAction action_enum) { case AX_ACTION_GET_IMAGE_DATA: case AX_ACTION_HIT_TEST: case AX_ACTION_INCREMENT: + case AX_ACTION_LOAD_INLINE_TEXT_BOXES: case AX_ACTION_REPLACE_SELECTED_TEXT: case AX_ACTION_SCROLL_TO_MAKE_VISIBLE: case AX_ACTION_SCROLL_TO_POINT: - case AX_ACTION_SET_ACCESSIBILITY_FOCUS: case AX_ACTION_SET_SCROLL_OFFSET: case AX_ACTION_SET_SELECTION: case AX_ACTION_SET_SEQUENTIAL_FOCUS_NAVIGATION_STARTING_POINT: case AX_ACTION_SET_VALUE: case AX_ACTION_SHOW_CONTEXT_MENU: + case AX_ACTION_SCROLL_BACKWARD: + case AX_ACTION_SCROLL_FORWARD: + case AX_ACTION_SCROLL_UP: + case AX_ACTION_SCROLL_DOWN: + case AX_ACTION_SCROLL_LEFT: + case AX_ACTION_SCROLL_RIGHT: break; } @@ -909,10 +915,13 @@ std::string AXNodeData::ToString() const { for (size_t i = 0; i < bool_attributes.size(); ++i) { std::string value = bool_attributes[i].second ? "true" : "false"; switch (bool_attributes[i].first) { + case AX_ATTR_EDITABLE_ROOT: + result += " editable_root=" + value; + break; case AX_ATTR_LIVE_ATOMIC: result += " atomic=" + value; break; - case AX_ATTR_LIVE_BUSY: + case AX_ATTR_BUSY: result += " busy=" + value; break; case AX_ATTR_CONTAINER_LIVE_ATOMIC: @@ -930,6 +939,9 @@ std::string AXNodeData::ToString() const { case AX_ATTR_MODAL: result += " modal=" + value; break; + case AX_ATTR_SCROLLABLE: + result += " scrollable=" + value; + break; case AX_BOOL_ATTRIBUTE_NONE: break; } @@ -977,6 +989,8 @@ std::string AXNodeData::ToString() const { types_str += "text_match&"; if (type & AX_MARKER_TYPE_ACTIVE_SUGGESTION) types_str += "active_suggestion&"; + if (type & AX_MARKER_TYPE_SUGGESTION) + types_str += "suggestion&"; if (!types_str.empty()) types_str = types_str.substr(0, types_str.size() - 1); diff --git a/chromium/ui/accessibility/ax_node_position_unittest.cc b/chromium/ui/accessibility/ax_node_position_unittest.cc index c6641e0ba2f..6fdbe6d15a4 100644 --- a/chromium/ui/accessibility/ax_node_position_unittest.cc +++ b/chromium/ui/accessibility/ax_node_position_unittest.cc @@ -419,10 +419,18 @@ TEST_F(AXPositionTest, AtEndOfLineWithTextPosition) { ASSERT_NE(nullptr, text_position); EXPECT_TRUE(text_position->AtEndOfLine()); + // A "before text" position anchored at the line break should visually be the + // same as a text position at the end of the previous line. text_position = AXNodePosition::CreateTextPosition( tree_.data().tree_id, line_break_.id, 0 /* text_offset */, AX_TEXT_AFFINITY_DOWNSTREAM); ASSERT_NE(nullptr, text_position); + EXPECT_TRUE(text_position->AtEndOfLine()); + + text_position = AXNodePosition::CreateTextPosition( + tree_.data().tree_id, line_break_.id, 1 /* text_offset */, + AX_TEXT_AFFINITY_DOWNSTREAM); + ASSERT_NE(nullptr, text_position); EXPECT_FALSE(text_position->AtEndOfLine()); text_position = AXNodePosition::CreateTextPosition( @@ -1142,10 +1150,12 @@ TEST_F(AXPositionTest, CreatePreviousTextAnchorPosition) { TEST_F(AXPositionTest, CreateNextAndPreviousCharacterPositionWithNullPosition) { TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); - TestPositionType test_position = null_position->CreateNextCharacterPosition(); + TestPositionType test_position = null_position->CreateNextCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); - test_position = null_position->CreatePreviousCharacterPosition(); + test_position = null_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -1155,7 +1165,8 @@ TEST_F(AXPositionTest, CreateNextCharacterPosition) { tree_.data().tree_id, inline_box1_.id, 4 /* text_offset */, AX_TEXT_AFFINITY_DOWNSTREAM); ASSERT_NE(nullptr, text_position); - TestPositionType test_position = text_position->CreateNextCharacterPosition(); + TestPositionType test_position = text_position->CreateNextCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -1165,19 +1176,34 @@ TEST_F(AXPositionTest, CreateNextCharacterPosition) { tree_.data().tree_id, inline_box1_.id, 5 /* text_offset */, AX_TEXT_AFFINITY_DOWNSTREAM); ASSERT_NE(nullptr, text_position); - test_position = text_position->CreateNextCharacterPosition(); + test_position = text_position->CreateNextCharacterPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + EXPECT_NE(nullptr, test_position); + EXPECT_TRUE(test_position->IsTextPosition()); + EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); + EXPECT_EQ(5, test_position->text_offset()); + test_position = text_position->CreateNextCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); - test_position = test_position->CreateNextCharacterPosition(); + test_position = test_position->CreateNextCharacterPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + EXPECT_NE(nullptr, test_position); + EXPECT_TRUE(test_position->IsTextPosition()); + EXPECT_EQ(line_break_.id, test_position->anchor_id()); + EXPECT_EQ(0, test_position->text_offset()); + test_position = test_position->CreateNextCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); - test_position = test_position->CreateNextCharacterPosition(); + test_position = test_position->CreateNextCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); @@ -1187,7 +1213,14 @@ TEST_F(AXPositionTest, CreateNextCharacterPosition) { tree_.data().tree_id, check_box_.id, 9 /* text_offset */, AX_TEXT_AFFINITY_DOWNSTREAM); ASSERT_NE(nullptr, text_position); - test_position = text_position->CreateNextCharacterPosition(); + test_position = text_position->CreateNextCharacterPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + EXPECT_NE(nullptr, test_position); + EXPECT_TRUE(test_position->IsTextPosition()); + EXPECT_EQ(check_box_.id, test_position->anchor_id()); + EXPECT_EQ(9, test_position->text_offset()); + test_position = text_position->CreateNextCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -1197,7 +1230,8 @@ TEST_F(AXPositionTest, CreateNextCharacterPosition) { tree_.data().tree_id, text_field_.id, 0 /* text_offset */, AX_TEXT_AFFINITY_UPSTREAM); ASSERT_NE(nullptr, text_position); - test_position = text_position->CreateNextCharacterPosition(); + test_position = text_position->CreateNextCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_field_.id, test_position->anchor_id()); @@ -1212,7 +1246,8 @@ TEST_F(AXPositionTest, CreatePreviousCharacterPosition) { AX_TEXT_AFFINITY_DOWNSTREAM); ASSERT_NE(nullptr, text_position); TestPositionType test_position = - text_position->CreatePreviousCharacterPosition(); + text_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); @@ -1222,19 +1257,34 @@ TEST_F(AXPositionTest, CreatePreviousCharacterPosition) { tree_.data().tree_id, inline_box2_.id, 0 /* text_offset */, AX_TEXT_AFFINITY_DOWNSTREAM); ASSERT_NE(nullptr, text_position); - test_position = text_position->CreatePreviousCharacterPosition(); + test_position = text_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + EXPECT_NE(nullptr, test_position); + EXPECT_TRUE(test_position->IsTextPosition()); + EXPECT_EQ(inline_box2_.id, test_position->anchor_id()); + EXPECT_EQ(0, test_position->text_offset()); + test_position = text_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(line_break_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); - test_position = test_position->CreatePreviousCharacterPosition(); + test_position = test_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + EXPECT_NE(nullptr, test_position); + EXPECT_TRUE(test_position->IsTextPosition()); + EXPECT_EQ(line_break_.id, test_position->anchor_id()); + EXPECT_EQ(0, test_position->text_offset()); + test_position = test_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); EXPECT_EQ(5, test_position->text_offset()); - test_position = test_position->CreatePreviousCharacterPosition(); + test_position = test_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); @@ -1244,7 +1294,14 @@ TEST_F(AXPositionTest, CreatePreviousCharacterPosition) { tree_.data().tree_id, inline_box1_.id, 0 /* text_offset */, AX_TEXT_AFFINITY_DOWNSTREAM); ASSERT_NE(nullptr, text_position); - test_position = text_position->CreatePreviousCharacterPosition(); + test_position = text_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + EXPECT_NE(nullptr, test_position); + EXPECT_TRUE(test_position->IsTextPosition()); + EXPECT_EQ(inline_box1_.id, test_position->anchor_id()); + EXPECT_EQ(0, test_position->text_offset()); + test_position = text_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(check_box_.id, test_position->anchor_id()); @@ -1254,21 +1311,25 @@ TEST_F(AXPositionTest, CreatePreviousCharacterPosition) { tree_.data().tree_id, text_field_.id, 1 /* text_offset */, AX_TEXT_AFFINITY_UPSTREAM); ASSERT_NE(nullptr, text_position); - test_position = text_position->CreatePreviousCharacterPosition(); + test_position = text_position->CreatePreviousCharacterPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsTextPosition()); EXPECT_EQ(text_field_.id, test_position->anchor_id()); EXPECT_EQ(0, test_position->text_offset()); + // Affinity should have been reset to downstream. EXPECT_EQ(AX_TEXT_AFFINITY_DOWNSTREAM, test_position->affinity()); } TEST_F(AXPositionTest, CreateNextAndPreviousWordStartPositionWithNullPosition) { TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); - TestPositionType test_position = null_position->CreateNextWordStartPosition(); + TestPositionType test_position = null_position->CreateNextWordStartPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); - test_position = null_position->CreatePreviousWordStartPosition(); + test_position = null_position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -1276,10 +1337,12 @@ TEST_F(AXPositionTest, CreateNextAndPreviousWordStartPositionWithNullPosition) { TEST_F(AXPositionTest, CreateNextAndPreviousWordEndPositionWithNullPosition) { TestPositionType null_position = AXNodePosition::CreateNullPosition(); ASSERT_NE(nullptr, null_position); - TestPositionType test_position = null_position->CreateNextWordEndPosition(); + TestPositionType test_position = null_position->CreateNextWordEndPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); - test_position = null_position->CreatePreviousWordEndPosition(); + test_position = null_position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::CrossBoundary); EXPECT_NE(nullptr, test_position); EXPECT_TRUE(test_position->IsNullPosition()); } @@ -1488,11 +1551,12 @@ TEST_P(AXPositionTestWithParam, TraverseTreeStartingWithAffinityUpstream) { // INSTANTIATE_TEST_CASE_P( - CreateNextWordStartPosition, + CreateNextWordStartPositionWithBoundaryBehaviorCrossBoundary, AXPositionTestWithParam, testing::Values( TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordStartPosition(); + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -1516,7 +1580,8 @@ INSTANTIATE_TEST_CASE_P( "1\nLine <2>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordStartPosition(); + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -1528,7 +1593,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Line 1\nLine <2>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordStartPosition(); + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -1540,7 +1606,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Line <2>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordStartPosition(); + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -1549,11 +1616,125 @@ INSTANTIATE_TEST_CASE_P( "NullPosition"}})); INSTANTIATE_TEST_CASE_P( - CreatePreviousWordStartPosition, + CreateNextWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Button<C>heck boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=12 " + "affinity=downstream annotated_text=ButtonCheck <b>oxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=20 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "<1>\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=22 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=27 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine <2>", + "TextPosition tree_id=0 anchor_id=1 text_offset=28 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=5 " + "affinity=downstream annotated_text=Line <1>\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=12 " + "affinity=downstream annotated_text=Line 1\nLine <2>", + "TextPosition tree_id=0 anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=5 " + "affinity=downstream annotated_text=Line <1>", + "TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=5 " + "affinity=downstream annotated_text=Line <2>", + "TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, AXPositionTestWithParam, testing::Values( TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordStartPosition(); + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=5 " + "affinity=downstream annotated_text=Line <1>", + "TextPosition tree_id=0 anchor_id=5 text_offset=5 " + "affinity=downstream annotated_text=Line <1>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=5 " + "affinity=downstream annotated_text=Line <2>", + "TextPosition tree_id=0 anchor_id=9 text_offset=5 " + "affinity=downstream annotated_text=Line <2>"}})); + +INSTANTIATE_TEST_CASE_P( + CreatePreviousWordStartPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 28 /* text_offset at end of root. */, @@ -1580,7 +1761,8 @@ INSTANTIATE_TEST_CASE_P( "1\nLine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordStartPosition(); + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -1600,7 +1782,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=<B>utton", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordStartPosition(); + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -1614,7 +1797,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=<B>utton", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordStartPosition(); + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -1633,11 +1817,128 @@ INSTANTIATE_TEST_CASE_P( "NullPosition"}})); INSTANTIATE_TEST_CASE_P( - CreateNextWordEndPosition, + CreatePreviousWordStartPositionWithBoundaryBehaviorStopAtAnchorBoundary, AXPositionTestWithParam, testing::Values( TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordEndPosition(); + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 28 /* text_offset at end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=27 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine <2>", + "TextPosition tree_id=0 anchor_id=1 text_offset=22 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=20 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "<1>\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=12 " + "affinity=downstream annotated_text=ButtonCheck <b>oxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Button<C>heck boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=12 " + "affinity=downstream annotated_text=Line 1\nLine <2>", + "TextPosition tree_id=0 anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=5 " + "affinity=downstream annotated_text=Line <1>\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition tree_id=0 anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_CASE_P( + CreatePreviousWordStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 28 /* text_offset at end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=27 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine <2>", + "TextPosition tree_id=0 anchor_id=1 text_offset=27 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine <2>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=12 " + "affinity=downstream annotated_text=Line 1\nLine <2>", + "TextPosition tree_id=0 anchor_id=4 text_offset=12 " + "affinity=downstream annotated_text=Line 1\nLine <2>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=5 " + "affinity=downstream annotated_text=Line <1>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextWordEndPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -1664,7 +1965,8 @@ INSTANTIATE_TEST_CASE_P( "1\nLine 2<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordEndPosition(); + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -1678,7 +1980,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Line 1\nLine 2<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordEndPosition(); + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -1692,7 +1995,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Line 2<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextWordEndPosition(); + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -1701,11 +2005,130 @@ INSTANTIATE_TEST_CASE_P( "NullPosition"}})); INSTANTIATE_TEST_CASE_P( - CreatePreviousWordEndPosition, + CreateNextWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary, AXPositionTestWithParam, testing::Values( TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition(); + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Button<C>heck boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=11 " + "affinity=downstream annotated_text=ButtonCheck< >boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=19 " + "affinity=downstream annotated_text=ButtonCheck boxLine< " + ">1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=26 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine< >2", + "TextPosition tree_id=0 anchor_id=1 text_offset=28 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine 2<>", + "TextPosition tree_id=0 anchor_id=1 text_offset=28 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=4 " + "affinity=downstream annotated_text=Line< >1\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=11 " + "affinity=downstream annotated_text=Line 1\nLine< >2", + "TextPosition tree_id=0 anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition tree_id=0 anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1", + "TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 6 /* text_offset before "Check". */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=11 " + "affinity=downstream annotated_text=ButtonCheck< >boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=11 " + "affinity=downstream annotated_text=ButtonCheck< >boxLine " + "1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=4 " + "affinity=downstream annotated_text=Line< >1\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=4 " + "affinity=downstream annotated_text=Line< >1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1", + "TextPosition tree_id=0 anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=4 " + "affinity=downstream annotated_text=Line< >2"}})); + +INSTANTIATE_TEST_CASE_P( + CreatePreviousWordEndPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 28 /* text_offset at end of root. */, @@ -1729,7 +2152,8 @@ INSTANTIATE_TEST_CASE_P( "1\nLine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition(); + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -1747,7 +2171,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Button<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition(); + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -1761,7 +2186,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Button<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousWordEndPosition(); + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -1778,11 +2204,116 @@ INSTANTIATE_TEST_CASE_P( "NullPosition"}})); INSTANTIATE_TEST_CASE_P( - CreateNextLineStartPosition, + CreatePreviousWordEndPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 28 /* text_offset at end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=26 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine< >2", + "TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=19 " + "affinity=downstream annotated_text=ButtonCheck boxLine< " + ">1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=11 " + "affinity=downstream annotated_text=ButtonCheck< >boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=6 " + "affinity=downstream annotated_text=Button<C>heck boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=11 " + "affinity=downstream annotated_text=Line 1\nLine< >2", + "TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=4 " + "affinity=downstream annotated_text=Line< >1\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1", + "TextPosition tree_id=0 anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_CASE_P( + CreatePreviousWordEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, AXPositionTestWithParam, testing::Values( TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineStartPosition(); + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 28 /* text_offset at end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=28 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1", + "TextPosition tree_id=0 anchor_id=5 text_offset=4 " + "affinity=downstream annotated_text=Line< >1"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousWordEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=4 " + "affinity=downstream annotated_text=Line< >2"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextLineStartPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -1794,7 +2325,8 @@ INSTANTIATE_TEST_CASE_P( "1\n<L>ine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineStartPosition(); + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -1802,7 +2334,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Line 1\n<L>ine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineStartPosition(); + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::CrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -1810,18 +2343,105 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=<L>ine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineStartPosition(); + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::CrossBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"NullPosition"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=22 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=28 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, {"NullPosition"}})); INSTANTIATE_TEST_CASE_P( - CreatePreviousLineStartPosition, + CreatePreviousLineStartPositionWithBoundaryBehaviorCrossBoundary, AXPositionTestWithParam, testing::Values( TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineStartPosition(); + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 28 /* text_offset at the end of root. */, @@ -1836,7 +2456,8 @@ INSTANTIATE_TEST_CASE_P( "1\nLine 2", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineStartPosition(); + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -1848,7 +2469,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=<B>utton", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineStartPosition(); + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::CrossBoundary); }), STATIC_TEXT1_ID, 5 /* text_offset */, @@ -1858,7 +2480,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=<B>utton", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineStartPosition(); + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -1871,11 +2494,114 @@ INSTANTIATE_TEST_CASE_P( "NullPosition"}})); INSTANTIATE_TEST_CASE_P( - CreateNextLineEndPosition, + CreatePreviousLineStartPositionWithBoundaryBehaviorStopAtAnchorBoundary, AXPositionTestWithParam, testing::Values( TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineEndPosition(); + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 28 /* text_offset at the end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=22 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=0 " + "affinity=downstream annotated_text=<B>uttonCheck boxLine " + "1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition tree_id=0 anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_CASE_P( + CreatePreviousLineStartPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 28 /* text_offset at the end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=22 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=22 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\n<L>ine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=7 " + "affinity=downstream annotated_text=Line 1\n<L>ine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 5 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1", + "TextPosition tree_id=0 anchor_id=5 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineStartPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2", + "TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextLineEndPositionWithBoundaryBehaviorCrossBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 0 /* text_offset */, @@ -1890,7 +2616,8 @@ INSTANTIATE_TEST_CASE_P( "1\nLine 2<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineEndPosition(); + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 0 /* text_offset */, @@ -1900,7 +2627,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Line 1\nLine 2<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineEndPosition(); + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), STATIC_TEXT1_ID, 1 /* text_offset */, @@ -1910,7 +2638,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Line 2<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreateNextLineEndPosition(); + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -1919,7 +2648,112 @@ INSTANTIATE_TEST_CASE_P( "NullPosition"}})); INSTANTIATE_TEST_CASE_P( - CreatePreviousLineEndPosition, + CreateNextLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine 1" + "<\n>Line 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=28 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine 2<>", + "TextPosition tree_id=0 anchor_id=1 text_offset=28 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>", + "TextPosition tree_id=0 anchor_id=4 text_offset=13 " + "affinity=downstream annotated_text=Line 1\nLine 2<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_CASE_P( + CreateNextLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1<\n>Line 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + STATIC_TEXT1_ID, + 1 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition tree_id=0 anchor_id=5 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreateNextLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>", + "TextPosition tree_id=0 anchor_id=9 text_offset=6 " + "affinity=downstream annotated_text=Line 2<>"}})); + +INSTANTIATE_TEST_CASE_P( + CreatePreviousLineEndPositionWithBoundaryBehaviorCrossBoundary, AXPositionTestWithParam, testing::Values( // Note that for the first test, we can't go past the line ending at the @@ -1928,7 +2762,8 @@ INSTANTIATE_TEST_CASE_P( // the beginning of the first line in the text field, and so an infinite // recursion will occur. TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineEndPosition(); + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), ROOT_ID, 28 /* text_offset at end of root. */, @@ -1939,7 +2774,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=ButtonCheck box<L>ine " "1\nLine 2"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineEndPosition(); + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), TEXT_FIELD_ID, 13 /* text_offset at end of text field */, @@ -1949,7 +2785,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Check box<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineEndPosition(); + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 4 /* text_offset */, @@ -1959,7 +2796,8 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Check box<>", "NullPosition"}}, TestParam{base::BindRepeating([](const TestPositionType& position) { - return position->CreatePreviousLineEndPosition(); + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::CrossBoundary); }), INLINE_BOX2_ID, 0 /* text_offset */, @@ -1969,6 +2807,104 @@ INSTANTIATE_TEST_CASE_P( "affinity=downstream annotated_text=Check box<>", "NullPosition"}})); +INSTANTIATE_TEST_CASE_P( + CreatePreviousLineEndPositionWithBoundaryBehaviorStopAtAnchorBoundary, + AXPositionTestWithParam, + testing::Values( + // Note that for the first test, we can't go past the line ending at the + // check box to test for a position at start of anchor, because the tree + // position that is at the end of the check box is equivalent to the one + // that is at the beginning of the first line in the text field, and so + // an infinite recursion will occur. + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + ROOT_ID, + 28 /* text_offset at end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=15 " + "affinity=downstream annotated_text=ButtonCheck box<L>ine " + "1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + TEXT_FIELD_ID, + 13 /* text_offset at end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 1\nLine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopAtAnchorBoundary); + }), + INLINE_BOX2_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=9 text_offset=0 " + "affinity=downstream annotated_text=<L>ine 2"}})); + +INSTANTIATE_TEST_CASE_P( + CreatePreviousLineEndPositionWithBoundaryBehaviorStopIfAlreadyAtBoundary, + AXPositionTestWithParam, + testing::Values( + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + ROOT_ID, + 27 /* text_offset one before the end of root. */, + {"TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=1 text_offset=21 " + "affinity=downstream annotated_text=ButtonCheck boxLine " + "1<\n>Line 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + TEXT_FIELD_ID, + 12 /* text_offset one before the end of text field */, + {"TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2", + "TextPosition tree_id=0 anchor_id=4 text_offset=6 " + "affinity=downstream annotated_text=Line 1<\n>Line 2"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 4 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=6 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition tree_id=0 anchor_id=6 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}}, + TestParam{base::BindRepeating([](const TestPositionType& position) { + return position->CreatePreviousLineEndPosition( + AXBoundaryBehavior::StopIfAlreadyAtBoundary); + }), + INLINE_BOX2_ID, + 0 /* text_offset */, + {"TextPosition tree_id=0 anchor_id=6 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>", + "TextPosition tree_id=0 anchor_id=6 text_offset=6 " + "affinity=downstream annotated_text=Line 1<>"}})); + // // Tests for |AXRange|. // diff --git a/chromium/ui/accessibility/ax_position.h b/chromium/ui/accessibility/ax_position.h index c38d9511128..9e70b2521df 100644 --- a/chromium/ui/accessibility/ax_position.h +++ b/chromium/ui/accessibility/ax_position.h @@ -30,6 +30,15 @@ namespace ui { // a boundary has been reached. enum class AXPositionKind { NULL_POSITION, TREE_POSITION, TEXT_POSITION }; +// Defines how creating the next or previous position should behave whenever we +// are at or are crossing a boundary, such as at the start of an anchor, a word +// or a line. +enum class AXBoundaryBehavior { + CrossBoundary, + StopAtAnchorBoundary, + StopIfAlreadyAtBoundary +}; + // Forward declarations. template <class AXPositionType, class AXNodeType> class AXPosition; @@ -296,8 +305,15 @@ class AXPosition { NOTREACHED(); return false; case AXPositionKind::TEXT_POSITION: - return !text_position->IsInLineBreak() && - GetNextOnLineID(text_position->anchor_id_) == + // Special case, when the caret is right before a line break and the + // previous position is not another line break. We should return |true| + // because visually the caret is at the end of the current line. + if (text_position->IsInLineBreak()) { + return text_position->AtStartOfAnchor() && + !text_position->CreatePreviousTextAnchorPosition() + ->IsInLineBreak(); + } + return GetNextOnLineID(text_position->anchor_id_) == INVALID_ANCHOR_ID && text_position->AtEndOfAnchor(); } @@ -343,31 +359,34 @@ class AXPosition { AXPositionInstance copy = Clone(); DCHECK(copy); - DCHECK_NE(copy->text_offset_, INVALID_OFFSET); - // If the anchor node has no text inside it then the child index should be - // set to |BEFORE_TEXT|, hence the check if |MaxTextOffset| is greater than - // 0. - if (copy->MaxTextOffset() > 0 && - copy->text_offset_ >= copy->MaxTextOffset()) { - copy->child_index_ = copy->AnchorChildCount(); - } else { - DCHECK_GE(copy->text_offset_, 0); + DCHECK_GE(copy->text_offset_, 0); + if (!copy->AnchorChildCount() && + copy->text_offset_ != copy->MaxTextOffset()) { copy->child_index_ = BEFORE_TEXT; + } else { + copy->child_index_ = 0; + } - int current_offset = 0; - for (int i = 0; i < copy->AnchorChildCount(); ++i) { - AXPositionInstance child = copy->CreateChildPositionAt(i); - DCHECK(child); - int child_length = child->MaxTextOffsetInParent(); - if (copy->text_offset_ >= current_offset && - copy->text_offset_ < (current_offset + child_length)) { - copy->child_index_ = i; - break; - } - - current_offset += child_length; + // Blink doesn't always remove all deleted whitespace at the end of a + // textarea even though it will have adjusted its value attribute, because + // the extra layout objects are invisible. Therefore, we will stop at the + // last child that we can reach with the current text offset and ignore any + // remaining children. + int current_offset = 0; + for (int i = 0; i < copy->AnchorChildCount(); ++i) { + AXPositionInstance child = copy->CreateChildPositionAt(i); + DCHECK(child); + int child_length = child->MaxTextOffsetInParent(); + if (copy->text_offset_ >= current_offset && + copy->text_offset_ < (current_offset + child_length)) { + copy->child_index_ = i; + break; } + + current_offset += child_length; } + if (current_offset >= copy->MaxTextOffset()) + copy->child_index_ = copy->AnchorChildCount(); copy->kind_ = AXPositionKind::TREE_POSITION; return copy; @@ -422,34 +441,22 @@ class AXPosition { if (IsNullPosition() || !AnchorChildCount()) return AsTextPosition(); - AXPositionInstance child_position; AXPositionInstance tree_position = AsTreePosition(); - // If we get an "after children" position, we should return an "after - // children" position on the last child and recurse. - if (tree_position->AtEndOfAnchor()) { - child_position = - tree_position->CreateChildPositionAt(AnchorChildCount() - 1); - // Affinity needs to be maintained, because we are not moving the position - // but simply changing the anchor to the deepest leaf. - child_position->affinity_ = affinity_; - return child_position->CreatePositionAtEndOfAnchor() - ->AsLeafTextPosition(); - } - // Adjust the text offset. // No need to check for "before text" positions here because they are only // present on leaf anchor nodes. int adjusted_offset = AsTextPosition()->text_offset_; - for (int i = 0; i < tree_position->child_index_; ++i) { + AXPositionInstance child_position = tree_position->CreateChildPositionAt(0); + DCHECK(child_position); + for (int i = 1; i <= tree_position->child_index_ && + i < tree_position->AnchorChildCount(); + ++i) { + adjusted_offset -= child_position->MaxTextOffsetInParent(); child_position = tree_position->CreateChildPositionAt(i); DCHECK(child_position); - adjusted_offset -= child_position->MaxTextOffsetInParent(); } - DCHECK_GE(adjusted_offset, 0); - child_position = - tree_position->CreateChildPositionAt(tree_position->child_index_) - ->AsTextPosition(); + child_position = child_position->AsTextPosition(); child_position->text_offset_ = adjusted_offset; // Affinity needs to be maintained, because we are not moving the position // but simply changing the anchor to the deepest leaf. @@ -580,14 +587,21 @@ class AXPosition { return previous_leaf->AsTextPosition(); } - // The following methods work across anchors. - - AXPositionInstance CreateNextCharacterPosition() const { + AXPositionInstance CreateNextCharacterPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsTextPosition(); if (text_position->IsNullPosition()) return text_position; + // Note that |BoundaryBehavior::StopIfAlreadyAtBoundary| doesn't make + // sense for character boundaries. + DCHECK_NE(boundary_behavior, AXBoundaryBehavior::StopIfAlreadyAtBoundary); + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary && + (text_position->text_offset_ + 1) >= text_position->MaxTextOffset()) { + return Clone(); + } + if ((text_position->text_offset_ + 1) < text_position->MaxTextOffset()) { text_position->text_offset_ += 1; // Even if our affinity was upstream, moving to the next character should @@ -606,7 +620,16 @@ class AXPosition { return text_position; } - AXPositionInstance CreatePreviousCharacterPosition() const { + AXPositionInstance CreatePreviousCharacterPosition( + AXBoundaryBehavior boundary_behavior) const { + // Note that |BoundaryBehavior::StopIfAlreadyAtBoundary| doesn't make + // sense for character boundaries. + DCHECK_NE(boundary_behavior, AXBoundaryBehavior::StopIfAlreadyAtBoundary); + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary && + AtStartOfAnchor()) { + return Clone(); + } + bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsTextPosition(); if (text_position->IsNullPosition()) @@ -629,11 +652,18 @@ class AXPosition { return text_position; } - AXPositionInstance CreateNextWordStartPosition() const { + AXPositionInstance CreateNextWordStartPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); if (text_position->IsNullPosition()) return text_position; + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtStartOfWord()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } const std::vector<int32_t> word_starts = text_position->GetWordStartOffsets(); @@ -647,8 +677,11 @@ class AXPosition { } while (!text_position->IsNullPosition() && (!text_position->MaxTextOffset() || text_position->GetWordStartOffsets().empty())); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtEndOfAnchor(); return text_position; + } const std::vector<int32_t> word_starts = text_position->GetWordStartOffsets(); @@ -665,24 +698,37 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtEndOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); return text_position; } - AXPositionInstance CreatePreviousWordStartPosition() const { + AXPositionInstance CreatePreviousWordStartPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtStartOfWord()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } if (text_position->AtStartOfAnchor()) { text_position = text_position->CreatePreviousTextAnchorPosition(); text_position = text_position->CreatePositionAtEndOfAnchor(); } - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtStartOfAnchor(); return text_position; + } const std::vector<int32_t> word_starts = text_position->GetWordStartOffsets(); @@ -696,8 +742,11 @@ class AXPosition { } while (!text_position->IsNullPosition() && (!text_position->MaxTextOffset() || text_position->GetWordStartOffsets().empty())); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtStartOfAnchor(); return text_position; + } const std::vector<int32_t> word_starts = text_position->GetWordStartOffsets(); @@ -714,8 +763,11 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtStartOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); @@ -723,14 +775,24 @@ class AXPosition { } // Word end positions are one past the last character of the word. - AXPositionInstance CreateNextWordEndPosition() const { + AXPositionInstance CreateNextWordEndPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtEndOfWord()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } if (text_position->AtEndOfAnchor()) text_position = text_position->CreateNextTextAnchorPosition(); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtEndOfAnchor(); return text_position; + } const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets(); auto iterator = @@ -743,8 +805,11 @@ class AXPosition { } while (!text_position->IsNullPosition() && (!text_position->MaxTextOffset() || text_position->GetWordEndOffsets().empty())); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtEndOfAnchor(); return text_position; + } const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets(); DCHECK(!word_ends.empty()); @@ -760,8 +825,11 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtEndOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); @@ -769,16 +837,26 @@ class AXPosition { } // Word end positions are one past the last character of the word. - AXPositionInstance CreatePreviousWordEndPosition() const { + AXPositionInstance CreatePreviousWordEndPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtEndOfWord()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } if (text_position->AtStartOfAnchor()) { text_position = text_position->CreatePreviousTextAnchorPosition(); text_position = text_position->CreatePositionAtEndOfAnchor(); } - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtStartOfAnchor(); return text_position; + } const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets(); auto iterator = @@ -791,8 +869,11 @@ class AXPosition { } while (!text_position->IsNullPosition() && (!text_position->MaxTextOffset() || text_position->GetWordStartOffsets().empty())); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtStartOfAnchor(); return text_position; + } const std::vector<int32_t> word_ends = text_position->GetWordEndOffsets(); DCHECK(!word_ends.empty()); @@ -808,19 +889,29 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtStartOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); return text_position; } - AXPositionInstance CreateNextLineStartPosition() const { + AXPositionInstance CreateNextLineStartPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); if (text_position->IsNullPosition()) return text_position; + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtStartOfLine()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } // Find the next line break. int32_t next_on_line_id = text_position->anchor_id_; @@ -833,8 +924,11 @@ class AXPosition { text_position->AsLeafTextPosition()->CreateNextTextAnchorPosition(); while (text_position->IsInLineBreak()) text_position = text_position->CreateNextTextAnchorPosition(); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtEndOfAnchor(); return text_position; + } // If the line boundary is in the same subtree, return a position rooted at // the current position. @@ -842,21 +936,35 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtEndOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); return text_position; } - AXPositionInstance CreatePreviousLineStartPosition() const { + AXPositionInstance CreatePreviousLineStartPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtStartOfLine()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } + if (text_position->IsInLineBreak() || text_position->AtStartOfAnchor()) text_position = text_position->CreatePreviousTextAnchorPosition(); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtStartOfAnchor(); return text_position; + } int32_t previous_on_line_id = text_position->anchor_id_; while (GetPreviousOnLineID(previous_on_line_id) != INVALID_ANCHOR_ID) @@ -865,8 +973,11 @@ class AXPosition { CreateTextPosition(tree_id_, previous_on_line_id, 0 /* text_offset */, AX_TEXT_AFFINITY_DOWNSTREAM); text_position = text_position->AsLeafTextPosition(); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtStartOfAnchor(); return text_position; + } // If the line boundary is in the same subtree, return a position rooted at // the current position. @@ -874,8 +985,11 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtStartOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); @@ -884,15 +998,26 @@ class AXPosition { // Line end positions are one past the last character of the line, excluding // any newline characters. - AXPositionInstance CreateNextLineEndPosition() const { + AXPositionInstance CreateNextLineEndPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtEndOfLine()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } + // Skip forward to the next line if we are at the end of one. // Note that not all lines end with a hard line break. while (text_position->IsInLineBreak() || text_position->AtEndOfAnchor()) text_position = text_position->CreateNextTextAnchorPosition(); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtEndOfAnchor(); return text_position; + } // Find the next line break. int32_t next_on_line_id = text_position->anchor_id_; @@ -904,8 +1029,11 @@ class AXPosition { text_position = text_position->AsLeafTextPosition(); while (text_position->IsInLineBreak()) text_position = text_position->CreatePreviousTextAnchorPosition(); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtEndOfAnchor(); return text_position; + } text_position = text_position->CreatePositionAtEndOfAnchor(); // If the line boundary is in the same subtree, return a position rooted at @@ -914,8 +1042,11 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtEndOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); @@ -924,11 +1055,18 @@ class AXPosition { // Line end positions are one past the last character of the line, excluding // any newline characters. - AXPositionInstance CreatePreviousLineEndPosition() const { + AXPositionInstance CreatePreviousLineEndPosition( + AXBoundaryBehavior boundary_behavior) const { bool was_tree_position = IsTreePosition(); AXPositionInstance text_position = AsLeafTextPosition(); if (text_position->IsNullPosition()) return text_position; + if (boundary_behavior == AXBoundaryBehavior::StopIfAlreadyAtBoundary && + text_position->AtEndOfLine()) { + AXPositionInstance clone = Clone(); + clone->affinity_ = AX_TEXT_AFFINITY_DOWNSTREAM; + return clone; + } int32_t previous_on_line_id = text_position->anchor_id_; while (GetPreviousOnLineID(previous_on_line_id) != INVALID_ANCHOR_ID) @@ -940,8 +1078,11 @@ class AXPosition { text_position->AsLeafTextPosition()->CreatePreviousTextAnchorPosition(); while (text_position->IsInLineBreak()) text_position = text_position->CreatePreviousTextAnchorPosition(); - if (text_position->IsNullPosition()) + if (text_position->IsNullPosition()) { + if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) + return CreatePositionAtStartOfAnchor(); return text_position; + } text_position = text_position->CreatePositionAtEndOfAnchor(); // If the line boundary is in the same subtree, return a position rooted at @@ -950,8 +1091,11 @@ class AXPosition { // be in the shadow DOM if the original position was not. AXPositionInstance common_ancestor = text_position->LowestCommonAncestor(*this); - if (GetAnchor() == common_ancestor->GetAnchor()) + if (GetAnchor() == common_ancestor->GetAnchor()) { text_position = std::move(common_ancestor); + } else if (boundary_behavior == AXBoundaryBehavior::StopAtAnchorBoundary) { + return CreatePositionAtStartOfAnchor(); + } if (was_tree_position) text_position = text_position->AsTreePosition(); @@ -1047,7 +1191,7 @@ class AXPosition { if (parent_position->IsNullPosition()) return CreateNullPosition(); - // Get the previous sibling's deepest first child if a previous sibling + // Get the previous sibling's deepest last child if a previous sibling // exists, otherwise move up to the parent. int index_in_parent = AnchorIndexInParent(); if (index_in_parent <= 0) @@ -1056,7 +1200,7 @@ class AXPosition { AXPositionInstance leaf = parent_position->CreateChildPositionAt(index_in_parent - 1); while (!leaf->IsNullPosition() && leaf->AnchorChildCount()) - leaf = leaf->CreateChildPositionAt(0); + leaf = leaf->CreateChildPositionAt(leaf->AnchorChildCount() - 1); return leaf; } diff --git a/chromium/ui/accessibility/ax_role_properties.cc b/chromium/ui/accessibility/ax_role_properties.cc index 34355fea453..e0e44547018 100644 --- a/chromium/ui/accessibility/ax_role_properties.cc +++ b/chromium/ui/accessibility/ax_role_properties.cc @@ -12,7 +12,6 @@ bool IsRoleClickable(AXRole role) { case AX_ROLE_CHECK_BOX: case AX_ROLE_COLOR_WELL: case AX_ROLE_DISCLOSURE_TRIANGLE: - case AX_ROLE_IMAGE_MAP_LINK: case AX_ROLE_LINK: case AX_ROLE_LIST_BOX_OPTION: case AX_ROLE_MENU_BUTTON: diff --git a/chromium/ui/accessibility/ax_tree.cc b/chromium/ui/accessibility/ax_tree.cc index d2c7f0d524f..734be5fefee 100644 --- a/chromium/ui/accessibility/ax_tree.cc +++ b/chromium/ui/accessibility/ax_tree.cc @@ -189,19 +189,31 @@ gfx::RectF AXTree::RelativeToTreeBounds(const AXNode* node, while (node != nullptr) { if (node->data().transform) node->data().transform->TransformRect(&bounds); - auto* container = GetFromId(node->data().offset_container_id); - if (!container) { - if (node == root()) - container = node->parent(); - else - container = root(); - } + const AXNode* container; + + // Normally we apply any transforms and offsets for each node and + // then walk up to its offset container - however, if the node has + // no width or height, walk up to its nearest ancestor until we find + // one that has bounds. + if (bounds.width() == 0 && bounds.height() == 0) + container = node->parent(); + else + container = GetFromId(node->data().offset_container_id); + if (!container && container != root()) + container = root(); if (!container || container == node) break; gfx::RectF container_bounds = container->data().location; bounds.Offset(container_bounds.x(), container_bounds.y()); + // If we don't have any size yet, take the size from this ancestor. + // The rationale is that it's not useful to the user for an object to + // have no width or height and it's probably a bug; it's better to + // reflect the bounds of the nearest ancestor rather than a 0x0 box. + if (bounds.width() == 0 && bounds.height() == 0) + bounds.set_size(container_bounds.size()); + int scroll_x = 0; int scroll_y = 0; if (container->data().GetIntAttribute(ui::AX_ATTR_SCROLL_X, &scroll_x) && diff --git a/chromium/ui/accessibility/ax_tree.h b/chromium/ui/accessibility/ax_tree.h index 95885decacb..c45f53afbad 100644 --- a/chromium/ui/accessibility/ax_tree.h +++ b/chromium/ui/accessibility/ax_tree.h @@ -163,6 +163,7 @@ class AX_EXPORT AXTree { virtual ~AXTree(); virtual void SetDelegate(AXTreeDelegate* delegate); + AXTreeDelegate* delegate() const { return delegate_; } AXNode* root() const { return root_; } diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc index 144b9f93833..0bcc650ae13 100644 --- a/chromium/ui/accessibility/ax_tree_unittest.cc +++ b/chromium/ui/accessibility/ax_tree_unittest.cc @@ -662,7 +662,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) { initial_state.nodes[0].AddStringAttribute(AX_ATTR_NAME, "N1"); initial_state.nodes[0].AddStringAttribute(AX_ATTR_DESCRIPTION, "D1"); initial_state.nodes[0].AddBoolAttribute(AX_ATTR_LIVE_ATOMIC, true); - initial_state.nodes[0].AddBoolAttribute(AX_ATTR_LIVE_BUSY, false); + initial_state.nodes[0].AddBoolAttribute(AX_ATTR_BUSY, false); initial_state.nodes[0].AddFloatAttribute(AX_ATTR_MIN_VALUE_FOR_RANGE, 1.0); initial_state.nodes[0].AddFloatAttribute(AX_ATTR_MAX_VALUE_FOR_RANGE, 10.0); initial_state.nodes[0].AddIntAttribute(AX_ATTR_SCROLL_X, 5); @@ -680,7 +680,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) { update0.nodes[0].AddStringAttribute(AX_ATTR_NAME, "N2"); update0.nodes[0].AddStringAttribute(AX_ATTR_DESCRIPTION, "D2"); update0.nodes[0].AddBoolAttribute(AX_ATTR_LIVE_ATOMIC, false); - update0.nodes[0].AddBoolAttribute(AX_ATTR_LIVE_BUSY, true); + update0.nodes[0].AddBoolAttribute(AX_ATTR_BUSY, true); update0.nodes[0].AddFloatAttribute(AX_ATTR_MIN_VALUE_FOR_RANGE, 2.0); update0.nodes[0].AddFloatAttribute(AX_ATTR_MAX_VALUE_FOR_RANGE, 9.0); update0.nodes[0].AddIntAttribute(AX_ATTR_SCROLL_X, 6); @@ -693,7 +693,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) { EXPECT_EQ("name changed from N1 to N2", change_log[0]); EXPECT_EQ("description changed from D1 to D2", change_log[1]); EXPECT_EQ("liveAtomic changed to false", change_log[2]); - EXPECT_EQ("liveBusy changed to true", change_log[3]); + EXPECT_EQ("busy changed to true", change_log[3]); EXPECT_EQ("minValueForRange changed from 1 to 2", change_log[4]); EXPECT_EQ("maxValueForRange changed from 10 to 9", change_log[5]); EXPECT_EQ("scrollX changed from 5 to 6", change_log[6]); @@ -722,7 +722,7 @@ TEST(AXTreeTest, AttributeChangeCallbacks) { EXPECT_EQ("name changed from N2 to ", change_log2[0]); EXPECT_EQ("description changed from D2 to D3", change_log2[1]); EXPECT_EQ("value changed from to V3", change_log2[2]); - EXPECT_EQ("liveBusy changed to false", change_log2[3]); + EXPECT_EQ("busy changed to false", change_log2[3]); EXPECT_EQ("modal changed to true", change_log2[4]); EXPECT_EQ("minValueForRange changed from 2 to 0", change_log2[5]); EXPECT_EQ("valueForRange changed from 0 to 5", change_log2[6]); @@ -901,4 +901,23 @@ TEST(AXTreeTest, GetBoundsWithScrolling) { EXPECT_EQ("(115, 70) size (50 x 5)", GetBoundsAsString(tree, 3)); } +TEST(AXTreeTest, GetBoundsEmptyBoundsInheritsFromParent) { + AXTreeUpdate tree_update; + tree_update.root_id = 1; + tree_update.nodes.resize(3); + tree_update.nodes[0].id = 1; + tree_update.nodes[0].location = gfx::RectF(0, 0, 800, 600); + tree_update.nodes[0].child_ids.push_back(2); + tree_update.nodes[1].id = 2; + tree_update.nodes[1].location = gfx::RectF(300, 200, 100, 100); + tree_update.nodes[1].child_ids.push_back(3); + tree_update.nodes[2].id = 3; + tree_update.nodes[2].location = gfx::RectF(); + + AXTree tree(tree_update); + EXPECT_EQ("(0, 0) size (800 x 600)", GetBoundsAsString(tree, 1)); + EXPECT_EQ("(300, 200) size (100 x 100)", GetBoundsAsString(tree, 2)); + EXPECT_EQ("(300, 200) size (100 x 100)", GetBoundsAsString(tree, 3)); +} + } // namespace ui diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb index 0642f954f6d..2cad7bee434 100644 --- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb +++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_ca.xtb @@ -14,7 +14,7 @@ <translation id="2179565792157161713">Obre la descripció llarga en una pestanya nova</translation> <translation id="2223143012868735942">Un filtre de color personalitzable que s'aplica en pàgines web per millorar la percepció del color.</translation> <translation id="2394933097471027016">Proveu-ho ara. La navegació mitjançant la marca d'inserció està sempre activada en aquesta pàgina.</translation> -<translation id="2471847333270902538">Esquema de color per a <ph name="SITE" />:</translation> +<translation id="2471847333270902538">Paleta de colors per a <ph name="SITE" />:</translation> <translation id="2648340354586434750">Manteniu premuda la tecla d'<span class='key'>opció</span> per desplaçar-vos per les paraules.</translation> <translation id="2795227192542594043">Aquesta extensió proporciona un cursor mòbil a la pàgina web que permet seleccionar el text amb el teclat.</translation> <translation id="2808027189040546825">Pas 1: seleccioneu la fila amb les estrelles més tènues</translation> @@ -40,9 +40,9 @@ <translation id="5555153510860501336">El contrast elevat està desactivat.</translation> <translation id="5558600050691192317">Ordres del teclat</translation> <translation id="5594989420907487559">Executeu les animacions només una vegada o desactiveu-les completament.</translation> -<translation id="5631241868147802353">Esquema de colors predeterminat:</translation> +<translation id="5631241868147802353">Paleta de colors predeterminada:</translation> <translation id="5650358096585648000">Suggeriments visuals</translation> -<translation id="5710185147685935461">Canvia o inverteix l'esquema de colors perquè les pàgines web siguin més fàcils de llegir.</translation> +<translation id="5710185147685935461">Canvia o inverteix la paleta de colors perquè les pàgines web siguin més fàcils de llegir.</translation> <translation id="5939518447894949180">Restableix</translation> <translation id="595639123821853262">Escala de grisos invertida</translation> <translation id="6017514345406065928">Verd</translation> diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb index 25550595ea0..2da42fe4ca8 100644 --- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb +++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_id.xtb @@ -12,11 +12,11 @@ <translation id="1996252509865389616">Aktifkan?</translation> <translation id="2079545284768500474">Urungkan</translation> <translation id="2179565792157161713">Buka Deskripsi Panjang di Tab Baru</translation> -<translation id="2223143012868735942">Filter warna yang disesuaikan diterapkan ke laman web untuk meningkatkan persepsi warna.</translation> -<translation id="2394933097471027016">Cobalah sekarang - Penjelajahan Caret selalu diaktifkan pada laman ini!</translation> +<translation id="2223143012868735942">Filter warna yang disesuaikan diterapkan ke halaman web untuk meningkatkan persepsi warna.</translation> +<translation id="2394933097471027016">Cobalah sekarang - Penjelajahan Caret selalu diaktifkan pada halaman ini!</translation> <translation id="2471847333270902538">Skema warna untuk <ph name="SITE" />:</translation> <translation id="2648340354586434750">Tahan <span class='key'>Opsi</span> untuk berpindah berdasarkan kata.</translation> -<translation id="2795227192542594043">Ekstensi ini memberi Anda kursor yang dapat digerakkan di laman web, memungkinkan Anda memilih teks dengan keyboard.</translation> +<translation id="2795227192542594043">Ekstensi ini memberi Anda kursor yang dapat digerakkan di halaman web, memungkinkan Anda memilih teks dengan keyboard.</translation> <translation id="2808027189040546825">Langkah 1: Pilih baris dengan bintang paling redup:</translation> <translation id="2965611304828530558"><p>Saat Anda mengjangkau tautan atau kontrol, tautan atau kontrol tersebut akan otomatis terfokus. Tekan <span class='key'>Enter</span> untuk mengeklik tautan atau tombol. </p> <p> Saat kontrol terfokus (seperti kotak teks atau kotak daftar) menangkap tombol panah, tekan <span class='key'>Esc</span> diikuti dengan tanda panah kiri atau kanan untuk melanjutkan Penjelajahan Caret. </p> <p> Atau, tekan <span class='key'>Tab</span> untuk berpindah ke kontrol yang dapat difokuskan berikutnya. </p></translation> <translation id="3252573918265662711">Penyiapan</translation> @@ -25,7 +25,7 @@ <translation id="3622586652998721735">Ditetapkan sebagai skema default</translation> <translation id="3812541808639806898">Penampil Teks Alternatif Gambar</translation> <translation id="381767806621926835">Klik kanan pada item apa pun yang memiliki atribut "longdesc" atau "aria-describedat" untuk mengakses deskripsi lognya.</translation> -<translation id="4023902424053835668">Menjelajahi teks laman web menggunakan tombol panah.</translation> +<translation id="4023902424053835668">Menjelajahi teks halaman web menggunakan tombol panah.</translation> <translation id="4388820049312272371">Sorot posisi kursor dengan lampu kilat cepat.</translation> <translation id="4394049700291259645">Nonaktifkan</translation> <translation id="4769065380738716500">Gambar telah diganti dengan teks alternatif.</translation> @@ -42,7 +42,7 @@ <translation id="5594989420907487559">Menjalankan animasi hanya satu kali, atau menonaktifkan animasi sepenuhnya.</translation> <translation id="5631241868147802353">Skema warna default:</translation> <translation id="5650358096585648000">Masukan Visual</translation> -<translation id="5710185147685935461">Mengubah atau membalikkan skema warna untuk membuat laman web lebih mudah dibaca.</translation> +<translation id="5710185147685935461">Mengubah atau membalikkan skema warna untuk membuat halaman web lebih mudah dibaca.</translation> <translation id="5939518447894949180">Setel ulang</translation> <translation id="595639123821853262">Abu-Abu Terbalik</translation> <translation id="6017514345406065928">Hijau</translation> diff --git a/chromium/ui/accessibility/platform/atk_util_auralinux.cc b/chromium/ui/accessibility/platform/atk_util_auralinux.cc index 0b25fcb10fe..207260ef57d 100644 --- a/chromium/ui/accessibility/platform/atk_util_auralinux.cc +++ b/chromium/ui/accessibility/platform/atk_util_auralinux.cc @@ -13,23 +13,26 @@ #include "base/memory/singleton.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" -#include "base/task_runner.h" +#include "base/task_scheduler/post_task.h" +#include "base/threading/thread_restrictions.h" #include "ui/accessibility/platform/atk_util_auralinux.h" #include "ui/accessibility/platform/ax_platform_node_auralinux.h" namespace { -typedef void (*gnome_accessibility_module_init)(); +typedef void (*GnomeAccessibilityModuleInitFunc)(); const char kAccessibilityEnabled[] = "ACCESSIBILITY_ENABLED"; -const char kAtkBridgeModule[] = "gail:atk-bridge"; +const char kAtkBridgeModule[] = "atk-bridge"; const char kAtkBridgePath[] = "gtk-2.0/modules/libatk-bridge.so"; const char kAtkBridgeSymbolName[] = "gnome_accessibility_module_init"; const char kGtkModules[] = "GTK_MODULES"; -gnome_accessibility_module_init g_accessibility_module_init = nullptr; +// Returns a function pointer to be invoked on the main thread to init +// the gnome accessibility module if it's enabled (nullptr otherwise). +GnomeAccessibilityModuleInitFunc GetAccessibilityModuleInitFunc() { + base::ThreadRestrictions::AssertIOAllowed(); -bool AccessibilityModuleInitOnFileThread() { // Try to load libatk-bridge.so. base::FilePath atk_bridge_path(ATK_LIB_DIR); atk_bridge_path = atk_bridge_path.Append(kAtkBridgePath); @@ -37,18 +40,28 @@ bool AccessibilityModuleInitOnFileThread() { static_cast<GModuleFlags>(0)); if (!bridge) { VLOG(1) << "Unable to open module " << atk_bridge_path.value(); - return false; + return nullptr; } - if (!g_module_symbol(bridge, kAtkBridgeSymbolName, - (gpointer *)&g_accessibility_module_init)) { + GnomeAccessibilityModuleInitFunc init_func = nullptr; + + if (!g_module_symbol(bridge, kAtkBridgeSymbolName, (gpointer*)&init_func)) { VLOG(1) << "Unable to get symbol pointer from " << atk_bridge_path.value(); - // Just to make sure it's null; - g_accessibility_module_init = nullptr; - return false; + return nullptr; } - return true; + DCHECK(init_func); + return init_func; +} + +void FinishAccessibilityInitOnMainThread( + GnomeAccessibilityModuleInitFunc init_func) { + if (!init_func) { + VLOG(1) << "Will not enable ATK accessibility support."; + return; + } + + init_func(); } bool PlatformShouldEnableAccessibility() { @@ -58,8 +71,8 @@ bool PlatformShouldEnableAccessibility() { return false; for (const std::string& module : - base::SplitString(gtk_modules, base::kWhitespaceASCII, - base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { + base::SplitString(gtk_modules, ":", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY)) { if (module == kAtkBridgeModule) return true; } @@ -161,42 +174,18 @@ AtkUtilAuraLinux* AtkUtilAuraLinux::GetInstance() { return base::Singleton<AtkUtilAuraLinux>::get(); } -AtkUtilAuraLinux::AtkUtilAuraLinux() : is_enabled_(false) {} - -void AtkUtilAuraLinux::Initialize( - scoped_refptr<base::TaskRunner> init_task_runner) { - +void AtkUtilAuraLinux::InitializeAsync() { // Register our util class. g_type_class_unref(g_type_class_ref(ATK_UTIL_AURALINUX_TYPE)); if (!ShouldEnableAccessibility()) return; - init_task_runner->PostTaskAndReply( + base::PostTaskWithTraitsAndReplyWithResult( FROM_HERE, - base::Bind( - &AtkUtilAuraLinux::CheckIfAccessibilityIsEnabledOnFileThread, - base::Unretained(this)), - base::Bind( - &AtkUtilAuraLinux::FinishAccessibilityInitOnUIThread, - base::Unretained(this))); -} - -AtkUtilAuraLinux::~AtkUtilAuraLinux() { -} - -void AtkUtilAuraLinux::CheckIfAccessibilityIsEnabledOnFileThread() { - is_enabled_ = AccessibilityModuleInitOnFileThread(); -} - -void AtkUtilAuraLinux::FinishAccessibilityInitOnUIThread() { - if (!is_enabled_) { - VLOG(1) << "Will not enable ATK accessibility support."; - return; - } - - DCHECK(g_accessibility_module_init); - g_accessibility_module_init(); + {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + base::Bind(&GetAccessibilityModuleInitFunc), + base::Bind(&FinishAccessibilityInitOnMainThread)); } } // namespace ui diff --git a/chromium/ui/accessibility/platform/atk_util_auralinux.h b/chromium/ui/accessibility/platform/atk_util_auralinux.h index 06cd8881bed..8119fb781c1 100644 --- a/chromium/ui/accessibility/platform/atk_util_auralinux.h +++ b/chromium/ui/accessibility/platform/atk_util_auralinux.h @@ -5,13 +5,10 @@ #ifndef UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_ #define UI_ACCESSIBILITY_AX_UTIL_AURALINUX_H_ +#include "base/macros.h" #include "base/memory/singleton.h" #include "ui/accessibility/ax_export.h" -namespace base { -class TaskRunner; -} - namespace ui { // This singleton class initializes ATK (accessibility toolkit) and @@ -22,18 +19,14 @@ class AtkUtilAuraLinux { // Get the single instance of this class. static AtkUtilAuraLinux* GetInstance(); - AtkUtilAuraLinux(); - virtual ~AtkUtilAuraLinux(); + AtkUtilAuraLinux() = default; - void Initialize(scoped_refptr<base::TaskRunner> init_task_runner); + void InitializeAsync(); private: friend struct base::DefaultSingletonTraits<AtkUtilAuraLinux>; - void CheckIfAccessibilityIsEnabledOnFileThread(); - void FinishAccessibilityInitOnUIThread(); - - bool is_enabled_; + DISALLOW_COPY_AND_ASSIGN(AtkUtilAuraLinux); }; } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node.cc b/chromium/ui/accessibility/platform/ax_platform_node.cc index 93a374abbac..d7f68012f77 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node.cc @@ -9,39 +9,35 @@ #include "build/build_config.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h" -#include "ui/accessibility/platform/ax_platform_unique_id.h" namespace ui { -namespace { +// static +base::LazyInstance<base::ObserverList<AXModeObserver>>::Leaky + AXPlatformNode::ax_mode_observers_ = LAZY_INSTANCE_INITIALIZER; -using UniqueIdMap = base::hash_map<int32_t, AXPlatformNode*>; -// Map from each AXPlatformNode's unique id to its instance. -base::LazyInstance<UniqueIdMap>::DestructorAtExit g_unique_id_map = - LAZY_INSTANCE_INITIALIZER; -} - -AXPlatformNode::AXPlatformNode() : unique_id_(GetNextAXPlatformNodeUniqueId()) { - g_unique_id_map.Get()[unique_id_] = this; -} +AXPlatformNode::AXPlatformNode() {} AXPlatformNode::~AXPlatformNode() { - if (unique_id_) - g_unique_id_map.Get().erase(unique_id_); } void AXPlatformNode::Destroy() { - g_unique_id_map.Get().erase(unique_id_); - unique_id_ = 0; } -AXPlatformNode* AXPlatformNode::GetFromUniqueId(int32_t unique_id) { - UniqueIdMap* unique_ids = g_unique_id_map.Pointer(); - auto iter = unique_ids->find(unique_id); - if (iter != unique_ids->end()) - return iter->second; +// static +void AXPlatformNode::AddAXModeObserver(AXModeObserver* observer) { + ax_mode_observers_.Get().AddObserver(observer); +} + +// static +void AXPlatformNode::RemoveAXModeObserver(AXModeObserver* observer) { + ax_mode_observers_.Get().RemoveObserver(observer); +} - return nullptr; +// static +void AXPlatformNode::NotifyAddAXModeFlags(AXMode mode_flags) { + for (auto& observer : ax_mode_observers_.Get()) + observer.OnAXModeAdded(mode_flags); } } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node.h b/chromium/ui/accessibility/platform/ax_platform_node.h index 7204c23aa88..8aa38ad0a82 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node.h +++ b/chromium/ui/accessibility/platform/ax_platform_node.h @@ -5,9 +5,12 @@ #ifndef UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_H_ #define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_H_ +#include "base/lazy_instance.h" +#include "base/observer_list.h" #include "build/build_config.h" #include "ui/accessibility/ax_enums.h" #include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_mode_observer.h" #include "ui/gfx/native_widget_types.h" namespace ui { @@ -30,6 +33,15 @@ class AX_EXPORT AXPlatformNode { static AXPlatformNode* FromNativeViewAccessible( gfx::NativeViewAccessible accessible); + // Register and unregister to receive notifications about AXMode changes + // for this node. + static void AddAXModeObserver(AXModeObserver* observer); + static void RemoveAXModeObserver(AXModeObserver* observer); + + // Helper static function to notify all global observers about + // the addition of an AXMode flag. + static void NotifyAddAXModeFlags(AXMode mode_flags); + // Call Destroy rather than deleting this, because the subclass may // use reference counting. virtual void Destroy(); @@ -41,7 +53,7 @@ class AX_EXPORT AXPlatformNode { // Fire a platform-specific notification that an event has occurred on // this object. - virtual void NotifyAccessibilityEvent(ui::AXEvent event_type) = 0; + virtual void NotifyAccessibilityEvent(AXEvent event_type) = 0; // Return this object's delegate. virtual AXPlatformNodeDelegate* GetDelegate() const = 0; @@ -50,9 +62,10 @@ class AX_EXPORT AXPlatformNode { AXPlatformNode(); virtual ~AXPlatformNode(); - virtual AXPlatformNode* GetFromUniqueId(int32_t unique_id); - - int32_t unique_id_; + private: + // Global ObserverList for AXMode changes. + static base::LazyInstance<base::ObserverList<AXModeObserver>>::Leaky + ax_mode_observers_; }; } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc index c73d7a93abf..1350607a562 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc @@ -8,7 +8,6 @@ #include "base/command_line.h" #include "base/strings/sys_string_conversions.h" -#include "base/task_runner.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/platform/atk_util_auralinux.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h" @@ -392,9 +391,8 @@ void AXPlatformNodeAuraLinux::SetApplication(AXPlatformNode* application) { } // static -void AXPlatformNodeAuraLinux::StaticInitialize( - scoped_refptr<base::TaskRunner> init_task_runner) { - AtkUtilAuraLinux::GetInstance()->Initialize(init_task_runner); +void AXPlatformNodeAuraLinux::StaticInitialize() { + AtkUtilAuraLinux::GetInstance()->InitializeAsync(); } AtkRole AXPlatformNodeAuraLinux::GetAtkRole() { @@ -474,8 +472,12 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDED); if (data.HasState(ui::AX_STATE_FOCUSABLE)) atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSABLE); +#if defined(ATK_CHECK_VERSION) +#if ATK_CHECK_VERSION(2, 11, 2) if (data.HasState(ui::AX_STATE_HASPOPUP)) atk_state_set_add_state(atk_state_set, ATK_STATE_HAS_POPUP); +#endif +#endif if (data.HasState(ui::AX_STATE_SELECTED)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTED); if (data.HasState(ui::AX_STATE_SELECTABLE)) diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h index 00581d93646..1504bfbf67b 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h @@ -8,14 +8,9 @@ #include <atk/atk.h> #include "base/macros.h" -#include "base/memory/ref_counted.h" #include "ui/accessibility/ax_export.h" #include "ui/accessibility/platform/ax_platform_node_base.h" -namespace base { -class TaskRunner; -} - namespace ui { // Implements accessibility on Aura Linux using ATK. @@ -28,9 +23,8 @@ class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { AX_EXPORT static void SetApplication(AXPlatformNode* application); static AXPlatformNode* application() { return application_; } - // Do static initialization using the given task runner for file operations. - AX_EXPORT static void StaticInitialize( - scoped_refptr<base::TaskRunner> init_task_runner); + // Do asynchronous static initialization. + AX_EXPORT static void StaticInitialize(); AtkRole GetAtkRole(); void GetAtkState(AtkStateSet* state_set); diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base.cc b/chromium/ui/accessibility/platform/ax_platform_node_base.cc index e16ba20c084..abdf04bba2b 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_base.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_base.cc @@ -19,7 +19,7 @@ void AXPlatformNodeBase::Init(AXPlatformNodeDelegate* delegate) { } const AXNodeData& AXPlatformNodeBase::GetData() const { - CR_DEFINE_STATIC_LOCAL(ui::AXNodeData, empty_data, ()); + CR_DEFINE_STATIC_LOCAL(AXNodeData, empty_data, ()); if (delegate_) return delegate_->GetData(); return empty_data; @@ -115,115 +115,107 @@ bool AXPlatformNodeBase::IsDescendant(AXPlatformNodeBase* node) { return IsDescendant(parent); } -bool AXPlatformNodeBase::HasBoolAttribute( - ui::AXBoolAttribute attribute) const { +bool AXPlatformNodeBase::HasBoolAttribute(AXBoolAttribute attribute) const { if (!delegate_) return false; return GetData().HasBoolAttribute(attribute); } -bool AXPlatformNodeBase::GetBoolAttribute( - ui::AXBoolAttribute attribute) const { +bool AXPlatformNodeBase::GetBoolAttribute(AXBoolAttribute attribute) const { if (!delegate_) return false; return GetData().GetBoolAttribute(attribute); } -bool AXPlatformNodeBase::GetBoolAttribute( - ui::AXBoolAttribute attribute, bool* value) const { +bool AXPlatformNodeBase::GetBoolAttribute(AXBoolAttribute attribute, + bool* value) const { if (!delegate_) return false; return GetData().GetBoolAttribute(attribute, value); } -bool AXPlatformNodeBase::HasFloatAttribute( - ui::AXFloatAttribute attribute) const { +bool AXPlatformNodeBase::HasFloatAttribute(AXFloatAttribute attribute) const { if (!delegate_) return false; return GetData().HasFloatAttribute(attribute); } -float AXPlatformNodeBase::GetFloatAttribute( - ui::AXFloatAttribute attribute) const { +float AXPlatformNodeBase::GetFloatAttribute(AXFloatAttribute attribute) const { if (!delegate_) return false; return GetData().GetFloatAttribute(attribute); } -bool AXPlatformNodeBase::GetFloatAttribute( - ui::AXFloatAttribute attribute, float* value) const { +bool AXPlatformNodeBase::GetFloatAttribute(AXFloatAttribute attribute, + float* value) const { if (!delegate_) return false; return GetData().GetFloatAttribute(attribute, value); } -bool AXPlatformNodeBase::HasIntAttribute( - ui::AXIntAttribute attribute) const { +bool AXPlatformNodeBase::HasIntAttribute(AXIntAttribute attribute) const { if (!delegate_) return false; return GetData().HasIntAttribute(attribute); } -int AXPlatformNodeBase::GetIntAttribute( - ui::AXIntAttribute attribute) const { +int AXPlatformNodeBase::GetIntAttribute(AXIntAttribute attribute) const { if (!delegate_) return false; return GetData().GetIntAttribute(attribute); } -bool AXPlatformNodeBase::GetIntAttribute( - ui::AXIntAttribute attribute, int* value) const { +bool AXPlatformNodeBase::GetIntAttribute(AXIntAttribute attribute, + int* value) const { if (!delegate_) return false; return GetData().GetIntAttribute(attribute, value); } -bool AXPlatformNodeBase::HasStringAttribute( - ui::AXStringAttribute attribute) const { +bool AXPlatformNodeBase::HasStringAttribute(AXStringAttribute attribute) const { if (!delegate_) return false; return GetData().HasStringAttribute(attribute); } const std::string& AXPlatformNodeBase::GetStringAttribute( - ui::AXStringAttribute attribute) const { + AXStringAttribute attribute) const { CR_DEFINE_STATIC_LOCAL(std::string, empty_data, ()); if (!delegate_) return empty_data; return GetData().GetStringAttribute(attribute); } -bool AXPlatformNodeBase::GetStringAttribute( - ui::AXStringAttribute attribute, std::string* value) const { +bool AXPlatformNodeBase::GetStringAttribute(AXStringAttribute attribute, + std::string* value) const { if (!delegate_) return false; return GetData().GetStringAttribute(attribute, value); } base::string16 AXPlatformNodeBase::GetString16Attribute( - ui::AXStringAttribute attribute) const { + AXStringAttribute attribute) const { if (!delegate_) return base::string16(); return GetData().GetString16Attribute(attribute); } -bool AXPlatformNodeBase::GetString16Attribute( - ui::AXStringAttribute attribute, - base::string16* value) const { +bool AXPlatformNodeBase::GetString16Attribute(AXStringAttribute attribute, + base::string16* value) const { if (!delegate_) return false; return GetData().GetString16Attribute(attribute, value); } bool AXPlatformNodeBase::HasIntListAttribute( - ui::AXIntListAttribute attribute) const { + AXIntListAttribute attribute) const { if (!delegate_) return false; return GetData().HasIntListAttribute(attribute); } const std::vector<int32_t>& AXPlatformNodeBase::GetIntListAttribute( - ui::AXIntListAttribute attribute) const { + AXIntListAttribute attribute) const { CR_DEFINE_STATIC_LOCAL(std::vector<int32_t>, empty_data, ()); if (!delegate_) return empty_data; @@ -231,7 +223,7 @@ const std::vector<int32_t>& AXPlatformNodeBase::GetIntListAttribute( } bool AXPlatformNodeBase::GetIntListAttribute( - ui::AXIntListAttribute attribute, + AXIntListAttribute attribute, std::vector<int32_t>* value) const { if (!delegate_) return false; @@ -252,8 +244,8 @@ AXPlatformNodeBase* AXPlatformNodeBase::FromNativeViewAccessible( } bool AXPlatformNodeBase::SetTextSelection(int start_offset, int end_offset) { - ui::AXActionData action_data; - action_data.action = ui::AX_ACTION_SET_SELECTION; + AXActionData action_data; + action_data.action = AX_ACTION_SET_SELECTION; action_data.anchor_node_id = action_data.focus_node_id = GetData().id; action_data.anchor_offset = start_offset; action_data.focus_offset = end_offset; @@ -264,13 +256,13 @@ bool AXPlatformNodeBase::SetTextSelection(int start_offset, int end_offset) { } bool AXPlatformNodeBase::IsTextOnlyObject() const { - return GetData().role == ui::AX_ROLE_STATIC_TEXT || - GetData().role == ui::AX_ROLE_LINE_BREAK || - GetData().role == ui::AX_ROLE_INLINE_TEXT_BOX; + return GetData().role == AX_ROLE_STATIC_TEXT || + GetData().role == AX_ROLE_LINE_BREAK || + GetData().role == AX_ROLE_INLINE_TEXT_BOX; } bool AXPlatformNodeBase::IsNativeTextControl() const { - const std::string& html_tag = GetStringAttribute(ui::AX_ATTR_HTML_TAG); + const std::string& html_tag = GetStringAttribute(AX_ATTR_HTML_TAG); if (html_tag == "input") { std::string input_type; if (!GetData().GetHtmlAttribute("type", &input_type)) @@ -287,11 +279,11 @@ bool AXPlatformNodeBase::IsSimpleTextControl() const { // Time fields, color wells and spinner buttons might also use text fields as // constituent parts, but they are not considered text fields as a whole. switch (GetData().role) { - case ui::AX_ROLE_COMBO_BOX: - case ui::AX_ROLE_SEARCH_BOX: + case AX_ROLE_COMBO_BOX: + case AX_ROLE_SEARCH_BOX: return true; - case ui::AX_ROLE_TEXT_FIELD: - return !GetData().HasState(ui::AX_STATE_RICHLY_EDITABLE); + case AX_ROLE_TEXT_FIELD: + return !GetData().HasState(AX_STATE_RICHLY_EDITABLE); default: return false; } @@ -304,13 +296,13 @@ bool AXPlatformNodeBase::IsRichTextControl() { if (!parent) return false; - return GetData().HasState(ui::AX_STATE_RICHLY_EDITABLE) && - (!parent || !parent->GetData().HasState(ui::AX_STATE_RICHLY_EDITABLE)); + return GetData().HasState(AX_STATE_RICHLY_EDITABLE) && + (!parent || !parent->GetData().HasState(AX_STATE_RICHLY_EDITABLE)); } base::string16 AXPlatformNodeBase::GetInnerText() { if (IsTextOnlyObject()) - return GetString16Attribute(ui::AX_ATTR_NAME); + return GetString16Attribute(AX_ATTR_NAME); base::string16 text; for (int i = 0; i < GetChildCount(); ++i) { @@ -326,13 +318,13 @@ base::string16 AXPlatformNodeBase::GetInnerText() { bool AXPlatformNodeBase::IsRangeValueSupported() const { switch (GetData().role) { - case ui::AX_ROLE_PROGRESS_INDICATOR: - case ui::AX_ROLE_SLIDER: - case ui::AX_ROLE_SPIN_BUTTON: - case ui::AX_ROLE_SCROLL_BAR: + case AX_ROLE_PROGRESS_INDICATOR: + case AX_ROLE_SLIDER: + case AX_ROLE_SPIN_BUTTON: + case AX_ROLE_SCROLL_BAR: return true; - case ui::AX_ROLE_SPLITTER: - return GetData().HasState(ui::AX_STATE_FOCUSABLE); + case AX_ROLE_SPLITTER: + return GetData().HasState(AX_STATE_FOCUSABLE); default: return false; } @@ -340,9 +332,9 @@ bool AXPlatformNodeBase::IsRangeValueSupported() const { base::string16 AXPlatformNodeBase::GetRangeValueText() { float fval; - base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE); + base::string16 value = GetString16Attribute(AX_ATTR_VALUE); - if (value.empty() && GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { + if (value.empty() && GetFloatAttribute(AX_ATTR_VALUE_FOR_RANGE, &fval)) { value = base::UTF8ToUTF16(base::DoubleToString(fval)); } return value; @@ -352,7 +344,7 @@ AXPlatformNodeBase* AXPlatformNodeBase::GetTable() const { if (!delegate_) return nullptr; AXPlatformNodeBase* table = const_cast<AXPlatformNodeBase*>(this); - while (table && !ui::IsTableLikeRole(table->GetData().role)) { + while (table && !IsTableLikeRole(table->GetData().role)) { gfx::NativeViewAccessible parent_accessible = table->GetParent(); AXPlatformNodeBase* parent = FromNativeViewAccessible(parent_accessible); @@ -364,15 +356,15 @@ AXPlatformNodeBase* AXPlatformNodeBase::GetTable() const { AXPlatformNodeBase* AXPlatformNodeBase::GetTableCell(int index) const { if (!delegate_) return nullptr; - if (!ui::IsTableLikeRole(GetData().role) && - !ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsTableLikeRole(GetData().role) && + !IsCellOrTableHeaderRole(GetData().role)) return nullptr; AXPlatformNodeBase* table = GetTable(); if (!table) return nullptr; const std::vector<int32_t>& unique_cell_ids = - table->GetIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS); + table->GetIntListAttribute(AX_ATTR_UNIQUE_CELL_IDS); if (index < 0 || index >= static_cast<int>(unique_cell_ids.size())) return nullptr; @@ -382,8 +374,8 @@ AXPlatformNodeBase* AXPlatformNodeBase::GetTableCell(int index) const { AXPlatformNodeBase* AXPlatformNodeBase::GetTableCell(int row, int column) const { - if (!ui::IsTableLikeRole(GetData().role) && - !ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsTableLikeRole(GetData().role) && + !IsCellOrTableHeaderRole(GetData().role)) return nullptr; if (row < 0 || row >= GetTableRowCount() || column < 0 || @@ -398,7 +390,7 @@ AXPlatformNodeBase* AXPlatformNodeBase::GetTableCell(int row, // In contrast to unique cell IDs, these are duplicated whenever a cell spans // multiple columns or rows. const std::vector<int32_t>& cell_ids = - table->GetIntListAttribute(ui::AX_ATTR_CELL_IDS); + table->GetIntListAttribute(AX_ATTR_CELL_IDS); DCHECK_EQ(GetTableRowCount() * GetTableColumnCount(), static_cast<int>(cell_ids.size())); int position = row * GetTableColumnCount() + column; @@ -410,7 +402,7 @@ AXPlatformNodeBase* AXPlatformNodeBase::GetTableCell(int row, } int AXPlatformNodeBase::GetTableCellIndex() const { - if (!ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsCellOrTableHeaderRole(GetData().role)) return -1; AXPlatformNodeBase* table = GetTable(); @@ -418,7 +410,7 @@ int AXPlatformNodeBase::GetTableCellIndex() const { return -1; const std::vector<int32_t>& unique_cell_ids = - table->GetIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS); + table->GetIntListAttribute(AX_ATTR_UNIQUE_CELL_IDS); auto iter = std::find(unique_cell_ids.begin(), unique_cell_ids.end(), GetData().id); if (iter == unique_cell_ids.end()) @@ -428,7 +420,7 @@ int AXPlatformNodeBase::GetTableCellIndex() const { } int AXPlatformNodeBase::GetTableColumn() const { - return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX); + return GetIntAttribute(AX_ATTR_TABLE_CELL_COLUMN_INDEX); } int AXPlatformNodeBase::GetTableColumnCount() const { @@ -436,21 +428,21 @@ int AXPlatformNodeBase::GetTableColumnCount() const { if (!table) return 0; - return table->GetIntAttribute(ui::AX_ATTR_TABLE_COLUMN_COUNT); + return table->GetIntAttribute(AX_ATTR_TABLE_COLUMN_COUNT); } int AXPlatformNodeBase::GetTableColumnSpan() const { - if (!ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsCellOrTableHeaderRole(GetData().role)) return 0; int column_span; - if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &column_span)) + if (GetIntAttribute(AX_ATTR_TABLE_CELL_COLUMN_SPAN, &column_span)) return column_span; return 1; } int AXPlatformNodeBase::GetTableRow() const { - return GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX); + return GetIntAttribute(AX_ATTR_TABLE_CELL_ROW_INDEX); } int AXPlatformNodeBase::GetTableRowCount() const { @@ -458,15 +450,15 @@ int AXPlatformNodeBase::GetTableRowCount() const { if (!table) return 0; - return table->GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT); + return table->GetIntAttribute(AX_ATTR_TABLE_ROW_COUNT); } int AXPlatformNodeBase::GetTableRowSpan() const { - if (!ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsCellOrTableHeaderRole(GetData().role)) return 0; int row_span; - if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &row_span)) + if (GetIntAttribute(AX_ATTR_TABLE_CELL_ROW_SPAN, &row_span)) return row_span; return 1; } diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base.h b/chromium/ui/accessibility/platform/ax_platform_node_base.h index 98aa7f5623e..1481ed97e87 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_base.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_base.h @@ -40,33 +40,31 @@ class AX_EXPORT AXPlatformNodeBase : public AXPlatformNode { AXPlatformNodeBase* GetNextSibling(); bool IsDescendant(AXPlatformNodeBase* descendant); - bool HasBoolAttribute(ui::AXBoolAttribute attr) const; - bool GetBoolAttribute(ui::AXBoolAttribute attr) const; - bool GetBoolAttribute(ui::AXBoolAttribute attr, bool* value) const; - - bool HasFloatAttribute(ui::AXFloatAttribute attr) const; - float GetFloatAttribute(ui::AXFloatAttribute attr) const; - bool GetFloatAttribute(ui::AXFloatAttribute attr, float* value) const; - - bool HasIntAttribute(ui::AXIntAttribute attribute) const; - int GetIntAttribute(ui::AXIntAttribute attribute) const; - bool GetIntAttribute(ui::AXIntAttribute attribute, int* value) const; - - bool HasStringAttribute( - ui::AXStringAttribute attribute) const; - const std::string& GetStringAttribute(ui::AXStringAttribute attribute) const; - bool GetStringAttribute(ui::AXStringAttribute attribute, + bool HasBoolAttribute(AXBoolAttribute attr) const; + bool GetBoolAttribute(AXBoolAttribute attr) const; + bool GetBoolAttribute(AXBoolAttribute attr, bool* value) const; + + bool HasFloatAttribute(AXFloatAttribute attr) const; + float GetFloatAttribute(AXFloatAttribute attr) const; + bool GetFloatAttribute(AXFloatAttribute attr, float* value) const; + + bool HasIntAttribute(AXIntAttribute attribute) const; + int GetIntAttribute(AXIntAttribute attribute) const; + bool GetIntAttribute(AXIntAttribute attribute, int* value) const; + + bool HasStringAttribute(AXStringAttribute attribute) const; + const std::string& GetStringAttribute(AXStringAttribute attribute) const; + bool GetStringAttribute(AXStringAttribute attribute, std::string* value) const; - bool GetString16Attribute(ui::AXStringAttribute attribute, + bool GetString16Attribute(AXStringAttribute attribute, base::string16* value) const; - base::string16 GetString16Attribute( - ui::AXStringAttribute attribute) const; + base::string16 GetString16Attribute(AXStringAttribute attribute) const; - bool HasIntListAttribute(ui::AXIntListAttribute attribute) const; + bool HasIntListAttribute(AXIntListAttribute attribute) const; const std::vector<int32_t>& GetIntListAttribute( - ui::AXIntListAttribute attribute) const; + AXIntListAttribute attribute) const; - bool GetIntListAttribute(ui::AXIntListAttribute attribute, + bool GetIntListAttribute(AXIntListAttribute attribute, std::vector<int32_t>* value) const; // Returns the table or ARIA grid if inside one. diff --git a/chromium/ui/accessibility/platform/ax_platform_node_delegate.h b/chromium/ui/accessibility/platform/ax_platform_node_delegate.h index 052eeb1adf6..ad548c90726 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_delegate.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_delegate.h @@ -38,7 +38,7 @@ class AX_EXPORT AXPlatformNodeDelegate { virtual const AXNodeData& GetData() const = 0; // Get the accessibility tree data for this node. - virtual const ui::AXTreeData& GetTreeData() const = 0; + virtual const AXTreeData& GetTreeData() const = 0; // Get the window the node is contained in. virtual gfx::NativeWindow GetTopLevelWidget() = 0; @@ -73,7 +73,7 @@ class AX_EXPORT AXPlatformNodeDelegate { // has focus. virtual gfx::NativeViewAccessible GetFocus() = 0; - virtual ui::AXPlatformNode* GetFromNodeID(int32_t id) = 0; + virtual AXPlatformNode* GetFromNodeID(int32_t id) = 0; // // Events. @@ -87,9 +87,9 @@ class AX_EXPORT AXPlatformNodeDelegate { // Actions. // - // Perform an accessibility action, switching on the ui::AXAction + // Perform an accessibility action, switching on the AXAction // provided in |data|. - virtual bool AccessibilityPerformAction(const ui::AXActionData& data) = 0; + virtual bool AccessibilityPerformAction(const AXActionData& data) = 0; // // Testing. diff --git a/chromium/ui/accessibility/platform/ax_platform_node_mac.h b/chromium/ui/accessibility/platform/ax_platform_node_mac.h index 73657fb986d..36ee00335e2 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_mac.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_mac.h @@ -36,6 +36,10 @@ class AXPlatformNodeMac : public AXPlatformNodeBase { DISALLOW_COPY_AND_ASSIGN(AXPlatformNodeMac); }; +// Convenience function to determine whether an internal object role should +// expose its accessible name in AXValue (as opposed to AXTitle/AXDescription). +AX_EXPORT bool IsNameExposedInAXValueForRole(AXRole role); + } // namespace ui AX_EXPORT diff --git a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm index c2513cb46d8..bb310ebbb84 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm +++ b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm @@ -36,7 +36,6 @@ RoleMap BuildRoleMap() { {ui::AX_ROLE_AUDIO, NSAccessibilityGroupRole}, {ui::AX_ROLE_BANNER, NSAccessibilityGroupRole}, {ui::AX_ROLE_BLOCKQUOTE, NSAccessibilityGroupRole}, - {ui::AX_ROLE_BUSY_INDICATOR, NSAccessibilityBusyIndicatorRole}, {ui::AX_ROLE_BUTTON, NSAccessibilityButtonRole}, {ui::AX_ROLE_CANVAS, NSAccessibilityImageRole}, {ui::AX_ROLE_CAPTION, NSAccessibilityGroupRole}, @@ -69,7 +68,9 @@ RoleMap BuildRoleMap() { {ui::AX_ROLE_FOOTER, NSAccessibilityGroupRole}, {ui::AX_ROLE_FORM, NSAccessibilityGroupRole}, {ui::AX_ROLE_GENERIC_CONTAINER, NSAccessibilityGroupRole}, - {ui::AX_ROLE_GRID, NSAccessibilityGridRole}, + // Should be NSAccessibilityGridRole but VoiceOver treating it like + // a list as of 10.12.6, so following WebKit and using table role: + {ui::AX_ROLE_GRID, NSAccessibilityTableRole}, // crbug.com/753925 {ui::AX_ROLE_GROUP, NSAccessibilityGroupRole}, {ui::AX_ROLE_HEADING, @"AXHeading"}, {ui::AX_ROLE_IFRAME, NSAccessibilityGroupRole}, @@ -77,7 +78,6 @@ RoleMap BuildRoleMap() { {ui::AX_ROLE_IGNORED, NSAccessibilityUnknownRole}, {ui::AX_ROLE_IMAGE, NSAccessibilityImageRole}, {ui::AX_ROLE_IMAGE_MAP, NSAccessibilityGroupRole}, - {ui::AX_ROLE_IMAGE_MAP_LINK, NSAccessibilityLinkRole}, {ui::AX_ROLE_INPUT_TIME, @"AXTimeField"}, {ui::AX_ROLE_LABEL_TEXT, NSAccessibilityGroupRole}, {ui::AX_ROLE_LEGEND, NSAccessibilityGroupRole}, @@ -104,7 +104,6 @@ RoleMap BuildRoleMap() { {ui::AX_ROLE_NAVIGATION, NSAccessibilityGroupRole}, {ui::AX_ROLE_NONE, NSAccessibilityGroupRole}, {ui::AX_ROLE_NOTE, NSAccessibilityGroupRole}, - {ui::AX_ROLE_OUTLINE, NSAccessibilityOutlineRole}, {ui::AX_ROLE_PARAGRAPH, NSAccessibilityGroupRole}, {ui::AX_ROLE_POP_UP_BUTTON, NSAccessibilityPopUpButtonRole}, {ui::AX_ROLE_PRE, NSAccessibilityGroupRole}, @@ -116,7 +115,6 @@ RoleMap BuildRoleMap() { {ui::AX_ROLE_ROOT_WEB_AREA, @"AXWebArea"}, {ui::AX_ROLE_ROW, NSAccessibilityRowRole}, {ui::AX_ROLE_ROW_HEADER, @"AXCell"}, - {ui::AX_ROLE_RULER, NSAccessibilityRulerRole}, {ui::AX_ROLE_SCROLL_BAR, NSAccessibilityScrollBarRole}, {ui::AX_ROLE_SEARCH, NSAccessibilityGroupRole}, {ui::AX_ROLE_SEARCH_BOX, NSAccessibilityTextFieldRole}, @@ -146,10 +144,6 @@ RoleMap BuildRoleMap() { {ui::AX_ROLE_VIDEO, NSAccessibilityGroupRole}, {ui::AX_ROLE_WEB_AREA, @"AXWebArea"}, {ui::AX_ROLE_WINDOW, NSAccessibilityWindowRole}, - - // TODO(dtseng): we don't correctly support the attributes for these - // roles. - // { ui::AX_ROLE_SCROLL_AREA, NSAccessibilityScrollAreaRole }, }; return RoleMap(begin(roles), end(roles)); @@ -636,14 +630,13 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { } - (id)AXValue { - switch (node_->GetData().role) { - case ui::AX_ROLE_TAB: - return [self AXSelected]; - case ui::AX_ROLE_STATIC_TEXT: - return [self AXTitle]; - default: - break; - } + ui::AXRole role = node_->GetData().role; + if (role == ui::AX_ROLE_TAB) + return [self AXSelected]; + + if (ui::IsNameExposedInAXValueForRole(role)) + return [self getStringAttribute:ui::AX_ATTR_NAME]; + return [self getStringAttribute:ui::AX_ATTR_VALUE]; } @@ -693,6 +686,9 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { } - (NSString*)AXTitle { + if (ui::IsNameExposedInAXValueForRole(node_->GetData().role)) + return @""; + return [self getStringAttribute:ui::AX_ATTR_NAME]; } @@ -860,4 +856,16 @@ int AXPlatformNodeMac::GetIndexInParent() { return -1; } +bool IsNameExposedInAXValueForRole(AXRole role) { + switch (role) { + case AX_ROLE_LIST_BOX_OPTION: + case AX_ROLE_LIST_MARKER: + case AX_ROLE_MENU_LIST_OPTION: + case AX_ROLE_STATIC_TEXT: + return true; + default: + return false; + } +} + } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.cc b/chromium/ui/accessibility/platform/ax_platform_node_win.cc index 25c8c227dd9..57db0173a5a 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.cc @@ -2,12 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <atlbase.h> -#include <atlcom.h> -#include <limits.h> -#include <oleacc.h> -#include <stdint.h> - +#include "ui/accessibility/platform/ax_platform_node_win.h" #include "base/containers/hash_tables.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" @@ -19,12 +14,13 @@ #include "third_party/iaccessible2/ia2_api_all.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/accessibility/ax_action_data.h" +#include "ui/accessibility/ax_mode_observer.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_role_properties.h" #include "ui/accessibility/ax_text_utils.h" #include "ui/accessibility/ax_tree_data.h" #include "ui/accessibility/platform/ax_platform_node_delegate.h" -#include "ui/accessibility/platform/ax_platform_node_win.h" +#include "ui/accessibility/platform/ax_platform_unique_id.h" #include "ui/base/win/atl_module.h" #include "ui/gfx/geometry/rect_conversions.h" @@ -155,6 +151,12 @@ base::LazyInstance<base::ObserverList<IAccessible2UsageObserver>>:: } // namespace +// There is no easy way to decouple |kScreenReader| and |kHTML| accessibility +// modes when Windows screen readers are used. For example, certain roles use +// the HTML tag name. Input fields require their type attribute to be exposed. +const uint32_t kScreenReaderAndHTMLAccessibilityModes = + AXMode::kScreenReader | AXMode::kHTML; + // // IAccessible2UsageObserver // @@ -172,7 +174,7 @@ base::ObserverList<IAccessible2UsageObserver>& } AXPlatformNodeRelationWin::AXPlatformNodeRelationWin() { - ui::win::CreateATLModuleIfNeeded(); + win::CreateATLModuleIfNeeded(); } AXPlatformNodeRelationWin::~AXPlatformNodeRelationWin() {} @@ -205,16 +207,16 @@ STDMETHODIMP AXPlatformNodeRelationWin::get_relationType(BSTR* relation_type) { return S_OK; } -STDMETHODIMP AXPlatformNodeRelationWin::get_nTargets(long* n_targets) { +STDMETHODIMP AXPlatformNodeRelationWin::get_nTargets(LONG* n_targets) { if (!n_targets) return E_INVALIDARG; if (!owner_->delegate_) return E_FAIL; - *n_targets = static_cast<long>(target_ids_.size()); + *n_targets = static_cast<LONG>(target_ids_.size()); - for (long i = *n_targets - 1; i >= 0; --i) { + for (LONG i = *n_targets - 1; i >= 0; --i) { AXPlatformNodeWin* result = static_cast<AXPlatformNodeWin*>( owner_->delegate_->GetFromNodeID(target_ids_[i])); if (!result || !result->delegate_) { @@ -225,7 +227,7 @@ STDMETHODIMP AXPlatformNodeRelationWin::get_nTargets(long* n_targets) { return S_OK; } -STDMETHODIMP AXPlatformNodeRelationWin::get_target(long target_index, +STDMETHODIMP AXPlatformNodeRelationWin::get_target(LONG target_index, IUnknown** target) { if (!target) return E_INVALIDARG; @@ -234,7 +236,7 @@ STDMETHODIMP AXPlatformNodeRelationWin::get_target(long target_index, return E_FAIL; if (target_index < 0 || - target_index >= static_cast<long>(target_ids_.size())) { + target_index >= static_cast<LONG>(target_ids_.size())) { return E_INVALIDARG; } @@ -248,16 +250,16 @@ STDMETHODIMP AXPlatformNodeRelationWin::get_target(long target_index, return S_OK; } -STDMETHODIMP AXPlatformNodeRelationWin::get_targets(long max_targets, +STDMETHODIMP AXPlatformNodeRelationWin::get_targets(LONG max_targets, IUnknown** targets, - long* n_targets) { + LONG* n_targets) { if (!targets || !n_targets) return E_INVALIDARG; if (!owner_->delegate_) return E_FAIL; - long count = static_cast<long>(target_ids_.size()); + LONG count = static_cast<LONG>(target_ids_.size()); if (count > max_targets) count = max_targets; @@ -265,7 +267,7 @@ STDMETHODIMP AXPlatformNodeRelationWin::get_targets(long max_targets, if (count == 0) return S_FALSE; - for (long i = 0; i < count; ++i) { + for (LONG i = 0; i < count; ++i) { HRESULT result = get_target(i, &targets[i]); if (result != S_OK) return result; @@ -286,7 +288,7 @@ AXPlatformNodeRelationWin::get_localizedRelationType(BSTR* relation_type) { // static AXPlatformNode* AXPlatformNode::Create(AXPlatformNodeDelegate* delegate) { // Make sure ATL is initialized in this module. - ui::win::CreateATLModuleIfNeeded(); + win::CreateATLModuleIfNeeded(); CComObject<AXPlatformNodeWin>* instance = nullptr; HRESULT hr = CComObject<AXPlatformNodeWin>::CreateInstance(&instance); @@ -306,33 +308,50 @@ AXPlatformNode* AXPlatformNode::FromNativeViewAccessible( return ax_platform_node.Get(); } +using UniqueIdMap = base::hash_map<int32_t, AXPlatformNode*>; +// Map from each AXPlatformNode's unique id to its instance. +base::LazyInstance<UniqueIdMap>::DestructorAtExit g_unique_id_map = + LAZY_INSTANCE_INITIALIZER; + +// static +AXPlatformNode* AXPlatformNodeWin::GetFromUniqueId(int32_t unique_id) { + UniqueIdMap* unique_ids = g_unique_id_map.Pointer(); + auto iter = unique_ids->find(unique_id); + if (iter != unique_ids->end()) + return iter->second; + + return nullptr; +} // // AXPlatformNodeWin // -AXPlatformNodeWin::AXPlatformNodeWin() { +AXPlatformNodeWin::AXPlatformNodeWin() + : unique_id_(GetNextAXPlatformNodeUniqueId()) { + g_unique_id_map.Get()[unique_id_] = this; } AXPlatformNodeWin::~AXPlatformNodeWin() { - for (ui::AXPlatformNodeRelationWin* relation : relations_) + for (AXPlatformNodeRelationWin* relation : relations_) relation->Release(); + if (unique_id_) + g_unique_id_map.Get().erase(unique_id_); } void AXPlatformNodeWin::CalculateRelationships() { ClearOwnRelations(); AddBidirectionalRelations(IA2_RELATION_CONTROLLER_FOR, - IA2_RELATION_CONTROLLED_BY, - ui::AX_ATTR_CONTROLS_IDS); + IA2_RELATION_CONTROLLED_BY, AX_ATTR_CONTROLS_IDS); AddBidirectionalRelations(IA2_RELATION_DESCRIBED_BY, IA2_RELATION_DESCRIPTION_FOR, - ui::AX_ATTR_DESCRIBEDBY_IDS); + AX_ATTR_DESCRIBEDBY_IDS); AddBidirectionalRelations(IA2_RELATION_FLOWS_TO, IA2_RELATION_FLOWS_FROM, - ui::AX_ATTR_FLOWTO_IDS); + AX_ATTR_FLOWTO_IDS); AddBidirectionalRelations(IA2_RELATION_LABELLED_BY, IA2_RELATION_LABEL_FOR, - ui::AX_ATTR_LABELLEDBY_IDS); + AX_ATTR_LABELLEDBY_IDS); int32_t details_id; - if (GetIntAttribute(ui::AX_ATTR_DETAILS_ID, &details_id)) { + if (GetIntAttribute(AX_ATTR_DETAILS_ID, &details_id)) { std::vector<int32_t> details_ids; details_ids.push_back(details_id); AddBidirectionalRelations(IA2_RELATION_DETAILS, IA2_RELATION_DETAILS_FOR, @@ -340,11 +359,11 @@ void AXPlatformNodeWin::CalculateRelationships() { } int member_of_id; - if (GetIntAttribute(ui::AX_ATTR_MEMBER_OF_ID, &member_of_id)) + if (GetIntAttribute(AX_ATTR_MEMBER_OF_ID, &member_of_id)) AddRelation(IA2_RELATION_MEMBER_OF, member_of_id); int error_message_id; - if (GetIntAttribute(ui::AX_ATTR_ERRORMESSAGE_ID, &error_message_id)) + if (GetIntAttribute(AX_ATTR_ERRORMESSAGE_ID, &error_message_id)) AddRelation(IA2_RELATION_ERROR_MESSAGE, error_message_id); } @@ -354,9 +373,8 @@ void AXPlatformNodeWin::AddRelation(const base::string16& relation_type, if (target_id == GetData().id) return; - CComObject<ui::AXPlatformNodeRelationWin>* relation; - HRESULT hr = - CComObject<ui::AXPlatformNodeRelationWin>::CreateInstance(&relation); + CComObject<AXPlatformNodeRelationWin>* relation; + HRESULT hr = CComObject<AXPlatformNodeRelationWin>::CreateInstance(&relation); DCHECK(SUCCEEDED(hr)); relation->AddRef(); relation->Initialize(this, relation_type); @@ -367,7 +385,7 @@ void AXPlatformNodeWin::AddRelation(const base::string16& relation_type, void AXPlatformNodeWin::AddBidirectionalRelations( const base::string16& relation_type, const base::string16& reverse_relation_type, - ui::AXIntListAttribute attribute) { + AXIntListAttribute attribute) { if (!HasIntListAttribute(attribute)) return; @@ -388,9 +406,8 @@ void AXPlatformNodeWin::AddBidirectionalRelations( if (filtered_target_ids.empty()) return; - CComObject<ui::AXPlatformNodeRelationWin>* relation; - HRESULT hr = - CComObject<ui::AXPlatformNodeRelationWin>::CreateInstance(&relation); + CComObject<AXPlatformNodeRelationWin>* relation; + HRESULT hr = CComObject<AXPlatformNodeRelationWin>::CreateInstance(&relation); DCHECK(SUCCEEDED(hr)); relation->AddRef(); relation->Initialize(this, relation_type); @@ -423,7 +440,7 @@ void AXPlatformNodeWin::ClearOwnRelations() { relations_.erase( std::remove_if(relations_.begin(), relations_.end(), - [](ui::AXPlatformNodeRelationWin* relation) { + [](AXPlatformNodeRelationWin* relation) { if (relation->get_type() == IA2_RELATION_MEMBER_OF) { relation->Release(); return true; @@ -437,7 +454,7 @@ void AXPlatformNodeWin::RemoveBidirectionalRelationsOfType( const base::string16& relation_type, const base::string16& reverse_relation_type) { for (auto iter = relations_.begin(); iter != relations_.end();) { - ui::AXPlatformNodeRelationWin* relation = *iter; + AXPlatformNodeRelationWin* relation = *iter; DCHECK(relation); if (relation->get_type() == relation_type) { for (int target_id : relation->get_target_ids()) { @@ -460,7 +477,7 @@ void AXPlatformNodeWin::RemoveTargetFromRelation( const base::string16& relation_type, int target_id) { for (auto iter = relations_.begin(); iter != relations_.end();) { - ui::AXPlatformNodeRelationWin* relation = *iter; + AXPlatformNodeRelationWin* relation = *iter; DCHECK(relation); if (relation->get_type() == relation_type) { // If |target_id| is not present, |RemoveTarget| will do nothing. @@ -492,7 +509,7 @@ void AXPlatformNodeWin::SanitizeStringAttributeForIA2( void AXPlatformNodeWin::StringAttributeToIA2( std::vector<base::string16>& attributes, - ui::AXStringAttribute attribute, + AXStringAttribute attribute, const char* ia2_attr) { base::string16 value; if (GetString16Attribute(attribute, &value)) { @@ -503,7 +520,7 @@ void AXPlatformNodeWin::StringAttributeToIA2( void AXPlatformNodeWin::BoolAttributeToIA2( std::vector<base::string16>& attributes, - ui::AXBoolAttribute attribute, + AXBoolAttribute attribute, const char* ia2_attr) { bool value; if (GetBoolAttribute(attribute, &value)) { @@ -514,7 +531,7 @@ void AXPlatformNodeWin::BoolAttributeToIA2( void AXPlatformNodeWin::IntAttributeToIA2( std::vector<base::string16>& attributes, - ui::AXIntAttribute attribute, + AXIntAttribute attribute, const char* ia2_attr) { int value; if (GetIntAttribute(attribute, &value)) { @@ -532,7 +549,13 @@ void AXPlatformNodeWin::Dispose() { } void AXPlatformNodeWin::Destroy() { + g_unique_id_map.Get().erase(unique_id_); + unique_id_ = 0; + RemoveAlertTarget(); + + // This will end up calling Dispose() which may result in deleting this object + // if there are no more outstanding references. AXPlatformNodeBase::Destroy(); } @@ -544,16 +567,15 @@ gfx::NativeViewAccessible AXPlatformNodeWin::GetNativeViewAccessible() { return this; } -void AXPlatformNodeWin::NotifyAccessibilityEvent(ui::AXEvent event_type) { +void AXPlatformNodeWin::NotifyAccessibilityEvent(AXEvent event_type) { HWND hwnd = delegate_->GetTargetForNativeAccessibilityEvent(); if (!hwnd) return; // Menu items fire selection events but Windows screen readers work reliably // with focus events. Remap here. - if (event_type == ui::AX_EVENT_SELECTION && - GetData().role == ui::AX_ROLE_MENU_ITEM) - event_type = ui::AX_EVENT_FOCUS; + if (event_type == AX_EVENT_SELECTION && GetData().role == AX_ROLE_MENU_ITEM) + event_type = AX_EVENT_FOCUS; int native_event = MSAAEvent(event_type); if (native_event < EVENT_MIN) @@ -562,7 +584,7 @@ void AXPlatformNodeWin::NotifyAccessibilityEvent(ui::AXEvent event_type) { ::NotifyWinEvent(native_event, hwnd, OBJID_CLIENT, -unique_id_); // Keep track of objects that are a target of an alert event. - if (event_type == ui::AX_EVENT_ALERT) + if (event_type == AX_EVENT_ALERT) AddAlertTarget(); } @@ -645,7 +667,7 @@ HRESULT AXPlatformNodeWin::accDoDefaultAction(VARIANT var_id) { AXPlatformNodeWin* target; COM_OBJECT_VALIDATE_VAR_ID_AND_GET_TARGET(var_id, target); AXActionData data; - data.action = ui::AX_ACTION_DO_DEFAULT; + data.action = AX_ACTION_DO_DEFAULT; if (target->delegate_->AccessibilityPerformAction(data)) return S_OK; @@ -712,8 +734,8 @@ STDMETHODIMP AXPlatformNodeWin::accNavigate( case NAVDIR_DOWN: { // This direction is not implemented except in tables. - if (!ui::IsTableLikeRole(GetData().role) && - !ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsTableLikeRole(GetData().role) && + !IsCellOrTableHeaderRole(GetData().role)) return E_NOTIMPL; AXPlatformNodeBase* next = target->GetTableCell( @@ -727,8 +749,8 @@ STDMETHODIMP AXPlatformNodeWin::accNavigate( case NAVDIR_UP: { // This direction is not implemented except in tables. - if (!ui::IsTableLikeRole(GetData().role) && - !ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsTableLikeRole(GetData().role) && + !IsCellOrTableHeaderRole(GetData().role)) return E_NOTIMPL; AXPlatformNodeBase* next = @@ -742,8 +764,8 @@ STDMETHODIMP AXPlatformNodeWin::accNavigate( case NAVDIR_LEFT: { // This direction is not implemented except in tables. - if (!ui::IsTableLikeRole(GetData().role) && - !ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsTableLikeRole(GetData().role) && + !IsCellOrTableHeaderRole(GetData().role)) return E_NOTIMPL; AXPlatformNodeBase* next = @@ -758,8 +780,8 @@ STDMETHODIMP AXPlatformNodeWin::accNavigate( case NAVDIR_RIGHT: { // This direction is not implemented except in tables. - if (!ui::IsTableLikeRole(GetData().role) && - !ui::IsCellOrTableHeaderRole(GetData().role)) + if (!IsTableLikeRole(GetData().role) && + !IsCellOrTableHeaderRole(GetData().role)) return E_NOTIMPL; AXPlatformNodeBase* next = target->GetTableCell( @@ -798,7 +820,6 @@ STDMETHODIMP AXPlatformNodeWin::get_accChild(VARIANT var_child, STDMETHODIMP AXPlatformNodeWin::get_accChildCount(LONG* child_count) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_CHILD_COUNT); - COM_OBJECT_VALIDATE_1_ARG(child_count); *child_count = delegate_->GetChildCount(); return S_OK; @@ -809,6 +830,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accDefaultAction( WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_DEFAULT_ACTION); AXPlatformNodeWin* target; COM_OBJECT_VALIDATE_VAR_ID_1_ARG_AND_GET_TARGET(var_id, def_action, target); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); int action; if (!target->GetIntAttribute(AX_ATTR_DEFAULT_ACTION_VERB, &action)) { @@ -834,7 +856,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accDescription( AXPlatformNodeWin* target; COM_OBJECT_VALIDATE_VAR_ID_1_ARG_AND_GET_TARGET(var_id, desc, target); - return target->GetStringAttributeAsBstr(ui::AX_ATTR_DESCRIPTION, desc); + return target->GetStringAttributeAsBstr(AX_ATTR_DESCRIPTION, desc); } STDMETHODIMP AXPlatformNodeWin::get_accFocus(VARIANT* focus_child) { @@ -862,7 +884,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accKeyboardShortcut( AXPlatformNodeWin* target; COM_OBJECT_VALIDATE_VAR_ID_1_ARG_AND_GET_TARGET(var_id, acc_key, target); - return target->GetStringAttributeAsBstr(ui::AX_ATTR_KEY_SHORTCUTS, acc_key); + return target->GetStringAttributeAsBstr(AX_ATTR_KEY_SHORTCUTS, acc_key); } STDMETHODIMP AXPlatformNodeWin::get_accName( @@ -871,7 +893,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accName( AXPlatformNodeWin* target; COM_OBJECT_VALIDATE_VAR_ID_1_ARG_AND_GET_TARGET(var_id, name, target); - HRESULT result = target->GetStringAttributeAsBstr(ui::AX_ATTR_NAME, name); + HRESULT result = target->GetStringAttributeAsBstr(AX_ATTR_NAME, name); if (FAILED(result) && MSAARole() == ROLE_SYSTEM_DOCUMENT && GetParent()) { // Hack: Some versions of JAWS crash if they get an empty name on // a document that's the child of an iframe, so always return a @@ -956,9 +978,9 @@ STDMETHODIMP AXPlatformNodeWin::get_accValue(VARIANT var_id, BSTR* value) { // // Color Well special case (Use AX_ATTR_COLOR_VALUE) // - if (target->GetData().role == ui::AX_ROLE_COLOR_WELL) { + if (target->GetData().role == AX_ROLE_COLOR_WELL) { unsigned int color = static_cast<unsigned int>(target->GetIntAttribute( - ui::AX_ATTR_COLOR_VALUE)); // todo, why the static cast? + AX_ATTR_COLOR_VALUE)); // todo, why the static cast? unsigned int red = SkColorGetR(color); unsigned int green = SkColorGetG(color); @@ -975,8 +997,8 @@ STDMETHODIMP AXPlatformNodeWin::get_accValue(VARIANT var_id, BSTR* value) { // // Document special case (Use the document's url) // - if (target->GetData().role == ui::AX_ROLE_ROOT_WEB_AREA || - target->GetData().role == ui::AX_ROLE_WEB_AREA) { + if (target->GetData().role == AX_ROLE_ROOT_WEB_AREA || + target->GetData().role == AX_ROLE_WEB_AREA) { result = base::UTF8ToUTF16(target->delegate_->GetTreeData().url); *value = SysAllocString(result.c_str()); DCHECK(*value); @@ -986,9 +1008,8 @@ STDMETHODIMP AXPlatformNodeWin::get_accValue(VARIANT var_id, BSTR* value) { // // Links (Use AX_ATTR_URL) // - if (target->GetData().role == ui::AX_ROLE_LINK || - target->GetData().role == ui::AX_ROLE_IMAGE_MAP_LINK) { - result = target->GetString16Attribute(ui::AX_ATTR_URL); + if (target->GetData().role == AX_ROLE_LINK) { + result = target->GetString16Attribute(AX_ATTR_URL); *value = SysAllocString(result.c_str()); DCHECK(*value); return S_OK; @@ -997,14 +1018,14 @@ STDMETHODIMP AXPlatformNodeWin::get_accValue(VARIANT var_id, BSTR* value) { // After this point, the role based special cases should test for an empty // result. - result = target->GetString16Attribute(ui::AX_ATTR_VALUE); + result = target->GetString16Attribute(AX_ATTR_VALUE); // // RangeValue (Use AX_ATTR_VALUE_FOR_RANGE) // if (result.empty() && target->IsRangeValueSupported()) { float fval; - if (target->GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { + if (target->GetFloatAttribute(AX_ATTR_VALUE_FOR_RANGE, &fval)) { result = base::UTF8ToUTF16(base::DoubleToString(fval)); *value = SysAllocString(result.c_str()); DCHECK(*value); @@ -1030,7 +1051,7 @@ STDMETHODIMP AXPlatformNodeWin::put_accValue(VARIANT var_id, COM_OBJECT_VALIDATE_VAR_ID_AND_GET_TARGET(var_id, target); AXActionData data; - data.action = ui::AX_ACTION_SET_VALUE; + data.action = AX_ACTION_SET_VALUE; data.value = new_value; if (target->delegate_->AccessibilityPerformAction(data)) return S_OK; @@ -1041,7 +1062,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accSelection(VARIANT* selected) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACC_SELECTION); COM_OBJECT_VALIDATE_1_ARG(selected); - if (GetData().role != ui::AX_ROLE_LIST_BOX) + if (GetData().role != AX_ROLE_LIST_BOX) return E_NOTIMPL; unsigned long selected_count = 0; @@ -1049,7 +1070,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accSelection(VARIANT* selected) { AXPlatformNodeWin* node = static_cast<AXPlatformNodeWin*>( FromNativeViewAccessible(delegate_->ChildAtIndex(i))); - if (node && node->GetData().state & (1 << ui::AX_STATE_SELECTED)) + if (node && node->GetData().HasState(AX_STATE_SELECTED)) ++selected_count; } @@ -1063,7 +1084,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accSelection(VARIANT* selected) { AXPlatformNodeWin* node = static_cast<AXPlatformNodeWin*>( FromNativeViewAccessible(delegate_->ChildAtIndex(i))); - if (node && node->GetData().state & (1 << ui::AX_STATE_SELECTED)) { + if (node && node->GetData().HasState(AX_STATE_SELECTED)) { selected->vt = VT_DISPATCH; selected->pdispVal = node; node->AddRef(); @@ -1081,7 +1102,7 @@ STDMETHODIMP AXPlatformNodeWin::get_accSelection(VARIANT* selected) { AXPlatformNodeWin* node = static_cast<AXPlatformNodeWin*>( FromNativeViewAccessible(delegate_->ChildAtIndex(i))); - if (node && node->GetData().state & (1 << ui::AX_STATE_SELECTED)) { + if (node && node->GetData().HasState(AX_STATE_SELECTED)) { enum_variant->ItemAt(index)->vt = VT_DISPATCH; enum_variant->ItemAt(index)->pdispVal = node; node->AddRef(); @@ -1101,8 +1122,8 @@ STDMETHODIMP AXPlatformNodeWin::accSelect( COM_OBJECT_VALIDATE_VAR_ID_AND_GET_TARGET(var_id, target); if (flagsSelect & SELFLAG_TAKEFOCUS) { - ui::AXActionData action_data; - action_data.action = ui::AX_ACTION_FOCUS; + AXActionData action_data; + action_data.action = AX_ACTION_FOCUS; target->delegate_->AccessibilityPerformAction(action_data); return S_OK; } @@ -1151,27 +1172,30 @@ STDMETHODIMP AXPlatformNodeWin::role(LONG* role) { STDMETHODIMP AXPlatformNodeWin::get_states(AccessibleStates* states) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_STATES); COM_OBJECT_VALIDATE_1_ARG(states); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + *states = ComputeIA2State(); return S_OK; } STDMETHODIMP AXPlatformNodeWin::get_uniqueID(LONG* unique_id) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_UNIQUE_ID); COM_OBJECT_VALIDATE_1_ARG(unique_id); *unique_id = -unique_id_; return S_OK; } STDMETHODIMP AXPlatformNodeWin::get_windowHandle(HWND* window_handle) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_WINDOW_HANDLE); COM_OBJECT_VALIDATE_1_ARG(window_handle); *window_handle = delegate_->GetTargetForNativeAccessibilityEvent(); return *window_handle ? S_OK : S_FALSE; } -STDMETHODIMP AXPlatformNodeWin::get_relationTargetsOfType( - BSTR type_bstr, - long max_targets, - IUnknown ***targets, - long *n_targets) { +STDMETHODIMP AXPlatformNodeWin::get_relationTargetsOfType(BSTR type_bstr, + LONG max_targets, + IUnknown*** targets, + LONG* n_targets) { COM_OBJECT_VALIDATE_2_ARGS(targets, n_targets); *n_targets = 0; @@ -1193,7 +1217,7 @@ STDMETHODIMP AXPlatformNodeWin::get_relationTargetsOfType( alert_targets.push_back(target); } - long count = static_cast<long>(alert_targets.size()); + LONG count = static_cast<LONG>(alert_targets.size()); if (count == 0) return S_FALSE; @@ -1208,7 +1232,7 @@ STDMETHODIMP AXPlatformNodeWin::get_relationTargetsOfType( // Allocate COM memory for the result array and populate it. *targets = static_cast<IUnknown**>( CoTaskMemAlloc(count * sizeof(IUnknown*))); - for (long i = 0; i < count; ++i) { + for (LONG i = 0; i < count; ++i) { (*targets)[i] = static_cast<IAccessible*>(alert_targets[i]); (*targets)[i]->AddRef(); } @@ -1216,17 +1240,18 @@ STDMETHODIMP AXPlatformNodeWin::get_relationTargetsOfType( } STDMETHODIMP AXPlatformNodeWin::get_attributes(BSTR* attributes) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_IA2_GET_ATTRIBUTES); COM_OBJECT_VALIDATE_1_ARG(attributes); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + *attributes = nullptr; + base::string16 attributes_str; + std::vector<base::string16> computed_attributes = ComputeIA2Attributes(); + for (const base::string16& attribute : computed_attributes) + attributes_str += attribute + L';'; - // Text fields need to report the attribute "text-model:a1" to instruct - // screen readers to use IAccessible2 APIs to handle text editing in this - // object (as opposed to treating it like a native Windows text box). - // The text-model:a1 attribute is documented here: - // http://www.linuxfoundation.org/collaborate/workgroups/accessibility/ia2/ia2_implementation_guide - if (GetData().role == ui::AX_ROLE_TEXT_FIELD) { - attributes_str = L"text-model:a1;"; - } + if (attributes_str.empty()) + return S_FALSE; *attributes = SysAllocString(attributes_str.c_str()); DCHECK(*attributes); @@ -1234,6 +1259,7 @@ STDMETHODIMP AXPlatformNodeWin::get_attributes(BSTR* attributes) { } STDMETHODIMP AXPlatformNodeWin::get_indexInParent(LONG* index_in_parent) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_INDEX_IN_PARENT); COM_OBJECT_VALIDATE_1_ARG(index_in_parent); *index_in_parent = GetIndexInParent(); if (*index_in_parent < 0) @@ -1243,16 +1269,20 @@ STDMETHODIMP AXPlatformNodeWin::get_indexInParent(LONG* index_in_parent) { } STDMETHODIMP AXPlatformNodeWin::get_nRelations(LONG* n_relations) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_RELATIONS); COM_OBJECT_VALIDATE_1_ARG(n_relations); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); *n_relations = static_cast<LONG>(relations_.size()); return S_OK; } STDMETHODIMP AXPlatformNodeWin::get_relation(LONG relation_index, IAccessibleRelation** relation) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_RELATION); COM_OBJECT_VALIDATE_1_ARG(relation); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); if (relation_index < 0 || - relation_index >= static_cast<long>(relations_.size())) { + relation_index >= static_cast<LONG>(relations_.size())) { return E_INVALIDARG; } @@ -1264,13 +1294,16 @@ STDMETHODIMP AXPlatformNodeWin::get_relation(LONG relation_index, STDMETHODIMP AXPlatformNodeWin::get_relations(LONG max_relations, IAccessibleRelation** relations, LONG* n_relations) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_RELATIONS); COM_OBJECT_VALIDATE_2_ARGS(relations, n_relations); - long count = static_cast<long>(relations_.size()); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + + LONG count = static_cast<LONG>(relations_.size()); *n_relations = count; if (count == 0) return S_FALSE; - for (long i = 0; i < count; ++i) { + for (LONG i = 0; i < count; ++i) { relations_[i]->AddRef(); relations[i] = relations_[i]; } @@ -1278,6 +1311,33 @@ STDMETHODIMP AXPlatformNodeWin::get_relations(LONG max_relations, return S_OK; } +STDMETHODIMP AXPlatformNodeWin::get_groupPosition(LONG* group_level, + LONG* similar_items_in_group, + LONG* position_in_group) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_GROUP_POSITION); + COM_OBJECT_VALIDATE_3_ARGS(group_level, similar_items_in_group, + position_in_group); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + + *group_level = GetIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL); + *similar_items_in_group = GetIntAttribute(ui::AX_ATTR_SET_SIZE); + *position_in_group = GetIntAttribute(ui::AX_ATTR_POS_IN_SET); + + if (*group_level == *similar_items_in_group == *position_in_group == 0) + return S_FALSE; + return S_OK; +} + +STDMETHODIMP AXPlatformNodeWin::get_localizedExtendedRole( + BSTR* localized_extended_role) { + WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_LOCALIZED_EXTENDED_ROLE); + COM_OBJECT_VALIDATE_1_ARG(localized_extended_role); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + + return GetStringAttributeAsBstr(ui::AX_ATTR_ROLE_DESCRIPTION, + localized_extended_role); +} + // // IAccessible2 methods not implemented. // @@ -1300,17 +1360,6 @@ STDMETHODIMP AXPlatformNodeWin::scrollToPoint( return E_NOTIMPL; } -STDMETHODIMP AXPlatformNodeWin::get_groupPosition(LONG* group_level, - LONG* similar_items_in_group, - LONG* position_in_group) { - return E_NOTIMPL; -} - -STDMETHODIMP AXPlatformNodeWin::get_localizedExtendedRole( - BSTR* localized_extended_role) { - return E_NOTIMPL; -} - STDMETHODIMP AXPlatformNodeWin::get_nExtendedStates(LONG* n_extended_states) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_EXTENDED_STATES); return E_NOTIMPL; @@ -1338,7 +1387,7 @@ STDMETHODIMP AXPlatformNodeWin::get_locale(IA2Locale* locale) { } STDMETHODIMP AXPlatformNodeWin::get_accessibleWithCaret(IUnknown** accessible, - long* caret_offset) { + LONG* caret_offset) { return E_NOTIMPL; } @@ -1346,10 +1395,12 @@ STDMETHODIMP AXPlatformNodeWin::get_accessibleWithCaret(IUnknown** accessible, // IAccessibleTable methods. // -STDMETHODIMP AXPlatformNodeWin::get_accessibleAt(long row, - long column, +STDMETHODIMP AXPlatformNodeWin::get_accessibleAt(LONG row, + LONG column, IUnknown** accessible) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ACCESSIBLE_AT); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!accessible) return E_INVALIDARG; @@ -1369,6 +1420,8 @@ STDMETHODIMP AXPlatformNodeWin::get_accessibleAt(long row, STDMETHODIMP AXPlatformNodeWin::get_caption(IUnknown** accessible) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CAPTION); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!accessible) return E_INVALIDARG; @@ -1377,14 +1430,17 @@ STDMETHODIMP AXPlatformNodeWin::get_caption(IUnknown** accessible) { return S_FALSE; } -STDMETHODIMP AXPlatformNodeWin::get_childIndex(long row, - long column, - long* cell_index) { +STDMETHODIMP AXPlatformNodeWin::get_childIndex(LONG row, + LONG column, + LONG* cell_index) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_CHILD_INDEX); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!cell_index) return E_INVALIDARG; - auto* cell = GetTableCell(static_cast<int>(row), static_cast<int>(column)); + AXPlatformNodeBase* cell = + GetTableCell(static_cast<int>(row), static_cast<int>(column)); if (cell) { *cell_index = static_cast<LONG>(cell->GetTableCellIndex()); return S_OK; @@ -1394,9 +1450,11 @@ STDMETHODIMP AXPlatformNodeWin::get_childIndex(long row, return E_INVALIDARG; } -STDMETHODIMP AXPlatformNodeWin::get_columnDescription(long column, +STDMETHODIMP AXPlatformNodeWin::get_columnDescription(LONG column, BSTR* description) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_DESCRIPTION); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!description) return E_INVALIDARG; @@ -1410,16 +1468,16 @@ STDMETHODIMP AXPlatformNodeWin::get_columnDescription(long column, return S_FALSE; } - for (int i = 0; i < rows; ++i) { - auto* cell = GetTableCell(i, column); - if (cell && cell->GetData().role == ui::AX_ROLE_COLUMN_HEADER) { - base::string16 cell_name = cell->GetString16Attribute(ui::AX_ATTR_NAME); + for (int r = 0; r < rows; ++r) { + AXPlatformNodeBase* cell = GetTableCell(r, column); + if (cell && cell->GetData().role == AX_ROLE_COLUMN_HEADER) { + base::string16 cell_name = cell->GetString16Attribute(AX_ATTR_NAME); if (cell_name.size() > 0) { *description = SysAllocString(cell_name.c_str()); return S_OK; } - cell_name = cell->GetString16Attribute(ui::AX_ATTR_DESCRIPTION); + cell_name = cell->GetString16Attribute(AX_ATTR_DESCRIPTION); if (cell_name.size() > 0) { *description = SysAllocString(cell_name.c_str()); return S_OK; @@ -1431,14 +1489,17 @@ STDMETHODIMP AXPlatformNodeWin::get_columnDescription(long column, return S_FALSE; } -STDMETHODIMP AXPlatformNodeWin::get_columnExtentAt(long row, - long column, - long* n_columns_spanned) { +STDMETHODIMP AXPlatformNodeWin::get_columnExtentAt(LONG row, + LONG column, + LONG* n_columns_spanned) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_EXTENT_AT); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!n_columns_spanned) return E_INVALIDARG; - auto* cell = GetTableCell(static_cast<int>(row), static_cast<int>(column)); + AXPlatformNodeBase* cell = + GetTableCell(static_cast<int>(row), static_cast<int>(column)); if (!cell) return E_INVALIDARG; @@ -1448,27 +1509,33 @@ STDMETHODIMP AXPlatformNodeWin::get_columnExtentAt(long row, STDMETHODIMP AXPlatformNodeWin::get_columnHeader( IAccessibleTable** accessible_table, - long* starting_row_index) { + LONG* starting_row_index) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_HEADER); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + // TODO(dmazzoni): implement return E_NOTIMPL; } -STDMETHODIMP AXPlatformNodeWin::get_columnIndex(long cell_index, - long* column_index) { +STDMETHODIMP AXPlatformNodeWin::get_columnIndex(LONG cell_index, + LONG* column_index) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_COLUMN_INDEX); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!column_index) return E_INVALIDARG; - auto* cell = GetTableCell(cell_index); + AXPlatformNodeBase* cell = GetTableCell(cell_index); if (!cell) return E_INVALIDARG; *column_index = cell->GetTableColumn(); return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_nColumns(long* column_count) { +STDMETHODIMP AXPlatformNodeWin::get_nColumns(LONG* column_count) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_COLUMNS); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!column_count) return E_INVALIDARG; @@ -1476,8 +1543,10 @@ STDMETHODIMP AXPlatformNodeWin::get_nColumns(long* column_count) { return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_nRows(long* row_count) { +STDMETHODIMP AXPlatformNodeWin::get_nRows(LONG* row_count) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_ROWS); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!row_count) return E_INVALIDARG; @@ -1485,37 +1554,96 @@ STDMETHODIMP AXPlatformNodeWin::get_nRows(long* row_count) { return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_nSelectedChildren(long* cell_count) { +STDMETHODIMP AXPlatformNodeWin::get_nSelectedChildren(LONG* cell_count) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_CHILDREN); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!cell_count) return E_INVALIDARG; - - // TODO(dmazzoni): add support for selected cells/rows/columns in tables. *cell_count = 0; - return S_FALSE; + + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0) + return S_FALSE; + + LONG result = 0; + 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_STATE_SELECTED)) + result++; + } + } + *cell_count = result; + return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_nSelectedColumns(long* column_count) { +STDMETHODIMP AXPlatformNodeWin::get_nSelectedColumns(LONG* column_count) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_COLUMNS); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!column_count) return E_INVALIDARG; - *column_count = 0; - return S_FALSE; + + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0) + return S_FALSE; + + // If every cell in a column is selected, then that column is selected. + LONG result = 0; + for (int c = 0; c < columns; ++c) { + bool selected = true; + for (int r = 0; r < rows && selected == true; ++r) { + AXPlatformNodeBase* cell = GetTableCell(r, c); + if (!cell || !(cell->GetData().HasState(AX_STATE_SELECTED))) + selected = false; + } + if (selected) + result++; + } + + *column_count = result; + return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_nSelectedRows(long* row_count) { +STDMETHODIMP AXPlatformNodeWin::get_nSelectedRows(LONG* row_count) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_N_SELECTED_ROWS); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!row_count) return E_INVALIDARG; - *row_count = 0; - return S_FALSE; + + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0) + return S_FALSE; + + // If every cell in a row is selected, then that row is selected. + LONG result = 0; + for (int r = 0; r < rows; ++r) { + bool selected = true; + for (int c = 0; c < columns && selected == true; ++c) { + AXPlatformNodeBase* cell = GetTableCell(r, c); + if (!cell || !(cell->GetData().HasState(AX_STATE_SELECTED))) + selected = false; + } + if (selected) + result++; + } + + *row_count = result; + return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_rowDescription(long row, +STDMETHODIMP AXPlatformNodeWin::get_rowDescription(LONG row, BSTR* description) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_DESCRIPTION); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!description) return E_INVALIDARG; @@ -1528,15 +1656,15 @@ STDMETHODIMP AXPlatformNodeWin::get_rowDescription(long row, return S_FALSE; } - for (int i = 0; i < columns; ++i) { - auto* cell = GetTableCell(row, i); - if (cell && cell->GetData().role == ui::AX_ROLE_ROW_HEADER) { - base::string16 cell_name = cell->GetString16Attribute(ui::AX_ATTR_NAME); + for (int c = 0; c < columns; ++c) { + AXPlatformNodeBase* cell = GetTableCell(row, c); + if (cell && cell->GetData().role == AX_ROLE_ROW_HEADER) { + base::string16 cell_name = cell->GetString16Attribute(AX_ATTR_NAME); if (cell_name.size() > 0) { *description = SysAllocString(cell_name.c_str()); return S_OK; } - cell_name = cell->GetString16Attribute(ui::AX_ATTR_DESCRIPTION); + cell_name = cell->GetString16Attribute(AX_ATTR_DESCRIPTION); if (cell_name.size() > 0) { *description = SysAllocString(cell_name.c_str()); return S_OK; @@ -1548,14 +1676,16 @@ STDMETHODIMP AXPlatformNodeWin::get_rowDescription(long row, return S_FALSE; } -STDMETHODIMP AXPlatformNodeWin::get_rowExtentAt(long row, - long column, - long* n_rows_spanned) { +STDMETHODIMP AXPlatformNodeWin::get_rowExtentAt(LONG row, + LONG column, + LONG* n_rows_spanned) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_EXTENT_AT); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!n_rows_spanned) return E_INVALIDARG; - auto* cell = GetTableCell(row, column); + AXPlatformNodeBase* cell = GetTableCell(row, column); if (!cell) return E_INVALIDARG; @@ -1565,17 +1695,22 @@ STDMETHODIMP AXPlatformNodeWin::get_rowExtentAt(long row, STDMETHODIMP AXPlatformNodeWin::get_rowHeader( IAccessibleTable** accessible_table, - long* starting_column_index) { + LONG* starting_column_index) { WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_ROW_HEADER); + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + // TODO(dmazzoni): implement return E_NOTIMPL; } -STDMETHODIMP AXPlatformNodeWin::get_rowIndex(long cell_index, long* row_index) { +STDMETHODIMP AXPlatformNodeWin::get_rowIndex(LONG cell_index, LONG* row_index) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!row_index) return E_INVALIDARG; - auto* cell = GetTableCell(cell_index); + AXPlatformNodeBase* cell = GetTableCell(cell_index); if (!cell) return E_INVALIDARG; @@ -1583,40 +1718,95 @@ STDMETHODIMP AXPlatformNodeWin::get_rowIndex(long cell_index, long* row_index) { return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_selectedChildren(long max_children, - long** children, - long* n_children) { - if (!children || !n_children) +STDMETHODIMP AXPlatformNodeWin::get_selectedChildren(LONG max_children, + LONG** children, + LONG* n_children) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + + if (!children || !n_children || max_children <= 0) return E_INVALIDARG; - // TODO(dmazzoni): Implement this. - *n_children = 0; - return S_FALSE; + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0) + return S_FALSE; + + std::vector<LONG> results; + 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_STATE_SELECTED)) + // index is row index * column count + column index. + results.push_back(r * columns + c); + } + } + + return AllocateComArrayFromVector(results, max_children, children, + n_children); } -STDMETHODIMP AXPlatformNodeWin::get_selectedColumns(long max_columns, - long** columns, - long* n_columns) { - if (!columns || !n_columns) +STDMETHODIMP AXPlatformNodeWin::get_selectedColumns(LONG max_columns, + LONG** columns, + LONG* n_columns) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + + if (!columns || !n_columns || max_columns <= 0) return E_INVALIDARG; - // TODO(dmazzoni): Implement this. - *n_columns = 0; - return S_FALSE; + int column_count = GetTableColumnCount(); + int row_count = GetTableRowCount(); + if (column_count <= 0 || row_count <= 0) + return S_FALSE; + + std::vector<LONG> results; + for (int c = 0; c < column_count; ++c) { + bool selected = true; + for (int r = 0; r < row_count && selected == true; ++r) { + AXPlatformNodeBase* cell = GetTableCell(r, c); + if (!cell || !(cell->GetData().HasState(AX_STATE_SELECTED))) + selected = false; + } + if (selected) + results.push_back(c); + } + + return AllocateComArrayFromVector(results, max_columns, columns, n_columns); } -STDMETHODIMP AXPlatformNodeWin::get_selectedRows(long max_rows, - long** rows, - long* n_rows) { - if (!rows || !n_rows) +STDMETHODIMP AXPlatformNodeWin::get_selectedRows(LONG max_rows, + LONG** rows, + LONG* n_rows) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!rows || !n_rows || max_rows <= 0) return E_INVALIDARG; - // TODO(dmazzoni): Implement this. - *n_rows = 0; - return S_FALSE; + int column_count = GetTableColumnCount(); + int row_count = GetTableRowCount(); + if (column_count <= 0 || row_count <= 0) + return S_FALSE; + + std::vector<LONG> results; + for (int r = 0; r < row_count; ++r) { + bool selected = true; + for (int c = 0; c < column_count && selected == true; ++c) { + AXPlatformNodeBase* cell = GetTableCell(r, c); + if (!cell || !(cell->GetData().HasState(AX_STATE_SELECTED))) + selected = false; + } + if (selected) + results.push_back(r); + } + + return AllocateComArrayFromVector(results, max_rows, rows, n_rows); } STDMETHODIMP AXPlatformNodeWin::get_summary(IUnknown** accessible) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!accessible) return E_INVALIDARG; @@ -1625,48 +1815,88 @@ STDMETHODIMP AXPlatformNodeWin::get_summary(IUnknown** accessible) { return S_FALSE; } -STDMETHODIMP AXPlatformNodeWin::get_isColumnSelected(long column, +STDMETHODIMP AXPlatformNodeWin::get_isColumnSelected(LONG column, boolean* is_selected) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); if (!is_selected) return E_INVALIDARG; - - // TODO(dmazzoni): Implement this. *is_selected = false; + + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0 || column >= columns || column < 0) + return S_FALSE; + + for (int r = 0; r < rows; ++r) { + AXPlatformNodeBase* cell = GetTableCell(r, column); + if (!cell || !(cell->GetData().HasState(AX_STATE_SELECTED))) + return S_OK; + } + + *is_selected = true; return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_isRowSelected(long row, +STDMETHODIMP AXPlatformNodeWin::get_isRowSelected(LONG row, boolean* is_selected) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); if (!is_selected) return E_INVALIDARG; - - // TODO(dmazzoni): Implement this. *is_selected = false; + + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0 || row >= rows || row < 0) + return S_FALSE; + + for (int c = 0; c < columns; ++c) { + AXPlatformNodeBase* cell = GetTableCell(row, c); + if (!cell || !(cell->GetData().HasState(AX_STATE_SELECTED))) + return S_OK; + } + + *is_selected = true; return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_isSelected(long row, - long column, +STDMETHODIMP AXPlatformNodeWin::get_isSelected(LONG row, + LONG column, boolean* is_selected) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); if (!is_selected) return E_INVALIDARG; - - // TODO(dmazzoni): Implement this. *is_selected = false; + + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0 || row >= rows || row < 0 || + column >= columns || column < 0) + return S_FALSE; + + AXPlatformNodeBase* cell = GetTableCell(row, column); + if (cell && cell->GetData().HasState(AX_STATE_SELECTED)) + *is_selected = true; + return S_OK; } STDMETHODIMP AXPlatformNodeWin::get_rowColumnExtentsAtIndex( - long index, - long* row, - long* column, - long* row_extents, - long* column_extents, + LONG index, + LONG* row, + LONG* column, + LONG* row_extents, + LONG* column_extents, boolean* is_selected) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!row || !column || !row_extents || !column_extents || !is_selected) return E_INVALIDARG; - auto* cell = GetTableCell(index); + AXPlatformNodeBase* cell = GetTableCell(index); if (!cell) return E_INVALIDARG; @@ -1679,24 +1909,37 @@ STDMETHODIMP AXPlatformNodeWin::get_rowColumnExtentsAtIndex( return S_OK; } -STDMETHODIMP AXPlatformNodeWin::selectRow(long row) { +STDMETHODIMP AXPlatformNodeWin::selectRow(LONG row) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + return E_NOTIMPL; } -STDMETHODIMP AXPlatformNodeWin::selectColumn(long column) { +STDMETHODIMP AXPlatformNodeWin::selectColumn(LONG column) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + return E_NOTIMPL; } -STDMETHODIMP AXPlatformNodeWin::unselectRow(long row) { +STDMETHODIMP AXPlatformNodeWin::unselectRow(LONG row) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + return E_NOTIMPL; } -STDMETHODIMP AXPlatformNodeWin::unselectColumn(long column) { +STDMETHODIMP AXPlatformNodeWin::unselectColumn(LONG column) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + return E_NOTIMPL; } STDMETHODIMP AXPlatformNodeWin::get_modelChange(IA2TableModelChange* model_change) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? return E_NOTIMPL; } @@ -1704,9 +1947,11 @@ AXPlatformNodeWin::get_modelChange(IA2TableModelChange* model_change) { // IAccessibleTable2 methods. // -STDMETHODIMP AXPlatformNodeWin::get_cellAt(long row, - long column, +STDMETHODIMP AXPlatformNodeWin::get_cellAt(LONG row, + LONG column, IUnknown** cell) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(AXMode::kScreenReader); if (!cell) return E_INVALIDARG; @@ -1723,44 +1968,65 @@ STDMETHODIMP AXPlatformNodeWin::get_cellAt(long row, return E_INVALIDARG; } -STDMETHODIMP AXPlatformNodeWin::get_nSelectedCells(long* cell_count) { +STDMETHODIMP AXPlatformNodeWin::get_nSelectedCells(LONG* cell_count) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + // Note that this method does not need to set any ax mode since it + // calls into get_nSelectedChildren() which does. return get_nSelectedChildren(cell_count); } STDMETHODIMP AXPlatformNodeWin::get_selectedCells(IUnknown*** cells, - long* n_selected_cells) { + LONG* n_selected_cells) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); if (!cells || !n_selected_cells) return E_INVALIDARG; - // TODO(dmazzoni): Implement this. + *cells = nullptr; *n_selected_cells = 0; - return S_OK; -} -STDMETHODIMP AXPlatformNodeWin::get_selectedColumns(long** columns, - long* n_columns) { - if (!columns || !n_columns) - return E_INVALIDARG; + int columns = GetTableColumnCount(); + int rows = GetTableRowCount(); + if (columns <= 0 || rows <= 0) + return S_FALSE; - // TODO(dmazzoni): Implement this. - *n_columns = 0; + std::vector<AXPlatformNodeBase*> selected; + 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_STATE_SELECTED)) + selected.push_back(cell); + } + } + + *n_selected_cells = static_cast<LONG>(selected.size()); + *cells = static_cast<IUnknown**>( + CoTaskMemAlloc((*n_selected_cells) * sizeof(cells[0]))); + + for (size_t i = 0; i < selected.size(); ++i) { + auto* node_win = static_cast<AXPlatformNodeWin*>(selected[i]); + node_win->AddRef(); + (*cells)[i] = static_cast<IAccessible*>(node_win); + } return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_selectedRows(long** rows, long* n_rows) { - if (!rows || !n_rows) - return E_INVALIDARG; +STDMETHODIMP AXPlatformNodeWin::get_selectedColumns(LONG** columns, + LONG* n_columns) { + return get_selectedColumns(INT_MAX, columns, n_columns); +} - // TODO(dmazzoni): Implement this. - *n_rows = 0; - return S_OK; +STDMETHODIMP AXPlatformNodeWin::get_selectedRows(LONG** rows, LONG* n_rows) { + return get_selectedRows(INT_MAX, rows, n_rows); } // // IAccessibleTableCell methods. // -STDMETHODIMP AXPlatformNodeWin::get_columnExtent(long* n_columns_spanned) { +STDMETHODIMP AXPlatformNodeWin::get_columnExtent(LONG* n_columns_spanned) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); if (!n_columns_spanned) return E_INVALIDARG; @@ -1770,12 +2036,14 @@ STDMETHODIMP AXPlatformNodeWin::get_columnExtent(long* n_columns_spanned) { STDMETHODIMP AXPlatformNodeWin::get_columnHeaderCells( IUnknown*** cell_accessibles, - long* n_column_header_cells) { + LONG* n_column_header_cells) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); if (!cell_accessibles || !n_column_header_cells) return E_INVALIDARG; *n_column_header_cells = 0; - auto* table = GetTable(); + AXPlatformNodeBase* table = GetTable(); if (!table) { return S_FALSE; } @@ -1786,18 +2054,18 @@ STDMETHODIMP AXPlatformNodeWin::get_columnHeaderCells( if (columns <= 0 || rows <= 0 || column < 0 || column >= columns) return S_FALSE; - for (int i = 0; i < rows; ++i) { - auto* cell = GetTableCell(i, column); - if (cell && cell->GetData().role == ui::AX_ROLE_COLUMN_HEADER) + for (int r = 0; r < rows; ++r) { + AXPlatformNodeBase* cell = GetTableCell(r, column); + if (cell && cell->GetData().role == AX_ROLE_COLUMN_HEADER) (*n_column_header_cells)++; } *cell_accessibles = static_cast<IUnknown**>( CoTaskMemAlloc((*n_column_header_cells) * sizeof(cell_accessibles[0]))); int index = 0; - for (int i = 0; i < rows; ++i) { - AXPlatformNodeBase* cell = GetTableCell(i, column); - if (cell && cell->GetData().role == ui::AX_ROLE_COLUMN_HEADER) { + for (int r = 0; r < rows; ++r) { + AXPlatformNodeBase* cell = GetTableCell(r, column); + if (cell && cell->GetData().role == AX_ROLE_COLUMN_HEADER) { auto* node_win = static_cast<AXPlatformNodeWin*>(cell); node_win->AddRef(); @@ -1809,7 +2077,10 @@ STDMETHODIMP AXPlatformNodeWin::get_columnHeaderCells( return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_columnIndex(long* column_index) { +STDMETHODIMP AXPlatformNodeWin::get_columnIndex(LONG* column_index) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!column_index) return E_INVALIDARG; @@ -1817,7 +2088,10 @@ STDMETHODIMP AXPlatformNodeWin::get_columnIndex(long* column_index) { return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_rowExtent(long* n_rows_spanned) { +STDMETHODIMP AXPlatformNodeWin::get_rowExtent(LONG* n_rows_spanned) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!n_rows_spanned) return E_INVALIDARG; @@ -1826,12 +2100,15 @@ STDMETHODIMP AXPlatformNodeWin::get_rowExtent(long* n_rows_spanned) { } STDMETHODIMP AXPlatformNodeWin::get_rowHeaderCells(IUnknown*** cell_accessibles, - long* n_row_header_cells) { + LONG* n_row_header_cells) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!cell_accessibles || !n_row_header_cells) return E_INVALIDARG; *n_row_header_cells = 0; - auto* table = GetTable(); + AXPlatformNodeBase* table = GetTable(); if (!table) { return S_FALSE; } @@ -1842,18 +2119,18 @@ STDMETHODIMP AXPlatformNodeWin::get_rowHeaderCells(IUnknown*** cell_accessibles, if (columns <= 0 || rows <= 0 || row < 0 || row >= rows) return S_FALSE; - for (int i = 0; i < columns; ++i) { - auto* cell = GetTableCell(row, i); - if (cell && cell->GetData().role == ui::AX_ROLE_ROW_HEADER) + for (int c = 0; c < columns; ++c) { + AXPlatformNodeBase* cell = GetTableCell(row, c); + if (cell && cell->GetData().role == AX_ROLE_ROW_HEADER) (*n_row_header_cells)++; } *cell_accessibles = static_cast<IUnknown**>( CoTaskMemAlloc((*n_row_header_cells) * sizeof(cell_accessibles[0]))); int index = 0; - for (int i = 0; i < columns; ++i) { - AXPlatformNodeBase* cell = GetTableCell(row, i); - if (cell && cell->GetData().role == ui::AX_ROLE_ROW_HEADER) { + for (int c = 0; c < columns; ++c) { + AXPlatformNodeBase* cell = GetTableCell(row, c); + if (cell && cell->GetData().role == AX_ROLE_ROW_HEADER) { auto* node_win = static_cast<AXPlatformNodeWin*>(cell); node_win->AddRef(); @@ -1865,7 +2142,10 @@ STDMETHODIMP AXPlatformNodeWin::get_rowHeaderCells(IUnknown*** cell_accessibles, return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_rowIndex(long* row_index) { +STDMETHODIMP AXPlatformNodeWin::get_rowIndex(LONG* row_index) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!row_index) return E_INVALIDARG; @@ -1874,6 +2154,9 @@ STDMETHODIMP AXPlatformNodeWin::get_rowIndex(long* row_index) { } STDMETHODIMP AXPlatformNodeWin::get_isSelected(boolean* is_selected) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!is_selected) return E_INVALIDARG; @@ -1881,11 +2164,14 @@ STDMETHODIMP AXPlatformNodeWin::get_isSelected(boolean* is_selected) { return S_OK; } -STDMETHODIMP AXPlatformNodeWin::get_rowColumnExtents(long* row_index, - long* column_index, - long* row_extents, - long* column_extents, +STDMETHODIMP AXPlatformNodeWin::get_rowColumnExtents(LONG* row_index, + LONG* column_index, + LONG* row_extents, + LONG* column_extents, boolean* is_selected) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!row_index || !column_index || !row_extents || !column_extents || !is_selected) { return E_INVALIDARG; @@ -1901,10 +2187,13 @@ STDMETHODIMP AXPlatformNodeWin::get_rowColumnExtents(long* row_index, } STDMETHODIMP AXPlatformNodeWin::get_table(IUnknown** table) { + // TODO(dougt) WIN_ACCESSIBILITY_API_HISTOGRAM? + AXPlatformNode::NotifyAddAXModeFlags(kScreenReaderAndHTMLAccessibilityModes); + if (!table) return E_INVALIDARG; - auto* find_table = GetTable(); + AXPlatformNodeBase* find_table = GetTable(); if (!find_table) { *table = nullptr; return S_FALSE; @@ -1933,14 +2222,14 @@ STDMETHODIMP AXPlatformNodeWin::get_nCharacters(LONG* n_characters) { STDMETHODIMP AXPlatformNodeWin::get_caretOffset(LONG* offset) { COM_OBJECT_VALIDATE_1_ARG(offset); - *offset = static_cast<LONG>(GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END)); + *offset = static_cast<LONG>(GetIntAttribute(AX_ATTR_TEXT_SEL_END)); return S_OK; } STDMETHODIMP AXPlatformNodeWin::get_nSelections(LONG* n_selections) { COM_OBJECT_VALIDATE_1_ARG(n_selections); - int sel_start = GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START); - int sel_end = GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END); + int sel_start = GetIntAttribute(AX_ATTR_TEXT_SEL_START); + int sel_end = GetIntAttribute(AX_ATTR_TEXT_SEL_END); if (sel_start != sel_end) *n_selections = 1; else @@ -1955,10 +2244,8 @@ STDMETHODIMP AXPlatformNodeWin::get_selection(LONG selection_index, if (selection_index != 0) return E_INVALIDARG; - *start_offset = static_cast<LONG>( - GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START)); - *end_offset = static_cast<LONG>( - GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END)); + *start_offset = static_cast<LONG>(GetIntAttribute(AX_ATTR_TEXT_SEL_START)); + *end_offset = static_cast<LONG>(GetIntAttribute(AX_ATTR_TEXT_SEL_END)); return S_OK; } @@ -1966,7 +2253,7 @@ STDMETHODIMP AXPlatformNodeWin::get_text(LONG start_offset, LONG end_offset, BSTR* text) { COM_OBJECT_VALIDATE_1_ARG(text); - int sel_end = GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START); + int sel_end = GetIntAttribute(AX_ATTR_TEXT_SEL_START); base::string16 text_str = TextForIAccessibleText(); LONG len = static_cast<LONG>(text_str.size()); @@ -2022,10 +2309,10 @@ STDMETHODIMP AXPlatformNodeWin::get_textAtOffset( const base::string16& text_str = TextForIAccessibleText(); - *start_offset = FindBoundary( - text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); - *end_offset = FindBoundary( - text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); + *start_offset = + FindBoundary(text_str, boundary_type, offset, BACKWARDS_DIRECTION); + *end_offset = + FindBoundary(text_str, boundary_type, offset, FORWARDS_DIRECTION); return get_text(*start_offset, *end_offset, text); } @@ -2048,8 +2335,8 @@ STDMETHODIMP AXPlatformNodeWin::get_textBeforeOffset( const base::string16& text_str = TextForIAccessibleText(); - *start_offset = FindBoundary( - text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); + *start_offset = + FindBoundary(text_str, boundary_type, offset, BACKWARDS_DIRECTION); *end_offset = offset; return get_text(*start_offset, *end_offset, text); } @@ -2074,8 +2361,8 @@ STDMETHODIMP AXPlatformNodeWin::get_textAfterOffset( const base::string16& text_str = TextForIAccessibleText(); *start_offset = offset; - *end_offset = FindBoundary( - text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); + *end_offset = + FindBoundary(text_str, boundary_type, offset, FORWARDS_DIRECTION); return get_text(*start_offset, *end_offset, text); } @@ -2099,7 +2386,7 @@ STDMETHODIMP AXPlatformNodeWin::removeSelection(LONG selection_index) { return E_INVALIDARG; // Simply collapse the selection to the position of the caret if a caret is // visible, otherwise set the selection to 0. - return setCaretOffset(GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END)); + return setCaretOffset(GetIntAttribute(AX_ATTR_TEXT_SEL_END)); } STDMETHODIMP AXPlatformNodeWin::setCaretOffset(LONG offset) { @@ -2205,326 +2492,314 @@ int AXPlatformNodeWin::MSAARole() { // If this is a web area for a presentational iframe, give it a role of // something other than DOCUMENT so that the fact that it's a separate doc // is not exposed to AT. - if (IsWebAreaForPresentationalIframe()) { + if (IsWebAreaForPresentationalIframe()) return ROLE_SYSTEM_GROUPING; - } switch (GetData().role) { - case ui::AX_ROLE_ALERT: + case AX_ROLE_ALERT: return ROLE_SYSTEM_ALERT; - case ui::AX_ROLE_ALERT_DIALOG: + case AX_ROLE_ALERT_DIALOG: return ROLE_SYSTEM_DIALOG; - case ui::AX_ROLE_ANCHOR: + case AX_ROLE_ANCHOR: return ROLE_SYSTEM_LINK; - case ui::AX_ROLE_APPLICATION: + case AX_ROLE_APPLICATION: return ROLE_SYSTEM_APPLICATION; - case ui::AX_ROLE_ARTICLE: + case AX_ROLE_ARTICLE: return ROLE_SYSTEM_DOCUMENT; - case ui::AX_ROLE_AUDIO: + case AX_ROLE_AUDIO: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_BANNER: + case AX_ROLE_BANNER: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_BUSY_INDICATOR: - return ROLE_SYSTEM_ANIMATION; - - case ui::AX_ROLE_BUTTON: + case AX_ROLE_BUTTON: return ROLE_SYSTEM_PUSHBUTTON; - case ui::AX_ROLE_CANVAS: + case AX_ROLE_CANVAS: return ROLE_SYSTEM_GRAPHIC; - case ui::AX_ROLE_CAPTION: + case AX_ROLE_CAPTION: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_CELL: + case AX_ROLE_CELL: return ROLE_SYSTEM_CELL; - case ui::AX_ROLE_CHECK_BOX: + case AX_ROLE_CHECK_BOX: return ROLE_SYSTEM_CHECKBUTTON; - case ui::AX_ROLE_COLOR_WELL: + case AX_ROLE_COLOR_WELL: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_COLUMN: + case AX_ROLE_COLUMN: return ROLE_SYSTEM_COLUMN; - case ui::AX_ROLE_COLUMN_HEADER: + case AX_ROLE_COLUMN_HEADER: return ROLE_SYSTEM_COLUMNHEADER; - case ui::AX_ROLE_COMBO_BOX: + case AX_ROLE_COMBO_BOX: return ROLE_SYSTEM_COMBOBOX; - case ui::AX_ROLE_COMPLEMENTARY: + case AX_ROLE_COMPLEMENTARY: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_CONTENT_INFO: + case AX_ROLE_CONTENT_INFO: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_DATE: - case ui::AX_ROLE_DATE_TIME: + case AX_ROLE_DATE: + case AX_ROLE_DATE_TIME: return ROLE_SYSTEM_DROPLIST; - case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL: + case AX_ROLE_DESCRIPTION_LIST_DETAIL: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_DESCRIPTION_LIST: + case AX_ROLE_DESCRIPTION_LIST: return ROLE_SYSTEM_LIST; - case ui::AX_ROLE_DESCRIPTION_LIST_TERM: + case AX_ROLE_DESCRIPTION_LIST_TERM: return ROLE_SYSTEM_LISTITEM; - case ui::AX_ROLE_DETAILS: + case AX_ROLE_DETAILS: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_DIALOG: + case AX_ROLE_DIALOG: return ROLE_SYSTEM_DIALOG; - case ui::AX_ROLE_DISCLOSURE_TRIANGLE: + case AX_ROLE_DISCLOSURE_TRIANGLE: return ROLE_SYSTEM_PUSHBUTTON; - case ui::AX_ROLE_DOCUMENT: - case ui::AX_ROLE_ROOT_WEB_AREA: - case ui::AX_ROLE_WEB_AREA: + case AX_ROLE_DOCUMENT: + case AX_ROLE_ROOT_WEB_AREA: + case AX_ROLE_WEB_AREA: return ROLE_SYSTEM_DOCUMENT; - case ui::AX_ROLE_EMBEDDED_OBJECT: + case AX_ROLE_EMBEDDED_OBJECT: if (delegate_->GetChildCount()) { return ROLE_SYSTEM_GROUPING; } else { return ROLE_SYSTEM_CLIENT; } - case ui::AX_ROLE_FIGURE: + case AX_ROLE_FIGURE: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_FEED: + case AX_ROLE_FEED: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_GENERIC_CONTAINER: + case AX_ROLE_GENERIC_CONTAINER: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_GRID: + case AX_ROLE_GRID: return ROLE_SYSTEM_TABLE; - case ui::AX_ROLE_GROUP: + case AX_ROLE_GROUP: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_HEADING: + case AX_ROLE_HEADING: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_IFRAME: + case AX_ROLE_IFRAME: return ROLE_SYSTEM_DOCUMENT; - case ui::AX_ROLE_IFRAME_PRESENTATIONAL: + case AX_ROLE_IFRAME_PRESENTATIONAL: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_IMAGE: + case AX_ROLE_IMAGE: return ROLE_SYSTEM_GRAPHIC; - case ui::AX_ROLE_IMAGE_MAP_LINK: - return ROLE_SYSTEM_LINK; - - case ui::AX_ROLE_INPUT_TIME: + case AX_ROLE_INPUT_TIME: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_LABEL_TEXT: - case ui::AX_ROLE_LEGEND: + case AX_ROLE_LABEL_TEXT: + case AX_ROLE_LEGEND: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_LINK: + case AX_ROLE_LINK: return ROLE_SYSTEM_LINK; - case ui::AX_ROLE_LIST: + case AX_ROLE_LIST: return ROLE_SYSTEM_LIST; - case ui::AX_ROLE_LIST_BOX: + case AX_ROLE_LIST_BOX: return ROLE_SYSTEM_LIST; - case ui::AX_ROLE_LIST_BOX_OPTION: + case AX_ROLE_LIST_BOX_OPTION: return ROLE_SYSTEM_LISTITEM; - case ui::AX_ROLE_LIST_ITEM: + case AX_ROLE_LIST_ITEM: return ROLE_SYSTEM_LISTITEM; - case ui::AX_ROLE_MAIN: + case AX_ROLE_MAIN: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_MARK: + case AX_ROLE_MARK: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_MARQUEE: + case AX_ROLE_MARQUEE: return ROLE_SYSTEM_ANIMATION; - case ui::AX_ROLE_MATH: + case AX_ROLE_MATH: return ROLE_SYSTEM_EQUATION; - case ui::AX_ROLE_MENU: - case ui::AX_ROLE_MENU_BUTTON: + case AX_ROLE_MENU: + case AX_ROLE_MENU_BUTTON: return ROLE_SYSTEM_MENUPOPUP; - case ui::AX_ROLE_MENU_BAR: + case AX_ROLE_MENU_BAR: return ROLE_SYSTEM_MENUBAR; - case ui::AX_ROLE_MENU_ITEM: + case AX_ROLE_MENU_ITEM: return ROLE_SYSTEM_MENUITEM; - case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: + case AX_ROLE_MENU_ITEM_CHECK_BOX: return ROLE_SYSTEM_MENUITEM; - case ui::AX_ROLE_MENU_ITEM_RADIO: + case AX_ROLE_MENU_ITEM_RADIO: return ROLE_SYSTEM_MENUITEM; case ui::AX_ROLE_MENU_LIST_POPUP: + if (IsAncestorComboBox()) + return ROLE_SYSTEM_LIST; return ROLE_SYSTEM_MENUPOPUP; case ui::AX_ROLE_MENU_LIST_OPTION: + if (IsAncestorComboBox()) + return ROLE_SYSTEM_LISTITEM; return ROLE_SYSTEM_MENUITEM; - case ui::AX_ROLE_METER: + case AX_ROLE_METER: return ROLE_SYSTEM_PROGRESSBAR; - case ui::AX_ROLE_NAVIGATION: + case AX_ROLE_NAVIGATION: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_NOTE: + case AX_ROLE_NOTE: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_OUTLINE: - return ROLE_SYSTEM_OUTLINE; - - case ui::AX_ROLE_POP_UP_BUTTON: { - std::string html_tag = GetData().GetStringAttribute(ui::AX_ATTR_HTML_TAG); + case AX_ROLE_POP_UP_BUTTON: { + std::string html_tag = GetData().GetStringAttribute(AX_ATTR_HTML_TAG); if (html_tag == "select") return ROLE_SYSTEM_COMBOBOX; return ROLE_SYSTEM_BUTTONMENU; } - case ui::AX_ROLE_PRE: + case AX_ROLE_PRE: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_PROGRESS_INDICATOR: + case AX_ROLE_PROGRESS_INDICATOR: return ROLE_SYSTEM_PROGRESSBAR; - case ui::AX_ROLE_RADIO_BUTTON: + case AX_ROLE_RADIO_BUTTON: return ROLE_SYSTEM_RADIOBUTTON; - case ui::AX_ROLE_RADIO_GROUP: + case AX_ROLE_RADIO_GROUP: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_REGION: { - std::string html_tag = GetData().GetStringAttribute(ui::AX_ATTR_HTML_TAG); + case AX_ROLE_REGION: { + std::string html_tag = GetData().GetStringAttribute(AX_ATTR_HTML_TAG); if (html_tag == "section") return ROLE_SYSTEM_GROUPING; return ROLE_SYSTEM_PANE; } - case ui::AX_ROLE_ROW: { + case AX_ROLE_ROW: { // Role changes depending on whether row is inside a treegrid // https://www.w3.org/TR/core-aam-1.1/#role-map-row return IsInTreeGrid() ? ROLE_SYSTEM_OUTLINEITEM : ROLE_SYSTEM_ROW; } - case ui::AX_ROLE_ROW_HEADER: + case AX_ROLE_ROW_HEADER: return ROLE_SYSTEM_ROWHEADER; - case ui::AX_ROLE_RUBY: + case AX_ROLE_RUBY: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_RULER: - return ROLE_SYSTEM_CLIENT; - - case ui::AX_ROLE_SCROLL_AREA: - return ROLE_SYSTEM_CLIENT; - - case ui::AX_ROLE_SCROLL_BAR: + case AX_ROLE_SCROLL_BAR: return ROLE_SYSTEM_SCROLLBAR; - case ui::AX_ROLE_SEARCH: + case AX_ROLE_SEARCH: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_SLIDER: + case AX_ROLE_SLIDER: return ROLE_SYSTEM_SLIDER; - case ui::AX_ROLE_SPIN_BUTTON: + case AX_ROLE_SPIN_BUTTON: return ROLE_SYSTEM_SPINBUTTON; - case ui::AX_ROLE_SPIN_BUTTON_PART: + case AX_ROLE_SPIN_BUTTON_PART: return ROLE_SYSTEM_PUSHBUTTON; - case ui::AX_ROLE_ANNOTATION: - case ui::AX_ROLE_LIST_MARKER: - case ui::AX_ROLE_STATIC_TEXT: + case AX_ROLE_ANNOTATION: + case AX_ROLE_LIST_MARKER: + case AX_ROLE_STATIC_TEXT: return ROLE_SYSTEM_STATICTEXT; - case ui::AX_ROLE_STATUS: + case AX_ROLE_STATUS: return ROLE_SYSTEM_STATUSBAR; - case ui::AX_ROLE_SPLITTER: + case AX_ROLE_SPLITTER: return ROLE_SYSTEM_SEPARATOR; - case ui::AX_ROLE_SVG_ROOT: + case AX_ROLE_SVG_ROOT: return ROLE_SYSTEM_GRAPHIC; - case ui::AX_ROLE_TAB: + case AX_ROLE_TAB: return ROLE_SYSTEM_PAGETAB; - case ui::AX_ROLE_TABLE: + case AX_ROLE_TABLE: return ROLE_SYSTEM_TABLE; - case ui::AX_ROLE_TABLE_HEADER_CONTAINER: + case AX_ROLE_TABLE_HEADER_CONTAINER: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_TAB_LIST: + case AX_ROLE_TAB_LIST: return ROLE_SYSTEM_PAGETABLIST; - case ui::AX_ROLE_TAB_PANEL: + case AX_ROLE_TAB_PANEL: return ROLE_SYSTEM_PROPERTYPAGE; - case ui::AX_ROLE_TERM: + case AX_ROLE_TERM: return ROLE_SYSTEM_LISTITEM; - case ui::AX_ROLE_TOGGLE_BUTTON: + case AX_ROLE_TOGGLE_BUTTON: return ROLE_SYSTEM_PUSHBUTTON; - case ui::AX_ROLE_TEXT_FIELD: - case ui::AX_ROLE_SEARCH_BOX: + case AX_ROLE_TEXT_FIELD: + case AX_ROLE_SEARCH_BOX: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_ABBR: - case ui::AX_ROLE_TIME: + case AX_ROLE_ABBR: + case AX_ROLE_TIME: return ROLE_SYSTEM_TEXT; - case ui::AX_ROLE_TIMER: + case AX_ROLE_TIMER: return ROLE_SYSTEM_CLOCK; - case ui::AX_ROLE_TOOLBAR: + case AX_ROLE_TOOLBAR: return ROLE_SYSTEM_TOOLBAR; - case ui::AX_ROLE_TOOLTIP: + case AX_ROLE_TOOLTIP: return ROLE_SYSTEM_TOOLTIP; - case ui::AX_ROLE_TREE: + case AX_ROLE_TREE: return ROLE_SYSTEM_OUTLINE; - case ui::AX_ROLE_TREE_GRID: + case AX_ROLE_TREE_GRID: return ROLE_SYSTEM_OUTLINE; - case ui::AX_ROLE_TREE_ITEM: + case AX_ROLE_TREE_ITEM: return ROLE_SYSTEM_OUTLINEITEM; - case ui::AX_ROLE_LINE_BREAK: + case AX_ROLE_LINE_BREAK: return ROLE_SYSTEM_WHITESPACE; - case ui::AX_ROLE_VIDEO: + case AX_ROLE_VIDEO: return ROLE_SYSTEM_GROUPING; - case ui::AX_ROLE_WINDOW: + case AX_ROLE_WINDOW: return ROLE_SYSTEM_WINDOW; // TODO(dmazzoni): figure out the proper MSAA role for roles listed below. @@ -2547,10 +2822,8 @@ int AXPlatformNodeWin::MSAARole() { case AX_ROLE_PANE: case AX_ROLE_PARAGRAPH: case AX_ROLE_PRESENTATIONAL: - case AX_ROLE_SEAMLESS_WEB_AREA: case AX_ROLE_SLIDER_THUMB: case AX_ROLE_SWITCH: - case AX_ROLE_TAB_GROUP: case AX_ROLE_TITLE_BAR: case AX_ROLE_UNKNOWN: case AX_ROLE_WEB_VIEW: @@ -2562,40 +2835,40 @@ int AXPlatformNodeWin::MSAARole() { } std::string AXPlatformNodeWin::StringOverrideForMSAARole() { - std::string html_tag = GetData().GetStringAttribute(ui::AX_ATTR_HTML_TAG); + std::string html_tag = GetData().GetStringAttribute(AX_ATTR_HTML_TAG); switch (GetData().role) { - case ui::AX_ROLE_BLOCKQUOTE: - case ui::AX_ROLE_DEFINITION: - case ui::AX_ROLE_IMAGE_MAP: + case AX_ROLE_BLOCKQUOTE: + case AX_ROLE_DEFINITION: + case AX_ROLE_IMAGE_MAP: return html_tag; - case ui::AX_ROLE_CANVAS: - if (GetData().GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) { + case AX_ROLE_CANVAS: + if (GetData().GetBoolAttribute(AX_ATTR_CANVAS_HAS_FALLBACK)) { return html_tag; } break; - case ui::AX_ROLE_FORM: + case AX_ROLE_FORM: // This could be a div with the role of form // so we return just the string "form". return "form"; - case ui::AX_ROLE_HEADING: + case AX_ROLE_HEADING: if (!html_tag.empty()) return html_tag; break; - case ui::AX_ROLE_PARAGRAPH: + case AX_ROLE_PARAGRAPH: return html_tag; - case ui::AX_ROLE_GENERIC_CONTAINER: + case AX_ROLE_GENERIC_CONTAINER: // TODO(dougt) why can't we always use div in this case? if (html_tag.empty()) return "div"; return html_tag; - case ui::AX_ROLE_SWITCH: + case AX_ROLE_SWITCH: return "switch"; default: @@ -2606,16 +2879,16 @@ std::string AXPlatformNodeWin::StringOverrideForMSAARole() { } bool AXPlatformNodeWin::IsWebAreaForPresentationalIframe() { - if (GetData().role != ui::AX_ROLE_WEB_AREA && - GetData().role != ui::AX_ROLE_ROOT_WEB_AREA) { + if (GetData().role != AX_ROLE_WEB_AREA && + GetData().role != AX_ROLE_ROOT_WEB_AREA) { return false; } - auto* parent = FromNativeViewAccessible(GetParent()); + AXPlatformNodeBase* parent = FromNativeViewAccessible(GetParent()); if (!parent) return false; - return parent->GetData().role == ui::AX_ROLE_IFRAME_PRESENTATIONAL; + return parent->GetData().role == AX_ROLE_IFRAME_PRESENTATIONAL; } int32_t AXPlatformNodeWin::ComputeIA2State() { @@ -2623,57 +2896,54 @@ int32_t AXPlatformNodeWin::ComputeIA2State() { int32_t ia2_state = IA2_STATE_OPAQUE; - const auto checked_state = static_cast<ui::AXCheckedState>( - GetIntAttribute(ui::AX_ATTR_CHECKED_STATE)); + const auto checked_state = + static_cast<AXCheckedState>(GetIntAttribute(AX_ATTR_CHECKED_STATE)); if (checked_state) { ia2_state |= IA2_STATE_CHECKABLE; } - if (HasIntAttribute(ui::AX_ATTR_INVALID_STATE) && - GetIntAttribute(ui::AX_ATTR_INVALID_STATE) != ui::AX_INVALID_STATE_FALSE) + if (HasIntAttribute(AX_ATTR_INVALID_STATE) && + GetIntAttribute(AX_ATTR_INVALID_STATE) != AX_INVALID_STATE_FALSE) ia2_state |= IA2_STATE_INVALID_ENTRY; - if (data.HasState(ui::AX_STATE_REQUIRED)) + if (data.HasState(AX_STATE_REQUIRED)) ia2_state |= IA2_STATE_REQUIRED; - if (data.HasState(ui::AX_STATE_VERTICAL)) + if (data.HasState(AX_STATE_VERTICAL)) ia2_state |= IA2_STATE_VERTICAL; - if (data.HasState(ui::AX_STATE_HORIZONTAL)) + if (data.HasState(AX_STATE_HORIZONTAL)) ia2_state |= IA2_STATE_HORIZONTAL; - const bool is_editable = data.HasState(ui::AX_STATE_EDITABLE); + const bool is_editable = data.HasState(AX_STATE_EDITABLE); if (is_editable) ia2_state |= IA2_STATE_EDITABLE; - if (IsRichTextControl() || ui::IsEditField(data.role)) { + if (IsRichTextControl() || IsEditField(data.role)) { // Support multi/single line states if root editable or appropriate role. // We support the edit box roles even if the area is not actually editable, // because it is technically feasible for JS to implement the edit box // by controlling selection. - if (data.HasState(ui::AX_STATE_MULTILINE)) { + if (data.HasState(AX_STATE_MULTILINE)) { ia2_state |= IA2_STATE_MULTI_LINE; } else { ia2_state |= IA2_STATE_SINGLE_LINE; } } - if (!GetStringAttribute(ui::AX_ATTR_AUTO_COMPLETE).empty()) + if (!GetStringAttribute(AX_ATTR_AUTO_COMPLETE).empty()) ia2_state |= IA2_STATE_SUPPORTS_AUTOCOMPLETION; - if (GetBoolAttribute(ui::AX_ATTR_MODAL)) + if (GetBoolAttribute(AX_ATTR_MODAL)) ia2_state |= IA2_STATE_MODAL; switch (data.role) { - case ui::AX_ROLE_MENU_LIST_POPUP: + case AX_ROLE_MENU_LIST_POPUP: ia2_state &= ~(IA2_STATE_EDITABLE); break; - case ui::AX_ROLE_MENU_LIST_OPTION: + case AX_ROLE_MENU_LIST_OPTION: ia2_state &= ~(IA2_STATE_EDITABLE); break; - case ui::AX_ROLE_SCROLL_AREA: - ia2_state &= ~(IA2_STATE_EDITABLE); - break; - case ui::AX_ROLE_TEXT_FIELD: - case ui::AX_ROLE_SEARCH_BOX: - if (data.HasState(ui::AX_STATE_MULTILINE)) { + case AX_ROLE_TEXT_FIELD: + case AX_ROLE_SEARCH_BOX: + if (data.HasState(AX_STATE_MULTILINE)) { ia2_state |= IA2_STATE_MULTI_LINE; } else { ia2_state |= IA2_STATE_SINGLE_LINE; @@ -2699,123 +2969,117 @@ int32_t AXPlatformNodeWin::ComputeIA2Role() { int32_t ia2_role = 0; switch (GetData().role) { - case ui::AX_ROLE_BANNER: + case AX_ROLE_BANNER: ia2_role = IA2_ROLE_HEADER; break; - case ui::AX_ROLE_BLOCKQUOTE: + case AX_ROLE_BLOCKQUOTE: ia2_role = IA2_ROLE_SECTION; break; - case ui::AX_ROLE_CANVAS: - if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) { + case AX_ROLE_CANVAS: + if (GetBoolAttribute(AX_ATTR_CANVAS_HAS_FALLBACK)) { ia2_role = IA2_ROLE_CANVAS; } break; - case ui::AX_ROLE_CAPTION: + case AX_ROLE_CAPTION: ia2_role = IA2_ROLE_CAPTION; break; - case ui::AX_ROLE_COLOR_WELL: + case AX_ROLE_COLOR_WELL: ia2_role = IA2_ROLE_COLOR_CHOOSER; break; - case ui::AX_ROLE_COMPLEMENTARY: + case AX_ROLE_COMPLEMENTARY: ia2_role = IA2_ROLE_NOTE; break; - case ui::AX_ROLE_CONTENT_INFO: + case AX_ROLE_CONTENT_INFO: ia2_role = IA2_ROLE_PARAGRAPH; break; - case ui::AX_ROLE_DATE: - case ui::AX_ROLE_DATE_TIME: + case AX_ROLE_DATE: + case AX_ROLE_DATE_TIME: ia2_role = IA2_ROLE_DATE_EDITOR; break; - case ui::AX_ROLE_DEFINITION: + case AX_ROLE_DEFINITION: ia2_role = IA2_ROLE_PARAGRAPH; break; - case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL: + case AX_ROLE_DESCRIPTION_LIST_DETAIL: ia2_role = IA2_ROLE_PARAGRAPH; break; - case ui::AX_ROLE_EMBEDDED_OBJECT: + case AX_ROLE_EMBEDDED_OBJECT: if (!delegate_->GetChildCount()) { ia2_role = IA2_ROLE_EMBEDDED_OBJECT; } break; - case ui::AX_ROLE_FIGCAPTION: + case AX_ROLE_FIGCAPTION: ia2_role = IA2_ROLE_CAPTION; break; - case ui::AX_ROLE_FORM: + case AX_ROLE_FORM: ia2_role = IA2_ROLE_FORM; break; - case ui::AX_ROLE_FOOTER: + case AX_ROLE_FOOTER: ia2_role = IA2_ROLE_FOOTER; break; - case ui::AX_ROLE_GENERIC_CONTAINER: + case AX_ROLE_GENERIC_CONTAINER: ia2_role = IA2_ROLE_SECTION; break; - case ui::AX_ROLE_HEADING: + case AX_ROLE_HEADING: ia2_role = IA2_ROLE_HEADING; break; - case ui::AX_ROLE_IFRAME: + case AX_ROLE_IFRAME: ia2_role = IA2_ROLE_INTERNAL_FRAME; break; - case ui::AX_ROLE_IMAGE_MAP: + case AX_ROLE_IMAGE_MAP: ia2_role = IA2_ROLE_IMAGE_MAP; break; - case ui::AX_ROLE_LABEL_TEXT: - case ui::AX_ROLE_LEGEND: + case AX_ROLE_LABEL_TEXT: + case AX_ROLE_LEGEND: ia2_role = IA2_ROLE_LABEL; break; - case ui::AX_ROLE_MAIN: + case AX_ROLE_MAIN: ia2_role = IA2_ROLE_PARAGRAPH; break; - case ui::AX_ROLE_MARK: + case AX_ROLE_MARK: ia2_role = IA2_ROLE_TEXT_FRAME; break; - case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: + case AX_ROLE_MENU_ITEM_CHECK_BOX: ia2_role = IA2_ROLE_CHECK_MENU_ITEM; break; - case ui::AX_ROLE_MENU_ITEM_RADIO: + case AX_ROLE_MENU_ITEM_RADIO: ia2_role = IA2_ROLE_RADIO_MENU_ITEM; break; - case ui::AX_ROLE_NAVIGATION: + case AX_ROLE_NAVIGATION: ia2_role = IA2_ROLE_SECTION; break; - case ui::AX_ROLE_NOTE: + case AX_ROLE_NOTE: ia2_role = IA2_ROLE_NOTE; break; - case ui::AX_ROLE_PARAGRAPH: + case AX_ROLE_PARAGRAPH: ia2_role = IA2_ROLE_PARAGRAPH; break; - case ui::AX_ROLE_PRE: + case AX_ROLE_PRE: ia2_role = IA2_ROLE_PARAGRAPH; break; - case ui::AX_ROLE_REGION: { - base::string16 html_tag = GetString16Attribute(ui::AX_ATTR_HTML_TAG); + case AX_ROLE_REGION: { + base::string16 html_tag = GetString16Attribute(AX_ATTR_HTML_TAG); if (html_tag == L"section") { ia2_role = IA2_ROLE_SECTION; } } break; - case ui::AX_ROLE_RUBY: + case AX_ROLE_RUBY: ia2_role = IA2_ROLE_TEXT_FRAME; break; - case ui::AX_ROLE_RULER: - ia2_role = IA2_ROLE_RULER; - break; - case ui::AX_ROLE_SCROLL_AREA: - ia2_role = IA2_ROLE_SCROLL_PANE; - break; - case ui::AX_ROLE_SEARCH: + case AX_ROLE_SEARCH: ia2_role = IA2_ROLE_SECTION; break; - case ui::AX_ROLE_SWITCH: + case AX_ROLE_SWITCH: ia2_role = IA2_ROLE_TOGGLE_BUTTON; break; - case ui::AX_ROLE_TABLE_HEADER_CONTAINER: + case AX_ROLE_TABLE_HEADER_CONTAINER: ia2_role = IA2_ROLE_SECTION; break; - case ui::AX_ROLE_TOGGLE_BUTTON: + case AX_ROLE_TOGGLE_BUTTON: ia2_role = IA2_ROLE_TOGGLE_BUTTON; break; - case ui::AX_ROLE_ABBR: - case ui::AX_ROLE_TIME: + case AX_ROLE_ABBR: + case AX_ROLE_TIME: ia2_role = IA2_ROLE_TEXT_FRAME; break; default: @@ -2831,84 +3095,83 @@ std::vector<base::string16> AXPlatformNodeWin::ComputeIA2Attributes() { // historical reasons. Aside from that virtually every ARIA attribute // is exposed in a really straightforward way, i.e. "aria-foo" is exposed // as "foo". - StringAttributeToIA2(result, ui::AX_ATTR_DISPLAY, "display"); - StringAttributeToIA2(result, ui::AX_ATTR_HTML_TAG, "tag"); - StringAttributeToIA2(result, ui::AX_ATTR_ROLE, "xml-roles"); - StringAttributeToIA2(result, ui::AX_ATTR_PLACEHOLDER, "placeholder"); + StringAttributeToIA2(result, AX_ATTR_DISPLAY, "display"); + StringAttributeToIA2(result, AX_ATTR_HTML_TAG, "tag"); + StringAttributeToIA2(result, AX_ATTR_ROLE, "xml-roles"); + StringAttributeToIA2(result, AX_ATTR_PLACEHOLDER, "placeholder"); - StringAttributeToIA2(result, ui::AX_ATTR_AUTO_COMPLETE, "autocomplete"); - StringAttributeToIA2(result, ui::AX_ATTR_ROLE_DESCRIPTION, "roledescription"); - StringAttributeToIA2(result, ui::AX_ATTR_KEY_SHORTCUTS, "keyshortcuts"); + StringAttributeToIA2(result, AX_ATTR_AUTO_COMPLETE, "autocomplete"); + StringAttributeToIA2(result, AX_ATTR_ROLE_DESCRIPTION, "roledescription"); + StringAttributeToIA2(result, AX_ATTR_KEY_SHORTCUTS, "keyshortcuts"); - IntAttributeToIA2(result, ui::AX_ATTR_HIERARCHICAL_LEVEL, "level"); - IntAttributeToIA2(result, ui::AX_ATTR_SET_SIZE, "setsize"); - IntAttributeToIA2(result, ui::AX_ATTR_POS_IN_SET, "posinset"); + IntAttributeToIA2(result, AX_ATTR_HIERARCHICAL_LEVEL, "level"); + IntAttributeToIA2(result, AX_ATTR_SET_SIZE, "setsize"); + IntAttributeToIA2(result, AX_ATTR_POS_IN_SET, "posinset"); - if (HasIntAttribute(ui::AX_ATTR_CHECKED_STATE)) + if (HasIntAttribute(AX_ATTR_CHECKED_STATE)) result.push_back(L"checkable:true"); // Expose live region attributes. - StringAttributeToIA2(result, ui::AX_ATTR_LIVE_STATUS, "live"); - StringAttributeToIA2(result, ui::AX_ATTR_LIVE_RELEVANT, "relevant"); - BoolAttributeToIA2(result, ui::AX_ATTR_LIVE_ATOMIC, "atomic"); - BoolAttributeToIA2(result, ui::AX_ATTR_LIVE_BUSY, "busy"); + StringAttributeToIA2(result, AX_ATTR_LIVE_STATUS, "live"); + StringAttributeToIA2(result, AX_ATTR_LIVE_RELEVANT, "relevant"); + BoolAttributeToIA2(result, AX_ATTR_LIVE_ATOMIC, "atomic"); + // Busy is usually associated with live regions but can occur anywhere: + BoolAttributeToIA2(result, AX_ATTR_BUSY, "busy"); // Expose container live region attributes. - StringAttributeToIA2(result, ui::AX_ATTR_CONTAINER_LIVE_STATUS, - "container-live"); - StringAttributeToIA2(result, ui::AX_ATTR_CONTAINER_LIVE_RELEVANT, + StringAttributeToIA2(result, AX_ATTR_CONTAINER_LIVE_STATUS, "container-live"); + StringAttributeToIA2(result, AX_ATTR_CONTAINER_LIVE_RELEVANT, "container-relevant"); - BoolAttributeToIA2(result, ui::AX_ATTR_CONTAINER_LIVE_ATOMIC, - "container-atomic"); - BoolAttributeToIA2(result, ui::AX_ATTR_CONTAINER_LIVE_BUSY, "container-busy"); + BoolAttributeToIA2(result, AX_ATTR_CONTAINER_LIVE_ATOMIC, "container-atomic"); + BoolAttributeToIA2(result, AX_ATTR_CONTAINER_LIVE_BUSY, "container-busy"); // Expose the non-standard explicit-name IA2 attribute. int name_from; - if (GetIntAttribute(ui::AX_ATTR_NAME_FROM, &name_from) && - name_from != ui::AX_NAME_FROM_CONTENTS) { + if (GetIntAttribute(AX_ATTR_NAME_FROM, &name_from) && + name_from != AX_NAME_FROM_CONTENTS) { result.push_back(L"explicit-name:true"); } // Expose the aria-current attribute. int32_t aria_current_state; - if (GetIntAttribute(ui::AX_ATTR_ARIA_CURRENT_STATE, &aria_current_state)) { - switch (static_cast<ui::AXAriaCurrentState>(aria_current_state)) { - case ui::AX_ARIA_CURRENT_STATE_NONE: + if (GetIntAttribute(AX_ATTR_ARIA_CURRENT_STATE, &aria_current_state)) { + switch (static_cast<AXAriaCurrentState>(aria_current_state)) { + case AX_ARIA_CURRENT_STATE_NONE: break; - case ui::AX_ARIA_CURRENT_STATE_FALSE: + case AX_ARIA_CURRENT_STATE_FALSE: result.push_back(L"current:false"); break; - case ui::AX_ARIA_CURRENT_STATE_TRUE: + case AX_ARIA_CURRENT_STATE_TRUE: result.push_back(L"current:true"); break; - case ui::AX_ARIA_CURRENT_STATE_PAGE: + case AX_ARIA_CURRENT_STATE_PAGE: result.push_back(L"current:page"); break; - case ui::AX_ARIA_CURRENT_STATE_STEP: + case AX_ARIA_CURRENT_STATE_STEP: result.push_back(L"current:step"); break; - case ui::AX_ARIA_CURRENT_STATE_LOCATION: + case AX_ARIA_CURRENT_STATE_LOCATION: result.push_back(L"current:location"); break; - case ui::AX_ARIA_CURRENT_STATE_DATE: + case AX_ARIA_CURRENT_STATE_DATE: result.push_back(L"current:date"); break; - case ui::AX_ARIA_CURRENT_STATE_TIME: + case AX_ARIA_CURRENT_STATE_TIME: result.push_back(L"current:time"); break; } } // Expose table cell index. - if (ui::IsCellOrTableHeaderRole(GetData().role)) { + if (IsCellOrTableHeaderRole(GetData().role)) { AXPlatformNodeBase* table = FromNativeViewAccessible(GetParent()); - while (table && !ui::IsTableLikeRole(table->GetData().role)) + while (table && !IsTableLikeRole(table->GetData().role)) table = FromNativeViewAccessible(table->GetParent()); if (table) { const std::vector<int32_t>& unique_cell_ids = - table->GetIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS); + table->GetIntListAttribute(AX_ATTR_UNIQUE_CELL_IDS); for (size_t i = 0; i < unique_cell_ids.size(); ++i) { if (unique_cell_ids[i] == GetData().id) { result.push_back(base::string16(L"table-cell-index:") + @@ -2919,43 +3182,43 @@ std::vector<base::string16> AXPlatformNodeWin::ComputeIA2Attributes() { } // Expose aria-colcount and aria-rowcount in a table, grid or treegrid. - if (ui::IsTableLikeRole(GetData().role)) { - IntAttributeToIA2(result, ui::AX_ATTR_ARIA_COLUMN_COUNT, "colcount"); - IntAttributeToIA2(result, ui::AX_ATTR_ARIA_ROW_COUNT, "rowcount"); + if (IsTableLikeRole(GetData().role)) { + IntAttributeToIA2(result, AX_ATTR_ARIA_COLUMN_COUNT, "colcount"); + IntAttributeToIA2(result, AX_ATTR_ARIA_ROW_COUNT, "rowcount"); } // Expose aria-colindex and aria-rowindex in a cell or row. - if (ui::IsCellOrTableHeaderRole(GetData().role) || - GetData().role == ui::AX_ROLE_ROW) { - if (GetData().role != ui::AX_ROLE_ROW) - IntAttributeToIA2(result, ui::AX_ATTR_ARIA_CELL_COLUMN_INDEX, "colindex"); - IntAttributeToIA2(result, ui::AX_ATTR_ARIA_CELL_ROW_INDEX, "rowindex"); + if (IsCellOrTableHeaderRole(GetData().role) || + GetData().role == AX_ROLE_ROW) { + if (GetData().role != AX_ROLE_ROW) + IntAttributeToIA2(result, AX_ATTR_ARIA_CELL_COLUMN_INDEX, "colindex"); + IntAttributeToIA2(result, AX_ATTR_ARIA_CELL_ROW_INDEX, "rowindex"); } // Expose row or column header sort direction. int32_t sort_direction; if ((MSAARole() == ROLE_SYSTEM_COLUMNHEADER || MSAARole() == ROLE_SYSTEM_ROWHEADER) && - GetIntAttribute(ui::AX_ATTR_SORT_DIRECTION, &sort_direction)) { - switch (static_cast<ui::AXSortDirection>(sort_direction)) { - case ui::AX_SORT_DIRECTION_NONE: + GetIntAttribute(AX_ATTR_SORT_DIRECTION, &sort_direction)) { + switch (static_cast<AXSortDirection>(sort_direction)) { + case AX_SORT_DIRECTION_NONE: break; - case ui::AX_SORT_DIRECTION_UNSORTED: + case AX_SORT_DIRECTION_UNSORTED: result.push_back(L"sort:none"); break; - case ui::AX_SORT_DIRECTION_ASCENDING: + case AX_SORT_DIRECTION_ASCENDING: result.push_back(L"sort:ascending"); break; - case ui::AX_SORT_DIRECTION_DESCENDING: + case AX_SORT_DIRECTION_DESCENDING: result.push_back(L"sort:descending"); break; - case ui::AX_SORT_DIRECTION_OTHER: + case AX_SORT_DIRECTION_OTHER: result.push_back(L"sort:other"); break; } } - if (ui::IsCellOrTableHeaderRole(GetData().role)) { + if (IsCellOrTableHeaderRole(GetData().role)) { // Expose colspan attribute. base::string16 colspan; if (GetData().GetHtmlAttribute("aria-colspan", &colspan)) { @@ -3000,7 +3263,7 @@ std::vector<base::string16> AXPlatformNodeWin::ComputeIA2Attributes() { // Expose datetime attribute. base::string16 datetime; - if (GetData().role == ui::AX_ROLE_TIME && + if (GetData().role == AX_ROLE_TIME && GetData().GetHtmlAttribute("datetime", &datetime)) { SanitizeStringAttributeForIA2(datetime, &datetime); result.push_back(L"datetime:" + datetime); @@ -3015,15 +3278,24 @@ std::vector<base::string16> AXPlatformNodeWin::ComputeIA2Attributes() { // Expose src attribute. base::string16 src; - if (GetData().role == ui::AX_ROLE_IMAGE && + if (GetData().role == AX_ROLE_IMAGE && GetData().GetHtmlAttribute("src", &src)) { SanitizeStringAttributeForIA2(src, &src); result.push_back(L"src:" + src); } + // Text fields need to report the attribute "text-model:a1" to instruct + // screen readers to use IAccessible2 APIs to handle text editing in this + // object (as opposed to treating it like a native Windows text box). + // The text-model:a1 attribute is documented here: + // http://www.linuxfoundation.org/collaborate/workgroups/accessibility/ia2/ia2_implementation_guide + if (GetData().role == AX_ROLE_TEXT_FIELD) { + result.push_back(L"text-model:a1;"); + } + // Expose input-text type attribute. base::string16 type; - base::string16 html_tag = GetString16Attribute(ui::AX_ATTR_HTML_TAG); + base::string16 html_tag = GetString16Attribute(AX_ATTR_HTML_TAG); if (IsSimpleTextControl() && html_tag == L"input" && GetData().GetHtmlAttribute("type", &type)) { SanitizeStringAttributeForIA2(type, &type); @@ -3036,30 +3308,26 @@ std::vector<base::string16> AXPlatformNodeWin::ComputeIA2Attributes() { bool AXPlatformNodeWin::ShouldNodeHaveReadonlyStateByDefault( const AXNodeData& data) const { switch (data.role) { - case ui::AX_ROLE_ARTICLE: - case ui::AX_ROLE_BUSY_INDICATOR: - case ui::AX_ROLE_DEFINITION: - case ui::AX_ROLE_DESCRIPTION_LIST: - case ui::AX_ROLE_DESCRIPTION_LIST_TERM: - case ui::AX_ROLE_DOCUMENT: - case ui::AX_ROLE_IFRAME: - case ui::AX_ROLE_IMAGE: - case ui::AX_ROLE_IMAGE_MAP: - case ui::AX_ROLE_IMAGE_MAP_LINK: - case ui::AX_ROLE_LIST: - case ui::AX_ROLE_LIST_ITEM: - case ui::AX_ROLE_PROGRESS_INDICATOR: - case ui::AX_ROLE_ROOT_WEB_AREA: - case ui::AX_ROLE_RULER: - case ui::AX_ROLE_SCROLL_AREA: - case ui::AX_ROLE_TERM: - case ui::AX_ROLE_TIMER: - case ui::AX_ROLE_TOOLBAR: - case ui::AX_ROLE_TOOLTIP: - case ui::AX_ROLE_WEB_AREA: + case AX_ROLE_ARTICLE: + case AX_ROLE_DEFINITION: + case AX_ROLE_DESCRIPTION_LIST: + case AX_ROLE_DESCRIPTION_LIST_TERM: + case AX_ROLE_DOCUMENT: + case AX_ROLE_IFRAME: + case AX_ROLE_IMAGE: + case AX_ROLE_IMAGE_MAP: + case AX_ROLE_LIST: + case AX_ROLE_LIST_ITEM: + case AX_ROLE_PROGRESS_INDICATOR: + case AX_ROLE_ROOT_WEB_AREA: + case AX_ROLE_TERM: + case AX_ROLE_TIMER: + case AX_ROLE_TOOLBAR: + case AX_ROLE_TOOLTIP: + case AX_ROLE_WEB_AREA: return true; - case ui::AX_ROLE_GRID: + case AX_ROLE_GRID: // TODO(aleventhal) this changed between ARIA 1.0 and 1.1, // need to determine whether grids/treegrids should really be readonly // or editable by default @@ -3075,24 +3343,24 @@ bool AXPlatformNodeWin::ShouldNodeHaveReadonlyStateByDefault( bool AXPlatformNodeWin::ShouldNodeHaveFocusableState( const AXNodeData& data) const { switch (data.role) { - case ui::AX_ROLE_DOCUMENT: - case ui::AX_ROLE_ROOT_WEB_AREA: - case ui::AX_ROLE_WEB_AREA: + case AX_ROLE_DOCUMENT: + case AX_ROLE_ROOT_WEB_AREA: + case AX_ROLE_WEB_AREA: return true; - case ui::AX_ROLE_IFRAME: + case AX_ROLE_IFRAME: return false; - case ui::AX_ROLE_LIST_BOX_OPTION: - case ui::AX_ROLE_MENU_LIST_OPTION: - if (data.HasState(ui::AX_STATE_SELECTABLE)) + case AX_ROLE_LIST_BOX_OPTION: + case AX_ROLE_MENU_LIST_OPTION: + if (data.HasState(AX_STATE_SELECTABLE)) return true; default: break; } - return data.HasState(ui::AX_STATE_FOCUSABLE); + return data.HasState(AX_STATE_FOCUSABLE); } int AXPlatformNodeWin::MSAAState() { @@ -3102,29 +3370,29 @@ int AXPlatformNodeWin::MSAAState() { // Map the AXState to MSAA state. Note that some of the states are not // currently handled. - if (data.HasState(ui::AX_STATE_BUSY)) + if (data.GetBoolAttribute(AX_ATTR_BUSY)) msaa_state |= STATE_SYSTEM_BUSY; - if (data.HasState(ui::AX_STATE_COLLAPSED)) + if (data.HasState(AX_STATE_COLLAPSED)) msaa_state |= STATE_SYSTEM_COLLAPSED; - if (data.HasState(ui::AX_STATE_DEFAULT)) + if (data.HasState(AX_STATE_DEFAULT)) msaa_state |= STATE_SYSTEM_DEFAULT; // TODO(dougt) unhandled ux::AX_STATE_EDITABLE - if (data.HasState(ui::AX_STATE_EXPANDED)) + if (data.HasState(AX_STATE_EXPANDED)) msaa_state |= STATE_SYSTEM_EXPANDED; if (ShouldNodeHaveFocusableState(data)) msaa_state |= STATE_SYSTEM_FOCUSABLE; - if (data.HasState(ui::AX_STATE_HASPOPUP)) + if (data.HasState(AX_STATE_HASPOPUP)) msaa_state |= STATE_SYSTEM_HASPOPUP; // TODO(dougt) unhandled ux::AX_STATE_HORIZONTAL - if (data.HasState(ui::AX_STATE_HOVERED)) { + if (data.HasState(AX_STATE_HOVERED)) { // Expose whether or not the mouse is over an element, but suppress // this for tests because it can make the test results flaky depending // on the position of the mouse. @@ -3133,52 +3401,50 @@ int AXPlatformNodeWin::MSAAState() { } // TODO(dougt) Why do we set any state on AX_ROLE_IGNORED? - if (data.HasState(ui::AX_STATE_INVISIBLE) || - GetData().role == ui::AX_ROLE_IGNORED) { + if (data.HasState(AX_STATE_INVISIBLE) || GetData().role == AX_ROLE_IGNORED) { msaa_state |= STATE_SYSTEM_INVISIBLE; } - if (data.HasState(ui::AX_STATE_LINKED)) + if (data.HasState(AX_STATE_LINKED)) msaa_state |= STATE_SYSTEM_LINKED; // TODO(dougt) unhandled ux::AX_STATE_MULTILINE - if (data.HasState(ui::AX_STATE_MULTISELECTABLE)) { + if (data.HasState(AX_STATE_MULTISELECTABLE)) { msaa_state |= STATE_SYSTEM_EXTSELECTABLE; msaa_state |= STATE_SYSTEM_MULTISELECTABLE; } - if (data.HasState(ui::AX_STATE_OFFSCREEN)) + if (data.HasState(AX_STATE_OFFSCREEN)) msaa_state |= STATE_SYSTEM_OFFSCREEN; - if (data.HasState(ui::AX_STATE_PROTECTED)) + if (data.HasState(AX_STATE_PROTECTED)) msaa_state |= STATE_SYSTEM_PROTECTED; // TODO(dougt) unhandled ux::AX_STATE_REQUIRED // TODO(dougt) unhandled ux::AX_STATE_RICHLY_EDITABLE - if (data.HasState(ui::AX_STATE_SELECTABLE)) + if (data.HasState(AX_STATE_SELECTABLE)) msaa_state |= STATE_SYSTEM_SELECTABLE; - if (data.HasState(ui::AX_STATE_SELECTED)) + if (data.HasState(AX_STATE_SELECTED)) msaa_state |= STATE_SYSTEM_SELECTED; // TODO(dougt) unhandled VERTICAL - if (data.HasState(ui::AX_STATE_VISITED)) + if (data.HasState(AX_STATE_VISITED)) msaa_state |= STATE_SYSTEM_TRAVERSED; // // Checked state // - const auto checked_state = static_cast<ui::AXCheckedState>( - GetIntAttribute(ui::AX_ATTR_CHECKED_STATE)); + const auto checked_state = + static_cast<AXCheckedState>(GetIntAttribute(AX_ATTR_CHECKED_STATE)); switch (checked_state) { - case ui::AX_CHECKED_STATE_TRUE: - msaa_state |= data.role == ui::AX_ROLE_TOGGLE_BUTTON - ? STATE_SYSTEM_PRESSED - : STATE_SYSTEM_CHECKED; + case AX_CHECKED_STATE_TRUE: + msaa_state |= data.role == AX_ROLE_TOGGLE_BUTTON ? STATE_SYSTEM_PRESSED + : STATE_SYSTEM_CHECKED; break; - case ui::AX_CHECKED_STATE_MIXED: + case AX_CHECKED_STATE_MIXED: msaa_state |= STATE_SYSTEM_MIXED; break; default: @@ -3186,12 +3452,12 @@ int AXPlatformNodeWin::MSAAState() { } const auto restriction = - static_cast<ui::AXRestriction>(GetIntAttribute(ui::AX_ATTR_RESTRICTION)); + static_cast<AXRestriction>(GetIntAttribute(AX_ATTR_RESTRICTION)); switch (restriction) { - case ui::AX_RESTRICTION_DISABLED: + case AX_RESTRICTION_DISABLED: msaa_state |= STATE_SYSTEM_UNAVAILABLE; break; - case ui::AX_RESTRICTION_READ_ONLY: + case AX_RESTRICTION_READ_ONLY: msaa_state |= STATE_SYSTEM_READONLY; break; default: @@ -3218,54 +3484,50 @@ int AXPlatformNodeWin::MSAAState() { // TODO(dmazzoni): this should probably check if focus is actually inside // the menu bar, but we don't currently track focus inside menu pop-ups, // and Chrome only has one menu visible at a time so this works for now. - if (data.role == ui::AX_ROLE_MENU_BAR && - !(data.HasState(ui::AX_STATE_INVISIBLE))) { + if (data.role == AX_ROLE_MENU_BAR && !(data.HasState(AX_STATE_INVISIBLE))) { msaa_state |= STATE_SYSTEM_FOCUSED; } // Handle STATE_SYSTEM_LINKED - if (GetData().role == ui::AX_ROLE_IMAGE_MAP_LINK || - GetData().role == ui::AX_ROLE_LINK) { + if (GetData().role == AX_ROLE_LINK) msaa_state |= STATE_SYSTEM_LINKED; - } return msaa_state; } -int AXPlatformNodeWin::MSAAEvent(ui::AXEvent event) { +int AXPlatformNodeWin::MSAAEvent(AXEvent event) { switch (event) { - case ui::AX_EVENT_ALERT: + case AX_EVENT_ALERT: return EVENT_SYSTEM_ALERT; - case ui::AX_EVENT_FOCUS: + case AX_EVENT_FOCUS: return EVENT_OBJECT_FOCUS; - case ui::AX_EVENT_MENU_START: + case AX_EVENT_MENU_START: return EVENT_SYSTEM_MENUSTART; - case ui::AX_EVENT_MENU_END: + case AX_EVENT_MENU_END: return EVENT_SYSTEM_MENUEND; - case ui::AX_EVENT_MENU_POPUP_START: + case AX_EVENT_MENU_POPUP_START: return EVENT_SYSTEM_MENUPOPUPSTART; - case ui::AX_EVENT_MENU_POPUP_END: + case AX_EVENT_MENU_POPUP_END: return EVENT_SYSTEM_MENUPOPUPEND; - case ui::AX_EVENT_SELECTION: + case AX_EVENT_SELECTION: return EVENT_OBJECT_SELECTION; - case ui::AX_EVENT_SELECTION_ADD: + case AX_EVENT_SELECTION_ADD: return EVENT_OBJECT_SELECTIONADD; - case ui::AX_EVENT_SELECTION_REMOVE: + case AX_EVENT_SELECTION_REMOVE: return EVENT_OBJECT_SELECTIONREMOVE; - case ui::AX_EVENT_TEXT_CHANGED: + case AX_EVENT_TEXT_CHANGED: return EVENT_OBJECT_NAMECHANGE; - case ui::AX_EVENT_TEXT_SELECTION_CHANGED: + case AX_EVENT_TEXT_SELECTION_CHANGED: return IA2_EVENT_TEXT_CARET_MOVED; - case ui::AX_EVENT_VALUE_CHANGED: + case AX_EVENT_VALUE_CHANGED: return EVENT_OBJECT_VALUECHANGE; default: return -1; } } -HRESULT AXPlatformNodeWin::GetStringAttributeAsBstr( - ui::AXStringAttribute attribute, - BSTR* value_bstr) const { +HRESULT AXPlatformNodeWin::GetStringAttributeAsBstr(AXStringAttribute attribute, + BSTR* value_bstr) const { base::string16 str; if (!GetString16Attribute(attribute, &str)) @@ -3287,9 +3549,9 @@ void AXPlatformNodeWin::RemoveAlertTarget() { } base::string16 AXPlatformNodeWin::TextForIAccessibleText() { - if (GetData().role == ui::AX_ROLE_TEXT_FIELD) - return GetString16Attribute(ui::AX_ATTR_VALUE); - return GetString16Attribute(ui::AX_ATTR_NAME); + if (GetData().role == AX_ROLE_TEXT_FIELD) + return GetString16Attribute(AX_ATTR_VALUE); + return GetString16Attribute(AX_ATTR_NAME); } void AXPlatformNodeWin::HandleSpecialTextOffset(LONG* offset) { @@ -3300,32 +3562,37 @@ void AXPlatformNodeWin::HandleSpecialTextOffset(LONG* offset) { } } -ui::TextBoundaryType AXPlatformNodeWin::IA2TextBoundaryToTextBoundary( +TextBoundaryType AXPlatformNodeWin::IA2TextBoundaryToTextBoundary( IA2TextBoundaryType ia2_boundary) { switch(ia2_boundary) { - case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY; - case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY; - case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY; - case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY; - case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY; - case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY; + case IA2_TEXT_BOUNDARY_CHAR: + return CHAR_BOUNDARY; + case IA2_TEXT_BOUNDARY_WORD: + return WORD_BOUNDARY; + case IA2_TEXT_BOUNDARY_LINE: + return LINE_BOUNDARY; + case IA2_TEXT_BOUNDARY_SENTENCE: + return SENTENCE_BOUNDARY; + case IA2_TEXT_BOUNDARY_PARAGRAPH: + return PARAGRAPH_BOUNDARY; + case IA2_TEXT_BOUNDARY_ALL: + return ALL_BOUNDARY; default: NOTREACHED(); - return ui::CHAR_BOUNDARY; + return CHAR_BOUNDARY; } } -LONG AXPlatformNodeWin::FindBoundary( - const base::string16& text, - IA2TextBoundaryType ia2_boundary, - LONG start_offset, - ui::TextBoundaryDirection direction) { +LONG AXPlatformNodeWin::FindBoundary(const base::string16& text, + IA2TextBoundaryType ia2_boundary, + LONG start_offset, + TextBoundaryDirection direction) { HandleSpecialTextOffset(&start_offset); - ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); + TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); std::vector<int32_t> line_breaks; - return static_cast<LONG>(ui::FindAccessibleTextBoundary( - text, line_breaks, boundary, start_offset, direction, - AX_TEXT_AFFINITY_DOWNSTREAM)); + return static_cast<LONG>( + FindAccessibleTextBoundary(text, line_breaks, boundary, start_offset, + direction, AX_TEXT_AFFINITY_DOWNSTREAM)); } AXPlatformNodeWin* AXPlatformNodeWin::GetTargetFromChildID( @@ -3362,16 +3629,45 @@ AXPlatformNodeWin* AXPlatformNodeWin::GetTargetFromChildID( } bool AXPlatformNodeWin::IsInTreeGrid() { - auto* container = FromNativeViewAccessible(GetParent()); + AXPlatformNodeBase* container = FromNativeViewAccessible(GetParent()); // If parent was a rowgroup, we need to look at the grandparent - if (container && container->GetData().role == ui::AX_ROLE_GROUP) + if (container && container->GetData().role == AX_ROLE_GROUP) container = FromNativeViewAccessible(container->GetParent()); if (!container) return false; - return container->GetData().role == ui::AX_ROLE_TREE_GRID; + return container->GetData().role == AX_ROLE_TREE_GRID; +} + +HRESULT AXPlatformNodeWin::AllocateComArrayFromVector( + std::vector<LONG>& results, + LONG max, + LONG** selected, + LONG* n_selected) { + DCHECK_GT(max, 0); + DCHECK(selected); + DCHECK(n_selected); + + auto count = std::min((LONG)results.size(), max); + *n_selected = count; + *selected = static_cast<LONG*>(CoTaskMemAlloc(sizeof(LONG) * count)); + + for (LONG i = 0; i < count; i++) + (*selected)[i] = results[i]; + return S_OK; +} + +// TODO(dmazzoni): Remove this function once combo box refactoring is complete. +bool AXPlatformNodeWin::IsAncestorComboBox() { + auto* parent = + static_cast<AXPlatformNodeWin*>(FromNativeViewAccessible(GetParent())); + if (!parent) + return false; + if (parent->MSAARole() == ROLE_SYSTEM_COMBOBOX) + return true; + return parent->IsAncestorComboBox(); } } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.h b/chromium/ui/accessibility/platform/ax_platform_node_win.h index e85a3c1855b..ecf7e8062b4 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.h @@ -224,17 +224,17 @@ class AXPlatformNodeRelationWin : public CComObjectRootEx<CComMultiThreadModel>, AXPlatformNodeRelationWin(); virtual ~AXPlatformNodeRelationWin(); - void Initialize(ui::AXPlatformNodeWin* owner, const base::string16& type); + void Initialize(AXPlatformNodeWin* owner, const base::string16& type); void AddTarget(int target_id); void RemoveTarget(int target_id); // IAccessibleRelation methods. STDMETHODIMP get_relationType(BSTR* relation_type) override; - STDMETHODIMP get_nTargets(long* n_targets) override; - STDMETHODIMP get_target(long target_index, IUnknown** target) override; - STDMETHODIMP get_targets(long max_targets, + STDMETHODIMP get_nTargets(LONG* n_targets) override; + STDMETHODIMP get_target(LONG target_index, IUnknown** target) override; + STDMETHODIMP get_targets(LONG max_targets, IUnknown** targets, - long* n_targets) override; + LONG* n_targets) override; STDMETHODIMP get_localizedRelationType(BSTR* relation_type) override; // Accessors. @@ -243,22 +243,21 @@ class AXPlatformNodeRelationWin : public CComObjectRootEx<CComMultiThreadModel>, private: base::string16 type_; - base::win::ScopedComPtr<ui::AXPlatformNodeWin> owner_; + base::win::ScopedComPtr<AXPlatformNodeWin> owner_; std::vector<int> target_ids_; }; class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) - AXPlatformNodeWin - : public NON_EXPORTED_BASE(CComObjectRootEx<CComMultiThreadModel>), - public IDispatchImpl<IAccessible2_2, - &IID_IAccessible2, - &LIBID_IAccessible2Lib>, - public IAccessibleText, - public IAccessibleTable, - public IAccessibleTable2, - public IAccessibleTableCell, - public IServiceProvider, - public AXPlatformNodeBase { + AXPlatformNodeWin : public CComObjectRootEx<CComMultiThreadModel>, + public IDispatchImpl<IAccessible2_2, + &IID_IAccessible2, + &LIBID_IAccessible2Lib>, + public IAccessibleText, + public IAccessibleTable, + public IAccessibleTable2, + public IAccessibleTableCell, + public IServiceProvider, + public AXPlatformNodeBase { public: BEGIN_COM_MAP(AXPlatformNodeWin) COM_INTERFACE_ENTRY2(IDispatch, IAccessible2_2) @@ -277,10 +276,12 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) // Clear node's current relationships and set them to the default values. void CalculateRelationships(); + static AXPlatformNode* GetFromUniqueId(int32_t unique_id); + int32_t unique_id() const { return unique_id_; } // AXPlatformNode overrides. gfx::NativeViewAccessible GetNativeViewAccessible() override; - void NotifyAccessibilityEvent(ui::AXEvent event_type) override; + void NotifyAccessibilityEvent(AXEvent event_type) override; // AXPlatformNodeBase overrides. void Destroy() override; @@ -368,9 +369,9 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) STDMETHODIMP get_windowHandle(HWND* window_handle) override; STDMETHODIMP get_relationTargetsOfType(BSTR type, - long max_targets, + LONG max_targets, IUnknown*** targets, - long* n_targets) override; + LONG* n_targets) override; STDMETHODIMP get_attributes(BSTR* attributes) override; @@ -409,7 +410,7 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) LONG* n_localized_extended_states) override; STDMETHODIMP get_locale(IA2Locale* locale) override; STDMETHODIMP get_accessibleWithCaret(IUnknown** accessible, - long* caret_offset) override; + LONG* caret_offset) override; // // IAccessibleText methods. @@ -458,87 +459,87 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) // get_description - also used by IAccessibleImage - STDMETHODIMP get_accessibleAt(long row, - long column, + STDMETHODIMP get_accessibleAt(LONG row, + LONG column, IUnknown** accessible) override; STDMETHODIMP get_caption(IUnknown** accessible) override; - STDMETHODIMP get_childIndex(long row_index, - long column_index, - long* cell_index) override; + STDMETHODIMP get_childIndex(LONG row_index, + LONG column_index, + LONG* cell_index) override; - STDMETHODIMP get_columnDescription(long column, BSTR* description) override; + STDMETHODIMP get_columnDescription(LONG column, BSTR* description) override; STDMETHODIMP - get_columnExtentAt(long row, long column, long* n_columns_spanned) override; + get_columnExtentAt(LONG row, LONG column, LONG* n_columns_spanned) override; STDMETHODIMP get_columnHeader(IAccessibleTable** accessible_table, - long* starting_row_index) override; + LONG* starting_row_index) override; - STDMETHODIMP get_columnIndex(long cell_index, long* column_index) override; + STDMETHODIMP get_columnIndex(LONG cell_index, LONG* column_index) override; - STDMETHODIMP get_nColumns(long* column_count) override; + STDMETHODIMP get_nColumns(LONG* column_count) override; - STDMETHODIMP get_nRows(long* row_count) override; + STDMETHODIMP get_nRows(LONG* row_count) override; - STDMETHODIMP get_nSelectedChildren(long* cell_count) override; + STDMETHODIMP get_nSelectedChildren(LONG* cell_count) override; - STDMETHODIMP get_nSelectedColumns(long* column_count) override; + STDMETHODIMP get_nSelectedColumns(LONG* column_count) override; - STDMETHODIMP get_nSelectedRows(long* row_count) override; + STDMETHODIMP get_nSelectedRows(LONG* row_count) override; - STDMETHODIMP get_rowDescription(long row, BSTR* description) override; + STDMETHODIMP get_rowDescription(LONG row, BSTR* description) override; - STDMETHODIMP get_rowExtentAt(long row, - long column, - long* n_rows_spanned) override; + STDMETHODIMP get_rowExtentAt(LONG row, + LONG column, + LONG* n_rows_spanned) override; STDMETHODIMP get_rowHeader(IAccessibleTable** accessible_table, - long* starting_column_index) override; + LONG* starting_column_index) override; - STDMETHODIMP get_rowIndex(long cell_index, long* row_index) override; + STDMETHODIMP get_rowIndex(LONG cell_index, LONG* row_index) override; - STDMETHODIMP get_selectedChildren(long max_children, - long** children, - long* n_children) override; + STDMETHODIMP get_selectedChildren(LONG max_children, + LONG** children, + LONG* n_children) override; - STDMETHODIMP get_selectedColumns(long max_columns, - long** columns, - long* n_columns) override; + STDMETHODIMP get_selectedColumns(LONG max_columns, + LONG** columns, + LONG* n_columns) override; - STDMETHODIMP get_selectedRows(long max_rows, - long** rows, - long* n_rows) override; + STDMETHODIMP get_selectedRows(LONG max_rows, + LONG** rows, + LONG* n_rows) override; STDMETHODIMP get_summary(IUnknown** accessible) override; STDMETHODIMP - get_isColumnSelected(long column, boolean* is_selected) override; + get_isColumnSelected(LONG column, boolean* is_selected) override; - STDMETHODIMP get_isRowSelected(long row, boolean* is_selected) override; + STDMETHODIMP get_isRowSelected(LONG row, boolean* is_selected) override; - STDMETHODIMP get_isSelected(long row, - long column, + STDMETHODIMP get_isSelected(LONG row, + LONG column, boolean* is_selected) override; STDMETHODIMP - get_rowColumnExtentsAtIndex(long index, - long* row, - long* column, - long* row_extents, - long* column_extents, + get_rowColumnExtentsAtIndex(LONG index, + LONG* row, + LONG* column, + LONG* row_extents, + LONG* column_extents, boolean* is_selected) override; - STDMETHODIMP selectRow(long row) override; + STDMETHODIMP selectRow(LONG row) override; - STDMETHODIMP selectColumn(long column) override; + STDMETHODIMP selectColumn(LONG column) override; - STDMETHODIMP unselectRow(long row) override; + STDMETHODIMP unselectRow(LONG row) override; - STDMETHODIMP unselectColumn(long column) override; + STDMETHODIMP unselectColumn(LONG column) override; STDMETHODIMP get_modelChange(IA2TableModelChange* model_change) override; @@ -550,45 +551,45 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) // unique ones are included here.) // - STDMETHODIMP get_cellAt(long row, long column, IUnknown** cell) override; + STDMETHODIMP get_cellAt(LONG row, LONG column, IUnknown** cell) override; - STDMETHODIMP get_nSelectedCells(long* cell_count) override; + STDMETHODIMP get_nSelectedCells(LONG* cell_count) override; STDMETHODIMP - get_selectedCells(IUnknown*** cells, long* n_selected_cells) override; + get_selectedCells(IUnknown*** cells, LONG* n_selected_cells) override; - STDMETHODIMP get_selectedColumns(long** columns, long* n_columns) override; + STDMETHODIMP get_selectedColumns(LONG** columns, LONG* n_columns) override; - STDMETHODIMP get_selectedRows(long** rows, long* n_rows) override; + STDMETHODIMP get_selectedRows(LONG** rows, LONG* n_rows) override; // // IAccessibleTableCell methods. // STDMETHODIMP - get_columnExtent(long* n_columns_spanned) override; + get_columnExtent(LONG* n_columns_spanned) override; STDMETHODIMP get_columnHeaderCells(IUnknown*** cell_accessibles, - long* n_column_header_cells) override; + LONG* n_column_header_cells) override; - STDMETHODIMP get_columnIndex(long* column_index) override; + STDMETHODIMP get_columnIndex(LONG* column_index) override; - STDMETHODIMP get_rowExtent(long* n_rows_spanned) override; + STDMETHODIMP get_rowExtent(LONG* n_rows_spanned) override; STDMETHODIMP get_rowHeaderCells(IUnknown*** cell_accessibles, - long* n_row_header_cells) override; + LONG* n_row_header_cells) override; - STDMETHODIMP get_rowIndex(long* row_index) override; + STDMETHODIMP get_rowIndex(LONG* row_index) override; STDMETHODIMP get_isSelected(boolean* is_selected) override; STDMETHODIMP - get_rowColumnExtents(long* row, - long* column, - long* row_extents, - long* column_extents, + get_rowColumnExtents(LONG* row, + LONG* column, + LONG* row_extents, + LONG* column_extents, boolean* is_selected) override; STDMETHODIMP get_table(IUnknown** table) override; @@ -650,14 +651,15 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) void Dispose() override; private: - int MSAAEvent(ui::AXEvent event); + int32_t unique_id_; + + int MSAAEvent(AXEvent event); bool IsWebAreaForPresentationalIframe(); bool ShouldNodeHaveReadonlyStateByDefault(const AXNodeData& data) const; bool ShouldNodeHaveFocusableState(const AXNodeData& data) const; - HRESULT GetStringAttributeAsBstr( - ui::AXStringAttribute attribute, - BSTR* value_bstr) const; + HRESULT GetStringAttributeAsBstr(AXStringAttribute attribute, + BSTR* value_bstr) const; // Escapes characters in string attributes as required by the IA2 Spec. // It's okay for input to be the same as output. @@ -670,19 +672,19 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) // If the string attribute |attribute| is present, add its value as an // IAccessible2 attribute with the name |ia2_attr|. void StringAttributeToIA2(std::vector<base::string16>& attributes, - ui::AXStringAttribute attribute, + AXStringAttribute attribute, const char* ia2_attr); // If the bool attribute |attribute| is present, add its value as an // IAccessible2 attribute with the name |ia2_attr|. void BoolAttributeToIA2(std::vector<base::string16>& attributes, - ui::AXBoolAttribute attribute, + AXBoolAttribute attribute, const char* ia2_attr); // If the int attribute |attribute| is present, add its value as an // IAccessible2 attribute with the name |ia2_attr|. void IntAttributeToIA2(std::vector<base::string16>& attributes, - ui::AXIntAttribute attribute, + AXIntAttribute attribute, const char* ia2_attr); void AddAlertTarget(); @@ -695,8 +697,8 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) // value of offset and returns, otherwise offset remains unchanged. void HandleSpecialTextOffset(LONG* offset); - // Convert from a IA2TextBoundaryType to a ui::TextBoundaryType. - ui::TextBoundaryType IA2TextBoundaryToTextBoundary(IA2TextBoundaryType type); + // Convert from a IA2TextBoundaryType to a TextBoundaryType. + TextBoundaryType IA2TextBoundaryToTextBoundary(IA2TextBoundaryType type); // Search forwards (direction == 1) or backwards (direction == -1) // from the given offset until the given boundary is found, and @@ -704,7 +706,7 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) LONG FindBoundary(const base::string16& text, IA2TextBoundaryType ia2_boundary, LONG start_offset, - ui::TextBoundaryDirection direction); + TextBoundaryDirection direction); // Many MSAA methods take a var_id parameter indicating that the operation // should be performed on a particular child ID, rather than this object. @@ -722,7 +724,7 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) void AddRelation(const base::string16& relation_type, int target_id); void AddBidirectionalRelations(const base::string16& relation_type, const base::string16& reverse_relation_type, - ui::AXIntListAttribute attribute); + AXIntListAttribute attribute); void AddBidirectionalRelations(const base::string16& relation_type, const base::string16& reverse_relation_type, const std::vector<int32_t>& target_ids); @@ -736,8 +738,17 @@ class AX_EXPORT __declspec(uuid("26f5641a-246d-457b-a96d-07f3fae6acf2")) void RemoveTargetFromRelation(const base::string16& relation_type, int target_id); + // Helper method for returning selected indicies. It is expected that the + // caller ensures that the input has been validated. + HRESULT AllocateComArrayFromVector(std::vector<LONG>& results, + LONG max, + LONG** selected, + LONG* n_selected); + + bool IsAncestorComboBox(); + // Relationships between this node and other nodes. - std::vector<ui::AXPlatformNodeRelationWin*> relations_; + std::vector<AXPlatformNodeRelationWin*> relations_; }; } // 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 9fe0108e065..8fcf2ee718a 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win_unittest.cc @@ -148,14 +148,14 @@ class AXPlatformNodeWinTest : public testing::Test { void CheckIUnknownHasName(ScopedComPtr<IUnknown> unknown, const wchar_t* expected_name) { ScopedComPtr<IAccessible2> accessible = ToIAccessible2(unknown); - ASSERT_NE(nullptr, accessible); + ASSERT_NE(nullptr, accessible.Get()); ScopedBstr name; EXPECT_EQ(S_OK, accessible->get_accName(SELF, name.Receive())); EXPECT_STREQ(expected_name, name); } - void Build3X3Table() { + AXTreeUpdate Build3X3Table() { /* Build a table the looks like: @@ -170,7 +170,7 @@ class AXPlatformNodeWinTest : public testing::Test { AXNodeData table; table.id = 0; - table.role = ui::AX_ROLE_TABLE; + table.role = AX_ROLE_TABLE; table.AddIntAttribute(AX_ATTR_TABLE_ROW_COUNT, 3); table.AddIntAttribute(AX_ATTR_TABLE_COLUMN_COUNT, 3); @@ -190,23 +190,23 @@ class AXPlatformNodeWinTest : public testing::Test { // Table column header AXNodeData table_row_header; table_row_header.id = 50; - table_row_header.role = ui::AX_ROLE_ROW; + table_row_header.role = AX_ROLE_ROW; table_row_header.child_ids.push_back(51); table_row_header.child_ids.push_back(52); table_row_header.child_ids.push_back(53); AXNodeData table_column_header_1; table_column_header_1.id = 51; - table_column_header_1.role = ui::AX_ROLE_COLUMN_HEADER; + table_column_header_1.role = AX_ROLE_COLUMN_HEADER; AXNodeData table_column_header_2; table_column_header_2.id = 52; - table_column_header_2.role = ui::AX_ROLE_COLUMN_HEADER; + table_column_header_2.role = AX_ROLE_COLUMN_HEADER; table_column_header_2.AddStringAttribute(AX_ATTR_NAME, "column header 1"); AXNodeData table_column_header_3; table_column_header_3.id = 53; - table_column_header_3.role = ui::AX_ROLE_COLUMN_HEADER; + table_column_header_3.role = AX_ROLE_COLUMN_HEADER; // Either AX_ATTR_NAME -or- AX_ATTR_DESCRIPTION is acceptable for a // description table_column_header_3.AddStringAttribute(AX_ATTR_DESCRIPTION, @@ -215,72 +215,75 @@ class AXPlatformNodeWinTest : public testing::Test { // Row 1 AXNodeData table_row_1; table_row_1.id = 1; - table_row_1.role = ui::AX_ROLE_ROW; + table_row_1.role = AX_ROLE_ROW; table_row_1.child_ids.push_back(2); table_row_1.child_ids.push_back(3); table_row_1.child_ids.push_back(4); AXNodeData table_row_header_1; table_row_header_1.id = 2; - table_row_header_1.role = ui::AX_ROLE_ROW_HEADER; + table_row_header_1.role = AX_ROLE_ROW_HEADER; table_row_header_1.AddStringAttribute(AX_ATTR_NAME, "row header 1"); AXNodeData table_cell_1; table_cell_1.id = 3; - table_cell_1.role = ui::AX_ROLE_CELL; + table_cell_1.role = AX_ROLE_CELL; table_cell_1.AddStringAttribute(AX_ATTR_NAME, "1"); AXNodeData table_cell_2; table_cell_2.id = 4; - table_cell_2.role = ui::AX_ROLE_CELL; + table_cell_2.role = AX_ROLE_CELL; table_cell_2.AddStringAttribute(AX_ATTR_NAME, "2"); // Row 2 AXNodeData table_row_2; table_row_2.id = 10; - table_row_2.role = ui::AX_ROLE_ROW; + table_row_2.role = AX_ROLE_ROW; table_row_2.child_ids.push_back(11); table_row_2.child_ids.push_back(12); table_row_2.child_ids.push_back(13); AXNodeData table_row_header_2; table_row_header_2.id = 11; - table_row_header_2.role = ui::AX_ROLE_ROW_HEADER; + table_row_header_2.role = AX_ROLE_ROW_HEADER; // Either AX_ATTR_NAME -or- AX_ATTR_DESCRIPTION is acceptable for a // description table_row_header_2.AddStringAttribute(AX_ATTR_DESCRIPTION, "row header 2"); AXNodeData table_cell_3; table_cell_3.id = 12; - table_cell_3.role = ui::AX_ROLE_CELL; + table_cell_3.role = AX_ROLE_CELL; table_cell_3.AddStringAttribute(AX_ATTR_NAME, "3"); AXNodeData table_cell_4; table_cell_4.id = 13; - table_cell_4.role = ui::AX_ROLE_CELL; + table_cell_4.role = AX_ROLE_CELL; table_cell_4.AddStringAttribute(AX_ATTR_NAME, "4"); AXTreeUpdate update; update.root_id = table.id; - update.nodes.push_back(table); + // Some of the table testing code will index into |nodes| + // and change the state of the given node. If you reorder + // these, you're going to need to update the tests. + update.nodes.push_back(table); // 0 - update.nodes.push_back(table_row_header); - update.nodes.push_back(table_column_header_1); - update.nodes.push_back(table_column_header_2); - update.nodes.push_back(table_column_header_3); + update.nodes.push_back(table_row_header); // 1 + update.nodes.push_back(table_column_header_1); // 2 + update.nodes.push_back(table_column_header_2); // 3 + update.nodes.push_back(table_column_header_3); // 4 - update.nodes.push_back(table_row_1); - update.nodes.push_back(table_row_header_1); - update.nodes.push_back(table_cell_1); - update.nodes.push_back(table_cell_2); + update.nodes.push_back(table_row_1); // 5 + update.nodes.push_back(table_row_header_1); // 6 + update.nodes.push_back(table_cell_1); // 7 + update.nodes.push_back(table_cell_2); // 8 - update.nodes.push_back(table_row_2); - update.nodes.push_back(table_row_header_2); - update.nodes.push_back(table_cell_3); - update.nodes.push_back(table_cell_4); + update.nodes.push_back(table_row_2); // 9 + update.nodes.push_back(table_row_header_2); // 10 + update.nodes.push_back(table_cell_3); // 11 + update.nodes.push_back(table_cell_4); // 12 - Init(update); + return update; } ScopedComPtr<IAccessibleTableCell> GetCellInTable() { @@ -483,7 +486,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionOneSelected) { AXNodeData list_item_2; list_item_2.id = 2; list_item_2.role = AX_ROLE_LIST_BOX_OPTION; - list_item_2.state = 1 << ui::AX_STATE_SELECTED; + list_item_2.state = 1 << AX_STATE_SELECTED; list_item_2.AddStringAttribute(AX_ATTR_NAME, "Name2"); AXNodeData list_item_3; @@ -516,13 +519,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleSelectionMultipleSelected) { AXNodeData list_item_2; list_item_2.id = 2; list_item_2.role = AX_ROLE_LIST_BOX_OPTION; - list_item_2.state = 1 << ui::AX_STATE_SELECTED; + list_item_2.state = 1 << AX_STATE_SELECTED; list_item_2.AddStringAttribute(AX_ATTR_NAME, "Name2"); AXNodeData list_item_3; list_item_3.id = 3; list_item_3.role = AX_ROLE_LIST_BOX_OPTION; - list_item_3.state = 1 << ui::AX_STATE_SELECTED; + list_item_3.state = 1 << AX_STATE_SELECTED; list_item_3.AddStringAttribute(AX_ATTR_NAME, "Name3"); AXNodeData list_item_4; @@ -682,7 +685,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { ScopedComPtr<IDispatch> result; EXPECT_EQ(S_OK, root_iaccessible->get_accChild(SELF, result.GetAddressOf())); - EXPECT_EQ(result.Get(), root_iaccessible); + EXPECT_EQ(result.Get(), root_iaccessible.Get()); } { @@ -690,7 +693,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { ScopedVariant child1(1); EXPECT_EQ(S_OK, root_iaccessible->get_accChild(child1, result.GetAddressOf())); - EXPECT_EQ(result.Get(), button_iaccessible); + EXPECT_EQ(result.Get(), button_iaccessible.Get()); } { @@ -698,7 +701,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { ScopedVariant child2(2); EXPECT_EQ(S_OK, root_iaccessible->get_accChild(child2, result.GetAddressOf())); - EXPECT_EQ(result.Get(), checkbox_iaccessible); + EXPECT_EQ(result.Get(), checkbox_iaccessible.Get()); } { @@ -720,7 +723,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { ScopedVariant button_id_variant(button_unique_id); EXPECT_EQ(S_OK, root_iaccessible->get_accChild(button_id_variant, result.GetAddressOf())); - EXPECT_EQ(result.Get(), button_iaccessible); + EXPECT_EQ(result.Get(), button_iaccessible.Get()); } // We shouldn't be able to ask for the root node by its unique ID @@ -741,13 +744,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleChildAndParent) { { ScopedComPtr<IDispatch> result; EXPECT_EQ(S_OK, button_iaccessible->get_accParent(result.GetAddressOf())); - EXPECT_EQ(result.Get(), root_iaccessible); + EXPECT_EQ(result.Get(), root_iaccessible.Get()); } { ScopedComPtr<IDispatch> result; EXPECT_EQ(S_OK, checkbox_iaccessible->get_accParent(result.GetAddressOf())); - EXPECT_EQ(result.Get(), root_iaccessible); + EXPECT_EQ(result.Get(), root_iaccessible.Get()); } { @@ -887,8 +890,8 @@ TEST_F(AXPlatformNodeWinTest, TestAccNavigate) { TEST_F(AXPlatformNodeWinTest, TestIAccessible2SetSelection) { AXNodeData text_field_node; text_field_node.id = 1; - text_field_node.role = ui::AX_ROLE_TEXT_FIELD; - text_field_node.state = 1 << ui::AX_STATE_EDITABLE; + text_field_node.role = AX_ROLE_TEXT_FIELD; + text_field_node.state = 1 << AX_STATE_EDITABLE; text_field_node.SetValue("Hi"); Init(text_field_node); @@ -896,7 +899,7 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessible2SetSelection) { ToIAccessible2(GetRootIAccessible()); ScopedComPtr<IAccessibleText> text_field; ia2_text_field.CopyTo(text_field.GetAddressOf()); - ASSERT_NE(nullptr, text_field); + ASSERT_NE(nullptr, text_field.Get()); EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 0, 1)); EXPECT_HRESULT_SUCCEEDED(text_field->setSelection(0, 1, 0)); @@ -909,13 +912,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessible2SetSelection) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetAccessibilityAt) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); ScopedComPtr<IUnknown> cell_1; EXPECT_EQ(S_OK, result->get_accessibleAt(1, 1, cell_1.GetAddressOf())); @@ -936,13 +939,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetAccessibilityAt) { TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetAccessibilityAtOutOfBounds) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); { ScopedComPtr<IUnknown> cell; @@ -970,13 +973,13 @@ TEST_F(AXPlatformNodeWinTest, } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetChildIndex) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long id; EXPECT_EQ(S_OK, result->get_childIndex(0, 0, &id)); @@ -998,13 +1001,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetChildIndex) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetColumnDescription) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); { ScopedBstr name; @@ -1026,13 +1029,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetColumnDescription) { TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetColumnExtentAt) { // TODO(dougt) This table doesn't have any spanning cells. This test // tests get_columnExtentAt for (1) and an invalid input. - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long columns_spanned; EXPECT_EQ(S_OK, result->get_columnExtentAt(1, 1, &columns_spanned)); @@ -1042,13 +1045,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetColumnExtentAt) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetColumnIndex) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long index; EXPECT_EQ(S_OK, result->get_columnIndex(1, &index)); @@ -1058,13 +1061,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetColumnIndex) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNColumns) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long count; EXPECT_EQ(S_OK, result->get_nColumns(&count)); @@ -1072,13 +1075,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNColumns) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNRows) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long count; EXPECT_EQ(S_OK, result->get_nRows(&count)); @@ -1086,13 +1089,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNRows) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowDescription) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); { ScopedBstr name; @@ -1114,13 +1117,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowDescription) { TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowExtentAt) { // TODO(dougt) This table doesn't have any spanning cells. This test // tests get_rowExtentAt for (1) and an invalid input. - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long rows_spanned; EXPECT_EQ(S_OK, result->get_rowExtentAt(0, 1, &rows_spanned)); @@ -1130,13 +1133,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowExtentAt) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowIndex) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long index; EXPECT_EQ(S_OK, result->get_rowIndex(1, &index)); @@ -1146,13 +1149,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowIndex) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowColumnExtentsAtIndex) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); long row, column, row_extents, column_extents; boolean is_selected; @@ -1171,13 +1174,13 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetRowColumnExtentsAtIndex) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetCellAt) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); ScopedComPtr<IAccessibleTable2> result; root_obj.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); { ScopedComPtr<IUnknown> cell; @@ -1192,10 +1195,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetCellAt) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnExtent) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); long column_spanned; EXPECT_EQ(S_OK, cell->get_columnExtent(&column_spanned)); @@ -1203,10 +1206,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnExtent) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnHeaderCells) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); IUnknown** cell_accessibles; @@ -1217,10 +1220,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnHeaderCells) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnIndex) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); long index; EXPECT_EQ(S_OK, cell->get_columnIndex(&index)); @@ -1228,10 +1231,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetColumnIndex) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowExtent) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); long rows_spanned; EXPECT_EQ(S_OK, cell->get_rowExtent(&rows_spanned)); @@ -1239,10 +1242,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowExtent) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowHeaderCells) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); IUnknown** cell_accessibles; @@ -1255,10 +1258,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowHeaderCells) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowIndex) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); long index; EXPECT_EQ(S_OK, cell->get_rowIndex(&index)); @@ -1266,10 +1269,10 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowIndex) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowColumnExtent) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); long row, column, row_extents, column_extents; boolean is_selected; @@ -1282,17 +1285,17 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetRowColumnExtent) { } TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableCellGetTable) { - Build3X3Table(); + Init(Build3X3Table()); ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); - ASSERT_NE(nullptr, cell); + ASSERT_NE(nullptr, cell.Get()); ScopedComPtr<IUnknown> table; EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); ScopedComPtr<IAccessibleTable> result; table.CopyTo(result.GetAddressOf()); - ASSERT_NE(nullptr, result); + ASSERT_NE(nullptr, result.Get()); // Check to make sure that this is the right table by checking one cell. ScopedComPtr<IUnknown> cell_1; @@ -1304,22 +1307,22 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessible2GetNRelations) { // This is is a duplicated of // BrowserAccessibilityTest::TestIAccessible2Relations but without the // specific COM/BrowserAccessibility knowledge. - ui::AXNodeData root; + AXNodeData root; root.id = 1; - root.role = ui::AX_ROLE_ROOT_WEB_AREA; + root.role = AX_ROLE_ROOT_WEB_AREA; std::vector<int32_t> describedby_ids = {1, 2, 3}; - root.AddIntListAttribute(ui::AX_ATTR_DESCRIBEDBY_IDS, describedby_ids); + root.AddIntListAttribute(AX_ATTR_DESCRIBEDBY_IDS, describedby_ids); - ui::AXNodeData child1; + AXNodeData child1; child1.id = 2; - child1.role = ui::AX_ROLE_STATIC_TEXT; + child1.role = AX_ROLE_STATIC_TEXT; root.child_ids.push_back(2); - ui::AXNodeData child2; + AXNodeData child2; child2.id = 3; - child2.role = ui::AX_ROLE_STATIC_TEXT; + child2.role = AX_ROLE_STATIC_TEXT; root.child_ids.push_back(3); @@ -1413,4 +1416,674 @@ TEST_F(AXPlatformNodeWinTest, TestIAccessible2GetNRelations) { // TODO(dougt): Try adding one more relation. } +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedChildrenZero) { + Init(Build3X3Table()); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedChildren; + EXPECT_EQ(S_OK, result->get_nSelectedChildren(&selectedChildren)); + EXPECT_EQ(0, selectedChildren); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedChildrenOne) { + AXTreeUpdate update = Build3X3Table(); + + // 7 == table_cell_1 + update.nodes[7].state = 1 << AX_STATE_SELECTED; + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedChildren; + EXPECT_EQ(S_OK, result->get_nSelectedChildren(&selectedChildren)); + EXPECT_EQ(1, selectedChildren); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedChildrenMany) { + AXTreeUpdate update = Build3X3Table(); + + // 7 == table_cell_1 + // 8 == table_cell_2 + // 11 == table_cell_3 + // 12 == table_cell_4 + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedChildren; + EXPECT_EQ(S_OK, result->get_nSelectedChildren(&selectedChildren)); + EXPECT_EQ(4, selectedChildren); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedColumnsZero) { + Init(Build3X3Table()); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedColumns; + EXPECT_EQ(S_OK, result->get_nSelectedColumns(&selectedColumns)); + EXPECT_EQ(0, selectedColumns); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedColumnsOne) { + AXTreeUpdate update = Build3X3Table(); + + // 3 == table_column_header_2 + // 7 == table_cell_1 + // 11 == table_cell_3 + update.nodes[3].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedColumns; + EXPECT_EQ(S_OK, result->get_nSelectedColumns(&selectedColumns)); + EXPECT_EQ(1, selectedColumns); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedColumnsMany) { + AXTreeUpdate update = Build3X3Table(); + + // 3 == table_column_header_2 + // 7 == table_cell_1 + // 11 == table_cell_3 + update.nodes[3].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + + // 4 == table_column_header_3 + // 8 == table_cell_2 + // 12 == table_cell_4 + update.nodes[4].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedColumns; + EXPECT_EQ(S_OK, result->get_nSelectedColumns(&selectedColumns)); + EXPECT_EQ(2, selectedColumns); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedRowsZero) { + Init(Build3X3Table()); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedRows; + EXPECT_EQ(S_OK, result->get_nSelectedRows(&selectedRows)); + EXPECT_EQ(0, selectedRows); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedRowsOne) { + AXTreeUpdate update = Build3X3Table(); + + // 6 == table_row_header_1 + // 7 == table_cell_1 + // 8 == table_cell_2 + update.nodes[6].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedRows; + EXPECT_EQ(S_OK, result->get_nSelectedRows(&selectedRows)); + EXPECT_EQ(1, selectedRows); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetNSelectedRowsMany) { + AXTreeUpdate update = Build3X3Table(); + + // 6 == table_row_header_3 + // 7 == table_cell_1 + // 8 == table_cell_2 + update.nodes[6].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + + // 10 == table_row_header_3 + // 11 == table_cell_1 + // 12 == table_cell_2 + update.nodes[10].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG selectedRows; + EXPECT_EQ(S_OK, result->get_nSelectedRows(&selectedRows)); + EXPECT_EQ(2, selectedRows); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedChildren) { + AXTreeUpdate update = Build3X3Table(); + + // 7 == table_cell_1 + // 12 == table_cell_4 + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG max = 10; + LONG* indices; + LONG count; + EXPECT_EQ(S_OK, result->get_selectedChildren(max, &indices, &count)); + EXPECT_EQ(2, count); + EXPECT_EQ(4, indices[0]); + EXPECT_EQ(8, indices[1]); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedChildrenZeroMax) { + AXTreeUpdate update = Build3X3Table(); + + // 7 == table_cell_1 + // 12 == table_cell_4 + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + LONG* indices; + LONG count; + EXPECT_EQ(E_INVALIDARG, result->get_selectedChildren(0, &indices, &count)); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedColumnsZero) { + AXTreeUpdate update = Build3X3Table(); + + // 7 == table_cell_1 + // 11 == table_cell_3 + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + long max_columns = 10; + long* columns; + long n_columns; + EXPECT_EQ(S_OK, + result->get_selectedColumns(max_columns, &columns, &n_columns)); + EXPECT_EQ(0, n_columns); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedColumnsOne) { + AXTreeUpdate update = Build3X3Table(); + + // 3 == table_column_header_2 + // 7 == table_cell_1 + // 11 == table_cell_3 + update.nodes[3].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + long max_columns = 10; + long* columns; + long n_columns; + EXPECT_EQ(S_OK, + result->get_selectedColumns(max_columns, &columns, &n_columns)); + EXPECT_EQ(1, n_columns); + EXPECT_EQ(1, columns[0]); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedColumnsMany) { + AXTreeUpdate update = Build3X3Table(); + + // 3 == table_column_header_2 + // 7 == table_cell_1 + // 11 == table_cell_3 + update.nodes[3].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + + // 4 == table_column_header_3 + // 8 == table_cell_2 + // 12 == table_cell_4 + update.nodes[4].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + long max_columns = 10; + long* columns; + long n_columns; + EXPECT_EQ(S_OK, + result->get_selectedColumns(max_columns, &columns, &n_columns)); + EXPECT_EQ(2, n_columns); + EXPECT_EQ(1, columns[0]); + EXPECT_EQ(2, columns[1]); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedRowsZero) { + Init(Build3X3Table()); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + long max_rows = 10; + long* rows; + long n_rows; + EXPECT_EQ(S_OK, result->get_selectedRows(max_rows, &rows, &n_rows)); + EXPECT_EQ(0, n_rows); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedRowsOne) { + AXTreeUpdate update = Build3X3Table(); + + // 6 == table_row_header_1 + // 7 == table_cell_1 + // 8 == table_cell_2 + update.nodes[6].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + long max_rows = 10; + long* rows; + long n_rows; + EXPECT_EQ(S_OK, result->get_selectedRows(max_rows, &rows, &n_rows)); + EXPECT_EQ(1, n_rows); + EXPECT_EQ(1, rows[0]); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableGetSelectedRowsMany) { + AXTreeUpdate update = Build3X3Table(); + + // 6 == table_row_header_3 + // 7 == table_cell_1 + // 8 == table_cell_2 + update.nodes[6].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + + // 10 == table_row_header_3 + // 11 == table_cell_1 + // 12 == table_cell_2 + update.nodes[10].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + long max_rows = 10; + long* rows; + long n_rows; + EXPECT_EQ(S_OK, result->get_selectedRows(max_rows, &rows, &n_rows)); + EXPECT_EQ(2, n_rows); + EXPECT_EQ(1, rows[0]); + EXPECT_EQ(2, rows[1]); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsColumnSelected) { + AXTreeUpdate update = Build3X3Table(); + + // 3 == table_column_header_2 + // 7 == table_cell_1 + // 11 == table_cell_3 + update.nodes[3].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[11].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + boolean selected; + EXPECT_EQ(S_OK, result->get_isColumnSelected(0, &selected)); + EXPECT_FALSE(selected); + + EXPECT_EQ(S_OK, result->get_isColumnSelected(1, &selected)); + EXPECT_TRUE(selected); + + EXPECT_EQ(S_OK, result->get_isColumnSelected(2, &selected)); + EXPECT_FALSE(selected); + + EXPECT_EQ(S_FALSE, result->get_isColumnSelected(3, &selected)); + EXPECT_EQ(S_FALSE, result->get_isColumnSelected(-3, &selected)); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsRowSelected) { + AXTreeUpdate update = Build3X3Table(); + + // 6 == table_row_header_3 + // 7 == table_cell_1 + // 8 == table_cell_2 + update.nodes[6].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + boolean selected; + EXPECT_EQ(S_OK, result->get_isRowSelected(0, &selected)); + EXPECT_FALSE(selected); + + EXPECT_EQ(S_OK, result->get_isRowSelected(1, &selected)); + EXPECT_TRUE(selected); + + EXPECT_EQ(S_OK, result->get_isRowSelected(2, &selected)); + EXPECT_FALSE(selected); + + EXPECT_EQ(S_FALSE, result->get_isRowSelected(3, &selected)); + EXPECT_EQ(S_FALSE, result->get_isRowSelected(-3, &selected)); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTableIsSelected) { + AXTreeUpdate update = Build3X3Table(); + + // 6 == table_row_header_3 + // 7 == table_cell_1 + // 8 == table_cell_2 + update.nodes[6].state = 1 << AX_STATE_SELECTED; + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[8].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + boolean selected; + + EXPECT_EQ(S_OK, result->get_isSelected(0, 0, &selected)); + EXPECT_FALSE(selected); + + EXPECT_EQ(S_OK, result->get_isSelected(0, 1, &selected)); + EXPECT_FALSE(selected); + + EXPECT_EQ(S_OK, result->get_isSelected(0, 2, &selected)); + EXPECT_FALSE(selected); + + EXPECT_EQ(S_FALSE, result->get_isSelected(0, 4, &selected)); + + EXPECT_EQ(S_OK, result->get_isSelected(1, 0, &selected)); + EXPECT_TRUE(selected); + + EXPECT_EQ(S_OK, result->get_isSelected(1, 1, &selected)); + EXPECT_TRUE(selected); + + EXPECT_EQ(S_OK, result->get_isSelected(1, 2, &selected)); + EXPECT_TRUE(selected); + + EXPECT_EQ(S_FALSE, result->get_isSelected(1, 4, &selected)); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTable2GetSelectedChildrenZero) { + Init(Build3X3Table()); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable2> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + IUnknown** cell_accessibles; + LONG count; + EXPECT_EQ(S_OK, result->get_selectedCells(&cell_accessibles, &count)); + EXPECT_EQ(0, count); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessibleTable2GetSelectedChildren) { + AXTreeUpdate update = Build3X3Table(); + + // 7 == table_cell_1 + // 12 == table_cell_4 + update.nodes[7].state = 1 << AX_STATE_SELECTED; + update.nodes[12].state = 1 << AX_STATE_SELECTED; + + Init(update); + + ScopedComPtr<IAccessibleTableCell> cell = GetCellInTable(); + ASSERT_NE(nullptr, cell.Get()); + + ScopedComPtr<IUnknown> table; + EXPECT_EQ(S_OK, cell->get_table(table.GetAddressOf())); + + ScopedComPtr<IAccessibleTable2> result; + table.CopyTo(result.GetAddressOf()); + ASSERT_NE(nullptr, result.Get()); + + IUnknown** cell_accessibles; + LONG count; + EXPECT_EQ(S_OK, result->get_selectedCells(&cell_accessibles, &count)); + EXPECT_EQ(2, count); + + ScopedComPtr<IUnknown> table_cell_1(cell_accessibles[0]); + CheckIUnknownHasName(table_cell_1, L"1"); + + ScopedComPtr<IUnknown> table_cell_4(cell_accessibles[1]); + CheckIUnknownHasName(table_cell_4, L"4"); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessible2GetGroupPosition) { + AXNodeData root; + root.id = 1; + root.AddIntAttribute(AX_ATTR_HIERARCHICAL_LEVEL, 1); + root.AddIntAttribute(AX_ATTR_SET_SIZE, 1); + root.AddIntAttribute(AX_ATTR_POS_IN_SET, 1); + Init(root); + + ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); + ScopedComPtr<IAccessible2> iaccessible2 = ToIAccessible2(root_obj); + LONG level, similar, position; + EXPECT_EQ(S_OK, iaccessible2->get_groupPosition(&level, &similar, &position)); + EXPECT_EQ(1, level); + EXPECT_EQ(1, similar); + EXPECT_EQ(1, position); + + EXPECT_EQ(E_INVALIDARG, + iaccessible2->get_groupPosition(nullptr, nullptr, nullptr)); +} + +TEST_F(AXPlatformNodeWinTest, TestIAccessible2GetLocalizedExtendedRole) { + AXNodeData root; + root.id = 1; + root.AddStringAttribute(AX_ATTR_ROLE_DESCRIPTION, "extended role"); + Init(root); + + ScopedComPtr<IAccessible> root_obj(GetRootIAccessible()); + ScopedComPtr<IAccessible2> iaccessible2 = ToIAccessible2(root_obj); + ScopedBstr role; + EXPECT_EQ(S_OK, iaccessible2->get_localizedExtendedRole(role.Receive())); + EXPECT_STREQ(L"extended role", role); +} + } // namespace ui 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 1d62fefc5ef..4d0bfe41d93 100644 --- a/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.cc +++ b/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.cc @@ -23,7 +23,7 @@ namespace { bool HasFocusableChild(const AXNode* node) { for (auto* child : node->children()) { - if ((child->data().state & ui::AX_STATE_FOCUSABLE) != 0 || + if (child->data().HasState(AX_STATE_FOCUSABLE) || HasFocusableChild(child)) { return true; } @@ -40,13 +40,13 @@ bool HasOnlyTextChildren(const AXNode* node) { } // TODO(muyuanli): share with BrowserAccessibility. -bool IsSimpleTextControl(AXRole role, uint32_t state) { - switch (role) { - case ui::AX_ROLE_COMBO_BOX: - case ui::AX_ROLE_SEARCH_BOX: +bool IsSimpleTextControl(const AXNode* node, uint32_t state) { + switch (node->data().role) { + case AX_ROLE_COMBO_BOX: + case AX_ROLE_SEARCH_BOX: return true; - case ui::AX_ROLE_TEXT_FIELD: - return (state & ui::AX_STATE_RICHLY_EDITABLE) == 0; + case AX_ROLE_TEXT_FIELD: + return !node->data().HasState(AX_STATE_RICHLY_EDITABLE); default: return false; } @@ -54,14 +54,13 @@ bool IsSimpleTextControl(AXRole role, uint32_t state) { bool IsRichTextEditable(const AXNode* node) { const AXNode* parent = node->parent(); - return (node->data().state & ui::AX_STATE_RICHLY_EDITABLE) != 0 && - (!parent || - (parent->data().state & ui::AX_STATE_RICHLY_EDITABLE) == 0); + return node->data().HasState(AX_STATE_RICHLY_EDITABLE) && + (!parent || !parent->data().HasState(AX_STATE_RICHLY_EDITABLE)); } bool IsNativeTextControl(const AXNode* node) { const std::string& html_tag = - node->data().GetStringAttribute(ui::AX_ATTR_HTML_TAG); + node->data().GetStringAttribute(AX_ATTR_HTML_TAG); if (html_tag == "input") { std::string input_type; if (!node->data().GetHtmlAttribute("type", &input_type)) @@ -83,15 +82,15 @@ bool IsLeaf(const AXNode* node) { } switch (node->data().role) { - case ui::AX_ROLE_IMAGE: - case ui::AX_ROLE_METER: - case ui::AX_ROLE_SCROLL_BAR: - case ui::AX_ROLE_SLIDER: - case ui::AX_ROLE_SPLITTER: - case ui::AX_ROLE_PROGRESS_INDICATOR: - case ui::AX_ROLE_DATE: - case ui::AX_ROLE_DATE_TIME: - case ui::AX_ROLE_INPUT_TIME: + case AX_ROLE_IMAGE: + case AX_ROLE_METER: + case AX_ROLE_SCROLL_BAR: + case AX_ROLE_SLIDER: + case AX_ROLE_SPLITTER: + case AX_ROLE_PROGRESS_INDICATOR: + case AX_ROLE_DATE: + case AX_ROLE_DATE_TIME: + case AX_ROLE_INPUT_TIME: return true; default: return false; @@ -100,7 +99,7 @@ bool IsLeaf(const AXNode* node) { base::string16 GetInnerText(const AXNode* node) { if (node->IsTextNode()) { - return node->data().GetString16Attribute(ui::AX_ATTR_NAME); + return node->data().GetString16Attribute(AX_ATTR_NAME); } base::string16 text; for (auto* child : node->children()) { @@ -110,16 +109,16 @@ base::string16 GetInnerText(const AXNode* node) { } base::string16 GetValue(const AXNode* node, bool show_password) { - base::string16 value = node->data().GetString16Attribute(ui::AX_ATTR_VALUE); + base::string16 value = node->data().GetString16Attribute(AX_ATTR_VALUE); if (value.empty() && - (IsSimpleTextControl(node->data().role, node->data().state) || + (IsSimpleTextControl(node, node->data().state) || IsRichTextEditable(node)) && !IsNativeTextControl(node)) { value = GetInnerText(node); } - if ((node->data().state & ui::AX_STATE_PROTECTED) != 0) { + if (node->data().HasState(AX_STATE_PROTECTED)) { if (!show_password) { value = base::string16(value.size(), kSecurePasswordBullet); } @@ -130,8 +129,8 @@ base::string16 GetValue(const AXNode* node, bool show_password) { bool HasOnlyTextAndImageChildren(const AXNode* node) { for (auto* child : node->children()) { - if (child->data().role != ui::AX_ROLE_STATIC_TEXT && - child->data().role != ui::AX_ROLE_IMAGE) { + if (child->data().role != AX_ROLE_STATIC_TEXT && + child->data().role != AX_ROLE_IMAGE) { return false; } } @@ -139,24 +138,24 @@ bool HasOnlyTextAndImageChildren(const AXNode* node) { } bool IsFocusable(const AXNode* node) { - if (node->data().role == ui::AX_ROLE_IFRAME || - node->data().role == ui::AX_ROLE_IFRAME_PRESENTATIONAL || - (node->data().role == ui::AX_ROLE_ROOT_WEB_AREA && node->parent())) { - return node->data().HasStringAttribute(ui::AX_ATTR_NAME); + if (node->data().role == AX_ROLE_IFRAME || + node->data().role == AX_ROLE_IFRAME_PRESENTATIONAL || + (node->data().role == AX_ROLE_ROOT_WEB_AREA && node->parent())) { + return node->data().HasStringAttribute(AX_ATTR_NAME); } - return (node->data().state & ui::AX_STATE_FOCUSABLE) != 0; + return node->data().HasState(AX_STATE_FOCUSABLE); } base::string16 GetText(const AXNode* node, bool show_password) { - if (node->data().role == ui::AX_ROLE_WEB_AREA || - node->data().role == ui::AX_ROLE_IFRAME || - node->data().role == ui::AX_ROLE_IFRAME_PRESENTATIONAL) { + if (node->data().role == AX_ROLE_WEB_AREA || + node->data().role == AX_ROLE_IFRAME || + node->data().role == AX_ROLE_IFRAME_PRESENTATIONAL) { return base::string16(); } - if (node->data().role == ui::AX_ROLE_LIST_ITEM && - node->data().GetIntAttribute(ui::AX_ATTR_NAME_FROM) == - ui::AX_NAME_FROM_CONTENTS) { + if (node->data().role == AX_ROLE_LIST_ITEM && + node->data().GetIntAttribute(AX_ATTR_NAME_FROM) == + AX_NAME_FROM_CONTENTS) { if (node->child_count() > 0 && !HasOnlyTextChildren(node)) return base::string16(); } @@ -164,22 +163,22 @@ base::string16 GetText(const AXNode* node, bool show_password) { base::string16 value = GetValue(node, show_password); if (!value.empty()) { - if ((node->data().state & ui::AX_STATE_EDITABLE) != 0) + if (node->data().HasState(AX_STATE_EDITABLE)) return value; switch (node->data().role) { - case ui::AX_ROLE_COMBO_BOX: - case ui::AX_ROLE_POP_UP_BUTTON: - case ui::AX_ROLE_TEXT_FIELD: + case AX_ROLE_COMBO_BOX: + case AX_ROLE_POP_UP_BUTTON: + case AX_ROLE_TEXT_FIELD: return value; default: break; } } - if (node->data().role == ui::AX_ROLE_COLOR_WELL) { + if (node->data().role == AX_ROLE_COLOR_WELL) { unsigned int color = static_cast<unsigned int>( - node->data().GetIntAttribute(ui::AX_ATTR_COLOR_VALUE)); + node->data().GetIntAttribute(AX_ATTR_COLOR_VALUE)); unsigned int red = color >> 16 & 0xFF; unsigned int green = color >> 8 & 0xFF; unsigned int blue = color >> 0 & 0xFF; @@ -187,9 +186,9 @@ base::string16 GetText(const AXNode* node, bool show_password) { base::StringPrintf("#%02X%02X%02X", red, green, blue)); } - base::string16 text = node->data().GetString16Attribute(ui::AX_ATTR_NAME); + base::string16 text = node->data().GetString16Attribute(AX_ATTR_NAME); base::string16 description = - node->data().GetString16Attribute(ui::AX_ATTR_DESCRIPTION); + node->data().GetString16Attribute(AX_ATTR_DESCRIPTION); if (!description.empty()) { if (!text.empty()) text += base::ASCIIToUTF16(" "); @@ -199,7 +198,7 @@ base::string16 GetText(const AXNode* node, bool show_password) { if (text.empty()) text = value; - if (node->data().role == ui::AX_ROLE_ROOT_WEB_AREA) + if (node->data().role == AX_ROLE_ROOT_WEB_AREA) return text; if (text.empty() && @@ -211,8 +210,8 @@ base::string16 GetText(const AXNode* node, bool show_password) { } if (text.empty() && (AXSnapshotNodeAndroid::AXRoleIsLink(node->data().role) || - node->data().role == ui::AX_ROLE_IMAGE)) { - base::string16 url = node->data().GetString16Attribute(ui::AX_ATTR_URL); + node->data().role == AX_ROLE_IMAGE)) { + base::string16 url = node->data().GetString16Attribute(AX_ATTR_URL); text = AXSnapshotNodeAndroid::AXUrlBaseText(url); } return text; @@ -271,7 +270,7 @@ AX_EXPORT AXSnapshotNodeAndroid::~AXSnapshotNodeAndroid() = default; AX_EXPORT std::unique_ptr<AXSnapshotNodeAndroid> AXSnapshotNodeAndroid::Create( const AXTreeUpdate& update, bool show_password) { - auto tree = base::MakeUnique<ui::AXSerializableTree>(); + auto tree = base::MakeUnique<AXSerializableTree>(); if (!tree->Unserialize(update)) { LOG(FATAL) << tree->error(); } @@ -286,7 +285,7 @@ AX_EXPORT std::unique_ptr<AXSnapshotNodeAndroid> AXSnapshotNodeAndroid::Create( // static AX_EXPORT bool AXSnapshotNodeAndroid::AXRoleIsLink(AXRole role) { - return role == ui::AX_ROLE_LINK || role == ui::AX_ROLE_IMAGE_MAP_LINK; + return role == AX_ROLE_LINK; } // static @@ -315,55 +314,55 @@ AX_EXPORT const char* AXSnapshotNodeAndroid::AXRoleToAndroidClassName( AXRole role, bool has_parent) { switch (role) { - case ui::AX_ROLE_SEARCH_BOX: - case ui::AX_ROLE_SPIN_BUTTON: - case ui::AX_ROLE_TEXT_FIELD: - return ui::kAXEditTextClassname; - case ui::AX_ROLE_SLIDER: - return ui::kAXSeekBarClassname; - case ui::AX_ROLE_COLOR_WELL: - case ui::AX_ROLE_COMBO_BOX: - case ui::AX_ROLE_DATE: - case ui::AX_ROLE_POP_UP_BUTTON: - case ui::AX_ROLE_INPUT_TIME: - return ui::kAXSpinnerClassname; - case ui::AX_ROLE_BUTTON: - case ui::AX_ROLE_MENU_BUTTON: - return ui::kAXButtonClassname; - case ui::AX_ROLE_CHECK_BOX: - case ui::AX_ROLE_SWITCH: - return ui::kAXCheckBoxClassname; - case ui::AX_ROLE_RADIO_BUTTON: - return ui::kAXRadioButtonClassname; - case ui::AX_ROLE_TOGGLE_BUTTON: - return ui::kAXToggleButtonClassname; - case ui::AX_ROLE_CANVAS: - case ui::AX_ROLE_IMAGE: - case ui::AX_ROLE_SVG_ROOT: - return ui::kAXImageClassname; - case ui::AX_ROLE_METER: - case ui::AX_ROLE_PROGRESS_INDICATOR: - return ui::kAXProgressBarClassname; - case ui::AX_ROLE_TAB_LIST: - return ui::kAXTabWidgetClassname; - case ui::AX_ROLE_GRID: - case ui::AX_ROLE_TREE_GRID: - case ui::AX_ROLE_TABLE: - return ui::kAXGridViewClassname; - case ui::AX_ROLE_LIST: - case ui::AX_ROLE_LIST_BOX: - case ui::AX_ROLE_DESCRIPTION_LIST: - return ui::kAXListViewClassname; - case ui::AX_ROLE_DIALOG: - return ui::kAXDialogClassname; - case ui::AX_ROLE_ROOT_WEB_AREA: - return has_parent ? ui::kAXViewClassname : ui::kAXWebViewClassname; - case ui::AX_ROLE_MENU_ITEM: - case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: - case ui::AX_ROLE_MENU_ITEM_RADIO: - return ui::kAXMenuItemClassname; + case AX_ROLE_SEARCH_BOX: + case AX_ROLE_SPIN_BUTTON: + case AX_ROLE_TEXT_FIELD: + return kAXEditTextClassname; + case AX_ROLE_SLIDER: + return kAXSeekBarClassname; + case AX_ROLE_COLOR_WELL: + case AX_ROLE_COMBO_BOX: + case AX_ROLE_DATE: + case AX_ROLE_POP_UP_BUTTON: + case AX_ROLE_INPUT_TIME: + return kAXSpinnerClassname; + case AX_ROLE_BUTTON: + case AX_ROLE_MENU_BUTTON: + return kAXButtonClassname; + case AX_ROLE_CHECK_BOX: + case AX_ROLE_SWITCH: + return kAXCheckBoxClassname; + case AX_ROLE_RADIO_BUTTON: + return kAXRadioButtonClassname; + case AX_ROLE_TOGGLE_BUTTON: + return kAXToggleButtonClassname; + case AX_ROLE_CANVAS: + case AX_ROLE_IMAGE: + case AX_ROLE_SVG_ROOT: + return kAXImageClassname; + case AX_ROLE_METER: + case AX_ROLE_PROGRESS_INDICATOR: + return kAXProgressBarClassname; + case AX_ROLE_TAB_LIST: + return kAXTabWidgetClassname; + case AX_ROLE_GRID: + case AX_ROLE_TREE_GRID: + case AX_ROLE_TABLE: + return kAXGridViewClassname; + case AX_ROLE_LIST: + case AX_ROLE_LIST_BOX: + case AX_ROLE_DESCRIPTION_LIST: + return kAXListViewClassname; + case AX_ROLE_DIALOG: + return kAXDialogClassname; + case AX_ROLE_ROOT_WEB_AREA: + return has_parent ? kAXViewClassname : kAXWebViewClassname; + case AX_ROLE_MENU_ITEM: + case AX_ROLE_MENU_ITEM_CHECK_BOX: + case AX_ROLE_MENU_ITEM_RADIO: + return kAXMenuItemClassname; default: - return ui::kAXViewClassname; + return kAXViewClassname; } } @@ -372,7 +371,7 @@ std::unique_ptr<AXSnapshotNodeAndroid> AXSnapshotNodeAndroid::WalkAXTreeDepthFirst( const AXNode* node, gfx::Rect rect, - const ui::AXTreeUpdate& update, + const AXTreeUpdate& update, const AXTree* tree, AXSnapshotNodeAndroid::WalkAXTreeConfig& config) { auto result = @@ -390,21 +389,20 @@ AXSnapshotNodeAndroid::WalkAXTreeDepthFirst( result->line_through = 0; result->underline = 0; - if (node->data().HasFloatAttribute(ui::AX_ATTR_FONT_SIZE)) { + if (node->data().HasFloatAttribute(AX_ATTR_FONT_SIZE)) { gfx::RectF text_size_rect( - 0, 0, 1, node->data().GetFloatAttribute(ui::AX_ATTR_FONT_SIZE)); + 0, 0, 1, node->data().GetFloatAttribute(AX_ATTR_FONT_SIZE)); gfx::Rect scaled_text_size_rect = gfx::ToEnclosingRect(tree->RelativeToTreeBounds(node, text_size_rect)); result->text_size = scaled_text_size_rect.height(); - const int text_style = node->data().GetIntAttribute(ui::AX_ATTR_TEXT_STYLE); - result->color = node->data().GetIntAttribute(ui::AX_ATTR_COLOR); - result->bgcolor = - node->data().GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR); - result->bold = (text_style & ui::AX_TEXT_STYLE_BOLD) != 0; - result->italic = (text_style & ui::AX_TEXT_STYLE_ITALIC) != 0; - result->line_through = (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) != 0; - result->underline = (text_style & ui::AX_TEXT_STYLE_UNDERLINE) != 0; + const int text_style = node->data().GetIntAttribute(AX_ATTR_TEXT_STYLE); + result->color = node->data().GetIntAttribute(AX_ATTR_COLOR); + result->bgcolor = node->data().GetIntAttribute(AX_ATTR_BACKGROUND_COLOR); + result->bold = (text_style & AX_TEXT_STYLE_BOLD) != 0; + result->italic = (text_style & AX_TEXT_STYLE_ITALIC) != 0; + result->line_through = (text_style & AX_TEXT_STYLE_LINE_THROUGH) != 0; + result->underline = (text_style & AX_TEXT_STYLE_UNDERLINE) != 0; } const gfx::Rect& absolute_rect = diff --git a/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.h b/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.h index 7314a2e0e03..be01b51bcee 100644 --- a/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.h +++ b/chromium/ui/accessibility/platform/ax_snapshot_node_android_platform.h @@ -71,7 +71,7 @@ struct AXSnapshotNodeAndroid { static std::unique_ptr<AXSnapshotNodeAndroid> WalkAXTreeDepthFirst( const AXNode* node, gfx::Rect rect, - const ui::AXTreeUpdate& update, + const AXTreeUpdate& update, const AXTree* tree, WalkAXTreeConfig& config); diff --git a/chromium/ui/accessibility/platform/ax_system_caret_win.cc b/chromium/ui/accessibility/platform/ax_system_caret_win.cc index d09bc04b8f7..b4a1d38c872 100644 --- a/chromium/ui/accessibility/platform/ax_system_caret_win.cc +++ b/chromium/ui/accessibility/platform/ax_system_caret_win.cc @@ -55,8 +55,8 @@ const AXNodeData& AXSystemCaretWin::GetData() const { return data_; } -const ui::AXTreeData& AXSystemCaretWin::GetTreeData() const { - CR_DEFINE_STATIC_LOCAL(ui::AXTreeData, empty_data, ()); +const AXTreeData& AXSystemCaretWin::GetTreeData() const { + CR_DEFINE_STATIC_LOCAL(AXTreeData, empty_data, ()); return empty_data; } @@ -103,8 +103,7 @@ AXSystemCaretWin::GetTargetForNativeAccessibilityEvent() { return event_target_; } -bool AXSystemCaretWin::AccessibilityPerformAction( - const ui::AXActionData& data) { +bool AXSystemCaretWin::AccessibilityPerformAction(const AXActionData& data) { return false; } diff --git a/chromium/ui/accessibility/platform/ax_system_caret_win.h b/chromium/ui/accessibility/platform/ax_system_caret_win.h index ec093e48e29..6c2d51dfdf8 100644 --- a/chromium/ui/accessibility/platform/ax_system_caret_win.h +++ b/chromium/ui/accessibility/platform/ax_system_caret_win.h @@ -35,7 +35,7 @@ class AX_EXPORT AXSystemCaretWin : private AXPlatformNodeDelegate { private: // |AXPlatformNodeDelegate| members. const AXNodeData& GetData() const override; - const ui::AXTreeData& GetTreeData() const override; + const AXTreeData& GetTreeData() const override; gfx::NativeWindow GetTopLevelWidget() override; gfx::NativeViewAccessible GetParent() override; int GetChildCount() override; @@ -43,9 +43,9 @@ class AX_EXPORT AXSystemCaretWin : private AXPlatformNodeDelegate { gfx::Rect GetScreenBoundsRect() const override; gfx::NativeViewAccessible HitTestSync(int x, int y) override; gfx::NativeViewAccessible GetFocus() override; - ui::AXPlatformNode* GetFromNodeID(int32_t id) override; + AXPlatformNode* GetFromNodeID(int32_t id) override; gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override; - bool AccessibilityPerformAction(const ui::AXActionData& data) override; + bool AccessibilityPerformAction(const AXActionData& data) override; bool ShouldIgnoreHoveredStateForTesting() override; AXPlatformNodeWin* caret_; diff --git a/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc b/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc index e0b153ef512..bfb7b4f1d99 100644 --- a/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc +++ b/chromium/ui/accessibility/platform/test_ax_node_wrapper.cc @@ -24,8 +24,8 @@ class TestAXTreeDelegate : public AXTreeDelegate { const AXNodeData& old_node_data, const AXNodeData& new_node_data) override {} void OnTreeDataChanged(AXTree* tree, - const ui::AXTreeData& old_data, - const ui::AXTreeData& new_data) override {} + const AXTreeData& old_data, + const AXTreeData& new_data) override {} void OnNodeWillBeDeleted(AXTree* tree, AXNode* node) override { auto iter = g_node_to_wrapper_map.find(node); if (iter != g_node_to_wrapper_map.end()) { @@ -76,7 +76,7 @@ const AXNodeData& TestAXNodeWrapper::GetData() const { return node_->data(); } -const ui::AXTreeData& TestAXNodeWrapper::GetTreeData() const { +const AXTreeData& TestAXNodeWrapper::GetTreeData() const { return tree_->data(); } @@ -171,8 +171,7 @@ TestAXNodeWrapper::GetTargetForNativeAccessibilityEvent() { return gfx::kNullAcceleratedWidget; } -bool TestAXNodeWrapper::AccessibilityPerformAction( - const ui::AXActionData& data) { +bool TestAXNodeWrapper::AccessibilityPerformAction(const AXActionData& data) { return true; } diff --git a/chromium/ui/accessibility/platform/test_ax_node_wrapper.h b/chromium/ui/accessibility/platform/test_ax_node_wrapper.h index 9ce258e31ed..a65b0c05281 100644 --- a/chromium/ui/accessibility/platform/test_ax_node_wrapper.h +++ b/chromium/ui/accessibility/platform/test_ax_node_wrapper.h @@ -32,7 +32,7 @@ class TestAXNodeWrapper : public AXPlatformNodeDelegate { // AXPlatformNodeDelegate. const AXNodeData& GetData() const override; - const ui::AXTreeData& GetTreeData() const override; + const AXTreeData& GetTreeData() const override; gfx::NativeWindow GetTopLevelWidget() override; gfx::NativeViewAccessible GetParent() override; int GetChildCount() override; @@ -40,9 +40,9 @@ class TestAXNodeWrapper : public AXPlatformNodeDelegate { gfx::Rect GetScreenBoundsRect() const override; gfx::NativeViewAccessible HitTestSync(int x, int y) override; gfx::NativeViewAccessible GetFocus() override; - ui::AXPlatformNode* GetFromNodeID(int32_t id) override; + AXPlatformNode* GetFromNodeID(int32_t id) override; gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override; - bool AccessibilityPerformAction(const ui::AXActionData& data) override; + bool AccessibilityPerformAction(const AXActionData& data) override; bool ShouldIgnoreHoveredStateForTesting() override; private: diff --git a/chromium/ui/android/BUILD.gn b/chromium/ui/android/BUILD.gn index df557616dcc..cbf034ca945 100644 --- a/chromium/ui/android/BUILD.gn +++ b/chromium/ui/android/BUILD.gn @@ -34,17 +34,15 @@ component("android") { "resources/resource.cc", "resources/resource.h", "resources/resource_factory.cc", - "resources/resource_factory.h", "resources/resource_manager.h", "resources/resource_manager_impl.cc", "resources/resource_manager_impl.h", "resources/ui_resource_provider.h", "screen_android.h", "ui_android_export.h", - "ui_android_jni_registrar.cc", - "ui_android_jni_registrar.h", "view_android.cc", "view_android.h", + "view_android_observer.h", "view_client.cc", "view_client.h", "window_android.cc", @@ -60,11 +58,11 @@ component("android") { ":ui_android_jni_headers", "//base", "//cc", - "//cc/surfaces", "//components/viz/common", "//components/viz/host", "//components/viz/service", "//skia", + "//third_party/WebKit/public:blink_headers", "//ui/base", "//ui/display", "//ui/events", @@ -210,6 +208,7 @@ android_library("ui_full_java") { "java/src/org/chromium/ui/base/SPenSupport.java", "java/src/org/chromium/ui/base/TouchDevice.java", "java/src/org/chromium/ui/base/ViewAndroidDelegate.java", + "java/src/org/chromium/ui/base/ViewUtils.java", "java/src/org/chromium/ui/base/WindowAndroid.java", "java/src/org/chromium/ui/display/DisplayAndroid.java", "java/src/org/chromium/ui/display/DisplayAndroidManager.java", @@ -251,7 +250,25 @@ android_library("ui_full_java") { "//third_party/android_tools:android_support_annotations_java", "//third_party/android_tools:android_support_v7_appcompat_java", ] - srcjar_deps = [ ":java_enums_srcjar" ] + srcjar_deps = [ + ":java_enums_srcjar", + "//third_party/WebKit/public:blink_cursor_type_java_enums_srcjar", + ] +} + +android_library("ui_java_test_support") { + testonly = true + java_files = [ + "javatests/src/org/chromium/ui/test/util/UiDisableIf.java", + "javatests/src/org/chromium/ui/test/util/UiDisableIfSkipCheck.java", + "javatests/src/org/chromium/ui/test/util/UiRestriction.java", + "javatests/src/org/chromium/ui/test/util/UiRestrictionSkipCheck.java", + ] + deps = [ + ":ui_java", + "//base:base_java", + "//base:base_java_test_support", + ] } junit_binary("ui_junit_tests") { diff --git a/chromium/ui/android/DEPS b/chromium/ui/android/DEPS index 410819d2634..54c50b66600 100644 --- a/chromium/ui/android/DEPS +++ b/chromium/ui/android/DEPS @@ -3,15 +3,16 @@ include_rules = [ "+cc/output", "+cc/resources", "+cc/scheduler/begin_frame_source.h", - "+cc/surfaces", "+cc/test/stub_layer_tree_host_client.h", "+cc/test/test_task_graph_runner.h", "+cc/trees/layer_tree_host.h", "+components/viz/common", "+components/viz/host", "+components/viz/service/frame_sinks", + "+components/viz/service/surfaces", "+jni", "+skia/ext", + "+third_party/WebKit/public/platform/WebCursorInfo.h", "+third_party/skia", "+ui/base", "+ui/display", diff --git a/chromium/ui/android/OWNERS b/chromium/ui/android/OWNERS index dd1d5e5064c..8dd68c8015f 100644 --- a/chromium/ui/android/OWNERS +++ b/chromium/ui/android/OWNERS @@ -10,3 +10,4 @@ boliu@chromium.org jinsukkim@chromium.org # COMPONENT: UI +# OS: Android diff --git a/chromium/ui/android/delegated_frame_host_android.cc b/chromium/ui/android/delegated_frame_host_android.cc index 847f15a12e8..7cc00f5422e 100644 --- a/chromium/ui/android/delegated_frame_host_android.cc +++ b/chromium/ui/android/delegated_frame_host_android.cc @@ -10,11 +10,11 @@ #include "cc/layers/solid_color_layer.h" #include "cc/layers/surface_layer.h" #include "cc/output/compositor_frame.h" -#include "cc/output/copy_output_result.h" -#include "cc/surfaces/surface.h" +#include "components/viz/common/quads/copy_output_result.h" #include "components/viz/common/surfaces/surface_id.h" #include "components/viz/host/host_frame_sink_manager.h" #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" +#include "components/viz/service/surfaces/surface.h" #include "ui/android/view_android.h" #include "ui/android/window_android_compositor.h" #include "ui/display/display.h" @@ -26,7 +26,7 @@ namespace ui { namespace { scoped_refptr<cc::SurfaceLayer> CreateSurfaceLayer( - cc::SurfaceManager* surface_manager, + viz::SurfaceManager* surface_manager, viz::SurfaceInfo surface_info, bool surface_opaque) { // manager must outlive compositors using it. @@ -42,8 +42,8 @@ scoped_refptr<cc::SurfaceLayer> CreateSurfaceLayer( void CopyOutputRequestCallback( scoped_refptr<cc::Layer> readback_layer, - cc::CopyOutputRequest::CopyOutputRequestCallback result_callback, - std::unique_ptr<cc::CopyOutputResult> copy_output_result) { + viz::CopyOutputRequest::CopyOutputRequestCallback result_callback, + std::unique_ptr<viz::CopyOutputResult> copy_output_result) { readback_layer->RemoveFromParent(); std::move(result_callback).Run(std::move(copy_output_result)); } @@ -65,7 +65,7 @@ DelegatedFrameHostAndroid::DelegatedFrameHostAndroid( DCHECK(view_); DCHECK(client_); - frame_sink_manager_->surface_manager()->RegisterFrameSinkId(frame_sink_id_); + host_frame_sink_manager_->RegisterFrameSinkId(frame_sink_id_, this); CreateNewCompositorFrameSinkSupport(); } @@ -73,7 +73,7 @@ DelegatedFrameHostAndroid::~DelegatedFrameHostAndroid() { DestroyDelegatedContent(); DetachFromCompositor(); support_.reset(); - frame_sink_manager_->surface_manager()->InvalidateFrameSinkId(frame_sink_id_); + host_frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_); } void DelegatedFrameHostAndroid::SubmitCompositorFrame( @@ -103,7 +103,7 @@ void DelegatedFrameHostAndroid::SubmitCompositorFrame( } void DelegatedFrameHostAndroid::DidNotProduceFrame( - const cc::BeginFrameAck& ack) { + const viz::BeginFrameAck& ack) { support_->DidNotProduceFrame(ack); } @@ -114,7 +114,7 @@ viz::FrameSinkId DelegatedFrameHostAndroid::GetFrameSinkId() const { void DelegatedFrameHostAndroid::RequestCopyOfSurface( WindowAndroidCompositor* compositor, const gfx::Rect& src_subrect_in_pixel, - cc::CopyOutputRequest::CopyOutputRequestCallback result_callback) { + viz::CopyOutputRequest::CopyOutputRequestCallback result_callback) { DCHECK(surface_info_.is_valid()); DCHECK(!result_callback.is_null()); @@ -123,8 +123,8 @@ void DelegatedFrameHostAndroid::RequestCopyOfSurface( !has_transparent_background_); readback_layer->SetHideLayerAndSubtree(true); compositor->AttachLayerForReadback(readback_layer); - std::unique_ptr<cc::CopyOutputRequest> copy_output_request = - cc::CopyOutputRequest::CreateRequest( + std::unique_ptr<viz::CopyOutputRequest> copy_output_request = + viz::CopyOutputRequest::CreateRequest( base::BindOnce(&CopyOutputRequestCallback, readback_layer, std::move(result_callback))); @@ -176,17 +176,17 @@ void DelegatedFrameHostAndroid::DetachFromCompositor() { } void DelegatedFrameHostAndroid::DidReceiveCompositorFrameAck( - const std::vector<cc::ReturnedResource>& resources) { + const std::vector<viz::ReturnedResource>& resources) { client_->ReclaimResources(resources); client_->DidReceiveCompositorFrameAck(); } -void DelegatedFrameHostAndroid::OnBeginFrame(const cc::BeginFrameArgs& args) { +void DelegatedFrameHostAndroid::OnBeginFrame(const viz::BeginFrameArgs& args) { begin_frame_source_.OnBeginFrame(args); } void DelegatedFrameHostAndroid::ReclaimResources( - const std::vector<cc::ReturnedResource>& resources) { + const std::vector<viz::ReturnedResource>& resources) { client_->ReclaimResources(resources); } @@ -202,14 +202,18 @@ void DelegatedFrameHostAndroid::OnNeedsBeginFrames(bool needs_begin_frames) { support_->SetNeedsBeginFrame(needs_begin_frames); } +void DelegatedFrameHostAndroid::OnFirstSurfaceActivation( + const viz::SurfaceInfo& surface_info) { + // TODO(fsamuel): Once surface synchronization is turned on, the fallback + // surface should be set here. +} + void DelegatedFrameHostAndroid::CreateNewCompositorFrameSinkSupport() { constexpr bool is_root = false; - constexpr bool handles_frame_sink_id_invalidation = false; constexpr bool needs_sync_points = true; support_.reset(); support_ = host_frame_sink_manager_->CreateCompositorFrameSinkSupport( - this, frame_sink_id_, is_root, handles_frame_sink_id_invalidation, - needs_sync_points); + this, frame_sink_id_, is_root, needs_sync_points); } viz::SurfaceId DelegatedFrameHostAndroid::SurfaceId() const { diff --git a/chromium/ui/android/delegated_frame_host_android.h b/chromium/ui/android/delegated_frame_host_android.h index 3d2605cedc8..00a03ae167a 100644 --- a/chromium/ui/android/delegated_frame_host_android.h +++ b/chromium/ui/android/delegated_frame_host_android.h @@ -7,9 +7,10 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "cc/output/copy_output_request.h" -#include "cc/resources/returned_resource.h" +#include "components/viz/common/quads/copy_output_request.h" +#include "components/viz/common/resources/returned_resource.h" #include "components/viz/common/surfaces/surface_info.h" +#include "components/viz/host/host_frame_sink_client.h" #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h" #include "components/viz/service/frame_sinks/compositor_frame_sink_support_client.h" #include "ui/android/ui_android_export.h" @@ -33,14 +34,16 @@ class WindowAndroidCompositor; class UI_ANDROID_EXPORT DelegatedFrameHostAndroid : public viz::CompositorFrameSinkSupportClient, - public cc::ExternalBeginFrameSourceClient { + public viz::ExternalBeginFrameSourceClient, + public viz::HostFrameSinkClient { public: class Client { public: virtual void SetBeginFrameSource( - cc::BeginFrameSource* begin_frame_source) = 0; + viz::BeginFrameSource* begin_frame_source) = 0; virtual void DidReceiveCompositorFrameAck() = 0; - virtual void ReclaimResources(const std::vector<cc::ReturnedResource>&) = 0; + virtual void ReclaimResources( + const std::vector<viz::ReturnedResource>&) = 0; }; DelegatedFrameHostAndroid(ViewAndroid* view, @@ -53,7 +56,7 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid void SubmitCompositorFrame(const viz::LocalSurfaceId& local_surface_id, cc::CompositorFrame frame); - void DidNotProduceFrame(const cc::BeginFrameAck& ack); + void DidNotProduceFrame(const viz::BeginFrameAck& ack); void DestroyDelegatedContent(); @@ -65,7 +68,7 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid void RequestCopyOfSurface( WindowAndroidCompositor* compositor, const gfx::Rect& src_subrect_in_pixel, - cc::CopyOutputRequest::CopyOutputRequestCallback result_callback); + viz::CopyOutputRequest::CopyOutputRequestCallback result_callback); void CompositorFrameSinkChanged(); @@ -80,17 +83,20 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid private: // viz::CompositorFrameSinkSupportClient implementation. void DidReceiveCompositorFrameAck( - const std::vector<cc::ReturnedResource>& resources) override; - void OnBeginFrame(const cc::BeginFrameArgs& args) override; + const std::vector<viz::ReturnedResource>& resources) override; + void OnBeginFrame(const viz::BeginFrameArgs& args) override; void ReclaimResources( - const std::vector<cc::ReturnedResource>& resources) override; + const std::vector<viz::ReturnedResource>& resources) override; void WillDrawSurface(const viz::LocalSurfaceId& local_surface_id, const gfx::Rect& damage_rect) override; void OnBeginFramePausedChanged(bool paused) override; - // cc::ExternalBeginFrameSourceClient implementation. + // viz::ExternalBeginFrameSourceClient implementation. void OnNeedsBeginFrames(bool needs_begin_frames) override; + // viz::HostFrameSinkClient implementation. + void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override; + void CreateNewCompositorFrameSinkSupport(); const viz::FrameSinkId frame_sink_id_; @@ -103,7 +109,7 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid Client* client_; std::unique_ptr<viz::CompositorFrameSinkSupport> support_; - cc::ExternalBeginFrameSource begin_frame_source_; + viz::ExternalBeginFrameSource begin_frame_source_; viz::SurfaceInfo surface_info_; bool has_transparent_background_ = false; diff --git a/chromium/ui/android/display_android_manager.cc b/chromium/ui/android/display_android_manager.cc index 4904bc8b331..b7c04d5e362 100644 --- a/chromium/ui/android/display_android_manager.cc +++ b/chromium/ui/android/display_android_manager.cc @@ -32,10 +32,6 @@ void SetScreenAndroid() { Java_DisplayAndroidManager_onNativeSideCreated(env, (jlong)manager); } -bool RegisterScreenAndroid(JNIEnv* env) { - return RegisterNativesImpl(env); -} - DisplayAndroidManager::DisplayAndroidManager() {} DisplayAndroidManager::~DisplayAndroidManager() {} @@ -94,10 +90,9 @@ void DisplayAndroidManager::UpdateDisplay( if (!Display::HasForceDeviceScaleFactor()) display.set_device_scale_factor(dipScale); if (!Display::HasForceColorProfile()) { - if (isWideColorGamut) - display.set_color_space(gfx::ColorSpace::CreateDisplayP3D65()); - else - display.set_color_space(gfx::ColorSpace::CreateSRGB()); + // TODO(ccameron): Use CreateDisplayP3D65 if isWideColorGamut is true, once + // the feature is ready to use. + display.set_color_space(gfx::ColorSpace::CreateSRGB()); } display.set_size_in_pixels(bounds_in_pixels.size()); diff --git a/chromium/ui/android/event_forwarder.cc b/chromium/ui/android/event_forwarder.cc index 0a19b41732c..023bc6e754a 100644 --- a/chromium/ui/android/event_forwarder.cc +++ b/chromium/ui/android/event_forwarder.cc @@ -67,7 +67,7 @@ jboolean EventForwarder::OnTouchEvent(JNIEnv* env, jint android_tool_type_1, jint android_button_state, jint android_meta_state, - jboolean is_touch_handle_event) { + jboolean for_touch_handle) { ui::MotionEventAndroid::Pointer pointer0( pointer_id_0, pos_x_0, pos_y_0, touch_major_0, touch_minor_0, orientation_0, tilt_0, android_tool_type_0); @@ -78,8 +78,9 @@ jboolean EventForwarder::OnTouchEvent(JNIEnv* env, env, motion_event.obj(), 1.f / view_->GetDipScale(), 0.f, 0.f, 0.f, time_ms, android_action, pointer_count, history_size, action_index, 0 /* action_button */, android_button_state, android_meta_state, - raw_pos_x - pos_x_0, raw_pos_y - pos_y_0, &pointer0, &pointer1); - return view_->OnTouchEvent(event, is_touch_handle_event); + raw_pos_x - pos_x_0, raw_pos_y - pos_y_0, for_touch_handle, &pointer0, + &pointer1); + return view_->OnTouchEvent(event); } void EventForwarder::OnMouseEvent(JNIEnv* env, @@ -107,7 +108,8 @@ void EventForwarder::OnMouseEvent(JNIEnv* env, time_ms, android_action, 1 /* pointer_count */, 0 /* history_size */, 0 /* action_index */, android_action_button, android_button_state, android_meta_state, 0 /* raw_offset_x_pixels */, - 0 /* raw_offset_y_pixels */, &pointer, nullptr); + 0 /* raw_offset_y_pixels */, false /* for_touch_handle */, &pointer, + nullptr); view_->OnMouseEvent(event); } @@ -131,12 +133,12 @@ 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); - ui::MotionEventAndroid event(env, nullptr, 1.f / view_->GetDipScale(), - ticks_x, ticks_y, pixels_per_tick, time_ms, - 0 /* action */, 1 /* pointer_count */, - 0 /* history_size */, 0 /* action_index */, 0, 0, - 0, 0 /* raw_offset_x_pixels */, - 0 /* raw_offset_y_pixels */, &pointer, nullptr); + ui::MotionEventAndroid event( + env, nullptr, 1.f / view_->GetDipScale(), ticks_x, ticks_y, + pixels_per_tick, time_ms, 0 /* action */, 1 /* pointer_count */, + 0 /* history_size */, 0 /* action_index */, 0, 0, 0, + 0 /* raw_offset_x_pixels */, 0 /* raw_offset_y_pixels */, + false /* for_touch_handle */, &pointer, nullptr); view_->OnMouseWheelEvent(event); } @@ -161,8 +163,4 @@ void EventForwarder::OnDragEvent(JNIEnv* env, view_->OnDragEvent(event); } -bool RegisterEventForwarder(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace ui diff --git a/chromium/ui/android/event_forwarder.h b/chromium/ui/android/event_forwarder.h index ddc02b9f18d..86cf413b39f 100644 --- a/chromium/ui/android/event_forwarder.h +++ b/chromium/ui/android/event_forwarder.h @@ -93,8 +93,6 @@ class EventForwarder { DISALLOW_COPY_AND_ASSIGN(EventForwarder); }; -bool RegisterEventForwarder(JNIEnv* env); - } // namespace ui #endif // UI_ANDROID_EVENT_FORWARDER_H_ diff --git a/chromium/ui/android/overscroll_glow.cc b/chromium/ui/android/overscroll_glow.cc index 7f3c43da306..0470d11ad17 100644 --- a/chromium/ui/android/overscroll_glow.cc +++ b/chromium/ui/android/overscroll_glow.cc @@ -74,10 +74,10 @@ float OverscrollGlow::GetVisibleAlpha() const { } bool OverscrollGlow::OnOverscrolled(base::TimeTicks current_time, - const gfx::Vector2dF& accumulated_overscroll, - gfx::Vector2dF overscroll_delta, - gfx::Vector2dF velocity, - const gfx::Vector2dF& overscroll_location) { + gfx::Vector2dF accumulated_overscroll, + gfx::Vector2dF overscroll_delta, + gfx::Vector2dF velocity, + gfx::Vector2dF overscroll_location) { // The size of the glow determines the relative effect of the inputs; an // empty-sized effect is effectively disabled. if (viewport_size_.IsEmpty()) diff --git a/chromium/ui/android/overscroll_glow.h b/chromium/ui/android/overscroll_glow.h index 8253de04575..64cb4ce82f8 100644 --- a/chromium/ui/android/overscroll_glow.h +++ b/chromium/ui/android/overscroll_glow.h @@ -47,11 +47,12 @@ class UI_ANDROID_EXPORT OverscrollGlow { // |velocity| is in device pixels / second. // |overscroll_location| is the coordinate of the causal overscrolling event. // Returns true if the effect still needs animation ticks. - bool OnOverscrolled(base::TimeTicks current_time, - const gfx::Vector2dF& accumulated_overscroll, - gfx::Vector2dF overscroll_delta, - gfx::Vector2dF velocity, - const gfx::Vector2dF& overscroll_location); + // This method is made virtual for mocking. + virtual bool OnOverscrolled(base::TimeTicks current_time, + gfx::Vector2dF accumulated_overscroll, + gfx::Vector2dF overscroll_delta, + gfx::Vector2dF velocity, + gfx::Vector2dF overscroll_location); // Returns true if the effect still needs animation ticks, with effect layers // attached to |parent_layer| if necessary. diff --git a/chromium/ui/android/overscroll_refresh.cc b/chromium/ui/android/overscroll_refresh.cc index 2f2af62d8cd..60b54aa3dc1 100644 --- a/chromium/ui/android/overscroll_refresh.cc +++ b/chromium/ui/android/overscroll_refresh.cc @@ -24,6 +24,12 @@ OverscrollRefresh::OverscrollRefresh(OverscrollRefreshHandler* handler) DCHECK(handler); } +OverscrollRefresh::OverscrollRefresh() + : scrolled_to_top_(true), + overflow_y_hidden_(false), + scroll_consumption_state_(DISABLED), + handler_(nullptr) {} + OverscrollRefresh::~OverscrollRefresh() { } @@ -43,15 +49,10 @@ void OverscrollRefresh::OnScrollEnd(const gfx::Vector2dF& scroll_velocity) { Release(allow_activation); } -void OverscrollRefresh::OnScrollUpdateAck(bool was_consumed) { +void OverscrollRefresh::OnOverscrolled() { if (scroll_consumption_state_ != AWAITING_SCROLL_UPDATE_ACK) return; - if (was_consumed) { - scroll_consumption_state_ = DISABLED; - return; - } - scroll_consumption_state_ = handler_->PullStart() ? ENABLED : DISABLED; } diff --git a/chromium/ui/android/overscroll_refresh.h b/chromium/ui/android/overscroll_refresh.h index 7cb98e272ce..f69b2f74cbf 100644 --- a/chromium/ui/android/overscroll_refresh.h +++ b/chromium/ui/android/overscroll_refresh.h @@ -30,6 +30,7 @@ class UI_ANDROID_EXPORT OverscrollRefresh { enum { kMinPullsToActivate = 3 }; explicit OverscrollRefresh(OverscrollRefreshHandler* handler); + ~OverscrollRefresh(); // Scroll event stream listening methods. @@ -37,9 +38,11 @@ class UI_ANDROID_EXPORT OverscrollRefresh { // Returns whether the refresh was activated. void OnScrollEnd(const gfx::Vector2dF& velocity); - // Scroll ack listener. The effect will only be activated if the initial - // updates go unconsumed. - void OnScrollUpdateAck(bool was_consumed); + // Scroll ack listener. The effect will only be activated if |can_navigate| + // is true which happens when the scroll update is not consumed and the + // scroll_boundary_behavior on y axis is 'auto'. + // This method is made virtual for mocking. + virtual void OnOverscrolled(); // Returns true if the effect has consumed the |scroll_delta|. bool WillHandleScrollUpdate(const gfx::Vector2dF& scroll_delta); @@ -55,13 +58,20 @@ class UI_ANDROID_EXPORT OverscrollRefresh { // Reset the effect to its inactive state, immediately detaching and // disabling any active effects. - void Reset(); + // This method is made virtual for mocking. + virtual void Reset(); // Returns true if the refresh effect is either being manipulated or animated. - bool IsActive() const; + // This method is made virtual for mocking. + virtual bool IsActive() const; // Returns true if the effect is waiting for an unconsumed scroll to start. - bool IsAwaitingScrollUpdateAck() const; + // This method is made virtual for mocking. + virtual bool IsAwaitingScrollUpdateAck() const; + + protected: + // This constructor is for mocking only. + OverscrollRefresh(); private: void Release(bool allow_refresh); diff --git a/chromium/ui/android/overscroll_refresh_unittest.cc b/chromium/ui/android/overscroll_refresh_unittest.cc index c133d759a8e..f31b85f4504 100644 --- a/chromium/ui/android/overscroll_refresh_unittest.cc +++ b/chromium/ui/android/overscroll_refresh_unittest.cc @@ -86,7 +86,7 @@ TEST_F(OverscrollRefreshTest, Basic) { EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck()); // The unconsumed, overscrolling scroll will trigger the effect. - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); EXPECT_TRUE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_TRUE(GetAndResetPullStarted()); @@ -124,7 +124,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialYOffsetIsNotZero) { ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); @@ -145,7 +145,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfOverflowYHidden) { ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); @@ -164,7 +164,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollDownward) { EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); @@ -180,11 +180,12 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollOrTouchConsumed) { // Consumption of the initial touchmove or scroll should prevent future // activation. - effect.OnScrollUpdateAck(true); + effect.Reset(); + effect.OnOverscrolled(); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); @@ -198,7 +199,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfFlungDownward) { effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); ASSERT_TRUE(effect.IsActive()); EXPECT_TRUE(GetAndResetPullStarted()); @@ -213,7 +214,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfReleasedWithoutActivation) { effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); ASSERT_TRUE(effect.IsActive()); EXPECT_TRUE(GetAndResetPullStarted()); @@ -229,7 +230,7 @@ TEST_F(OverscrollRefreshTest, NotTriggeredIfReset) { effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); - effect.OnScrollUpdateAck(false); + effect.OnOverscrolled(); ASSERT_TRUE(effect.IsActive()); EXPECT_TRUE(GetAndResetPullStarted()); diff --git a/chromium/ui/android/resources/resource_factory.cc b/chromium/ui/android/resources/resource_factory.cc index f6ffba329d5..1cbdde23f6b 100644 --- a/chromium/ui/android/resources/resource_factory.cc +++ b/chromium/ui/android/resources/resource_factory.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/android/resources/resource_factory.h" - #include "jni/ResourceFactory_jni.h" #include "ui/android/resources/nine_patch_resource.h" #include "ui/gfx/geometry/rect.h" @@ -12,10 +10,6 @@ using base::android::JavaParamRef; namespace ui { -bool RegisterResourceFactory(JNIEnv* env) { - return RegisterNativesImpl(env); -} - jlong CreateBitmapResource(JNIEnv* env, const JavaParamRef<jclass>& clazz) { return reinterpret_cast<intptr_t>(new Resource()); } diff --git a/chromium/ui/android/resources/resource_factory.h b/chromium/ui/android/resources/resource_factory.h deleted file mode 100644 index 670c290d358..00000000000 --- a/chromium/ui/android/resources/resource_factory.h +++ /dev/null @@ -1,16 +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_ANDROID_RESOURCES_RESOURCE_FACTORY_H_ -#define UI_ANDROID_RESOURCES_RESOURCE_FACTORY_H_ - -#include "base/android/jni_android.h" - -namespace ui { - -bool RegisterResourceFactory(JNIEnv* env); - -} // namespace ui - -#endif // UI_ANDROID_RESOURCES_RESOURCE_FACTORY_H_ diff --git a/chromium/ui/android/resources/resource_manager_impl.cc b/chromium/ui/android/resources/resource_manager_impl.cc index c2d5cf938b4..c7c02d9377b 100644 --- a/chromium/ui/android/resources/resource_manager_impl.cc +++ b/chromium/ui/android/resources/resource_manager_impl.cc @@ -233,11 +233,6 @@ bool ResourceManagerImpl::OnMemoryDump( return true; } -// static -bool ResourceManagerImpl::RegisterResourceManager(JNIEnv* env) { - return RegisterNativesImpl(env); -} - void ResourceManagerImpl::PreloadResourceFromJava(AndroidResourceType res_type, int res_id) { TRACE_EVENT2("ui", "ResourceManagerImpl::PreloadResourceFromJava", diff --git a/chromium/ui/android/resources/resource_manager_impl.h b/chromium/ui/android/resources/resource_manager_impl.h index 430078717ab..9eb3998d303 100644 --- a/chromium/ui/android/resources/resource_manager_impl.h +++ b/chromium/ui/android/resources/resource_manager_impl.h @@ -58,8 +58,6 @@ class UI_ANDROID_EXPORT ResourceManagerImpl void ClearTintedResourceCache(JNIEnv* env, const base::android::JavaRef<jobject>& jobj); - static bool RegisterResourceManager(JNIEnv* env); - // base::trace_event::MemoryDumpProvider implementation. bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, base::trace_event::ProcessMemoryDump* pmd) override; diff --git a/chromium/ui/android/run_all_unittests.cc b/chromium/ui/android/run_all_unittests.cc index f15eb51ea02..f4419350176 100644 --- a/chromium/ui/android/run_all_unittests.cc +++ b/chromium/ui/android/run_all_unittests.cc @@ -10,10 +10,8 @@ #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/android/ui_android_jni_registrar.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" -#include "ui/gfx/android/gfx_jni_registrar.h" namespace { @@ -25,9 +23,6 @@ class UIAndroidTestSuite : public base::TestSuite { void Initialize() override { base::TestSuite::Initialize(); - gfx::android::RegisterJni(base::android::AttachCurrentThread()); - ui::RegisterUIAndroidJni(base::android::AttachCurrentThread()); - ui::RegisterPathProvider(); base::FilePath ui_test_pak_path; diff --git a/chromium/ui/android/screen_android.h b/chromium/ui/android/screen_android.h index 4352c0e3f26..1482223b0e4 100644 --- a/chromium/ui/android/screen_android.h +++ b/chromium/ui/android/screen_android.h @@ -10,8 +10,6 @@ namespace ui { -bool RegisterScreenAndroid(JNIEnv* env); - UI_ANDROID_EXPORT void SetScreenAndroid(); } // namespace display diff --git a/chromium/ui/android/view_android.cc b/chromium/ui/android/view_android.cc index f2d1486dbfd..dd7bf1b8efb 100644 --- a/chromium/ui/android/view_android.cc +++ b/chromium/ui/android/view_android.cc @@ -12,12 +12,14 @@ #include "base/stl_util.h" #include "cc/layers/layer.h" #include "jni/ViewAndroidDelegate_jni.h" +#include "third_party/WebKit/public/platform/WebCursorInfo.h" #include "ui/android/event_forwarder.h" #include "ui/android/view_client.h" #include "ui/android/window_android.h" #include "ui/base/layout.h" #include "ui/events/android/drag_event_android.h" #include "ui/events/android/motion_event_android.h" +#include "ui/gfx/android/java_bitmap.h" #include "url/gurl.h" namespace ui { @@ -25,6 +27,7 @@ namespace ui { using base::android::ConvertUTF8ToJavaString; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; +using blink::WebCursorInfo; ViewAndroid::ScopedAnchorView::ScopedAnchorView( JNIEnv* env, @@ -84,6 +87,7 @@ ViewAndroid::ViewAndroid(ViewClient* view_client) ViewAndroid::ViewAndroid() : ViewAndroid(nullptr) {} ViewAndroid::~ViewAndroid() { + observer_list_.Clear(); RemoveFromParent(); for (std::list<ViewAndroid*>::iterator it = children_.begin(); @@ -136,6 +140,8 @@ void ViewAndroid::AddChild(ViewAndroid* child) { // accidentally overwrite the valid ones in the children. if (!physical_size_.IsEmpty()) child->OnPhysicalBackingSizeChanged(physical_size_); + if (GetWindowAndroid()) + child->OnAttachedToWindow(); } // static @@ -210,16 +216,16 @@ ScopedJavaLocalRef<jobject> ViewAndroid::GetContainerView() { return Java_ViewAndroidDelegate_getContainerView(env, delegate); } -gfx::Point ViewAndroid::GetLocationOfContainerViewOnScreen() { +gfx::Point ViewAndroid::GetLocationOfContainerViewInWindow() { ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate()); if (delegate.is_null()) return gfx::Point(); JNIEnv* env = base::android::AttachCurrentThread(); gfx::Point result( - Java_ViewAndroidDelegate_getXLocationOfContainerViewOnScreen(env, + Java_ViewAndroidDelegate_getXLocationOfContainerViewInWindow(env, delegate), - Java_ViewAndroidDelegate_getYLocationOfContainerViewOnScreen(env, + Java_ViewAndroidDelegate_getYLocationOfContainerViewInWindow(env, delegate)); return result; @@ -229,6 +235,8 @@ void ViewAndroid::RemoveChild(ViewAndroid* child) { DCHECK(child); DCHECK_EQ(child->parent_, this); + if (GetWindowAndroid()) + child->OnDetachedFromWindow(); std::list<ViewAndroid*>::iterator it = std::find(children_.begin(), children_.end(), child); DCHECK(it != children_.end()); @@ -236,6 +244,28 @@ void ViewAndroid::RemoveChild(ViewAndroid* child) { child->parent_ = nullptr; } +void ViewAndroid::AddObserver(ViewAndroidObserver* observer) { + observer_list_.AddObserver(observer); +} + +void ViewAndroid::RemoveObserver(ViewAndroidObserver* observer) { + observer_list_.RemoveObserver(observer); +} + +void ViewAndroid::OnAttachedToWindow() { + for (auto& observer : observer_list_) + observer.OnAttachedToWindow(); + for (auto* child : children_) + child->OnAttachedToWindow(); +} + +void ViewAndroid::OnDetachedFromWindow() { + for (auto& observer : observer_list_) + observer.OnDetachedFromWindow(); + for (auto* child : children_) + child->OnDetachedFromWindow(); +} + WindowAndroid* ViewAndroid::GetWindowAndroid() const { return parent_ ? parent_->GetWindowAndroid() : nullptr; } @@ -254,6 +284,22 @@ cc::Layer* ViewAndroid::GetLayer() const { return layer_.get(); } +bool ViewAndroid::HasFocus() { + ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate()); + if (delegate.is_null()) + return false; + JNIEnv* env = base::android::AttachCurrentThread(); + return Java_ViewAndroidDelegate_hasFocus(env, delegate); +} + +void ViewAndroid::RequestFocus() { + ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate()); + if (delegate.is_null()) + return; + JNIEnv* env = base::android::AttachCurrentThread(); + Java_ViewAndroidDelegate_requestFocus(env, delegate); +} + void ViewAndroid::SetLayer(scoped_refptr<cc::Layer> layer) { layer_ = layer; } @@ -272,6 +318,28 @@ bool ViewAndroid::StartDragAndDrop(const JavaRef<jstring>& jtext, jimage); } +void ViewAndroid::OnCursorChanged(int type, + const SkBitmap& custom_image, + const gfx::Point& hotspot) { + ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate()); + if (delegate.is_null()) + return; + JNIEnv* env = base::android::AttachCurrentThread(); + if (type == WebCursorInfo::kTypeCustom) { + if (custom_image.drawsNothing()) { + Java_ViewAndroidDelegate_onCursorChanged(env, delegate, + WebCursorInfo::kTypePointer); + return; + } + ScopedJavaLocalRef<jobject> java_bitmap = + gfx::ConvertToJavaBitmap(&custom_image); + Java_ViewAndroidDelegate_onCursorChangedToCustom(env, delegate, java_bitmap, + hotspot.x(), hotspot.y()); + } else { + Java_ViewAndroidDelegate_onCursorChanged(env, delegate, type); + } +} + void ViewAndroid::OnBackgroundColorChanged(unsigned int color) { ScopedJavaLocalRef<jobject> delegate(GetViewAndroidDelegate()); if (delegate.is_null()) @@ -335,20 +403,17 @@ bool ViewAndroid::SendDragEventToClient(ViewClient* client, return client->OnDragEvent(*e); } -bool ViewAndroid::OnTouchEvent(const MotionEventAndroid& event, - bool for_touch_handle) { - return HitTest( - base::Bind(&ViewAndroid::SendTouchEventToClient, for_touch_handle), event, - event.GetPoint()); +bool ViewAndroid::OnTouchEvent(const MotionEventAndroid& event) { + return HitTest(base::Bind(&ViewAndroid::SendTouchEventToClient), event, + event.GetPoint()); } // static -bool ViewAndroid::SendTouchEventToClient(bool for_touch_handle, - ViewClient* client, +bool ViewAndroid::SendTouchEventToClient(ViewClient* client, const MotionEventAndroid& event, const gfx::PointF& point) { std::unique_ptr<MotionEventAndroid> e(event.CreateFor(point)); - return client->OnTouchEvent(*e, for_touch_handle); + return client->OnTouchEvent(*e); } bool ViewAndroid::OnMouseEvent(const MotionEventAndroid& event) { diff --git a/chromium/ui/android/view_android.h b/chromium/ui/android/view_android.h index a9880543ca1..5dc9253034d 100644 --- a/chromium/ui/android/view_android.h +++ b/chromium/ui/android/view_android.h @@ -11,19 +11,28 @@ #include "base/android/jni_weak_ref.h" #include "base/bind.h" #include "base/memory/ref_counted.h" +#include "base/observer_list.h" #include "ui/android/ui_android_export.h" +#include "ui/android/view_android_observer.h" #include "ui/gfx/geometry/rect_f.h" +class SkBitmap; + namespace cc { class Layer; } +namespace gfx { +class Point; +} + namespace ui { class DragEventAndroid; class EventForwarder; class MotionEventAndroid; class ViewClient; class WindowAndroid; +class ViewAndroidObserver; // View-related parameters from frame updates. struct FrameInfo { @@ -135,6 +144,9 @@ class UI_ANDROID_EXPORT ViewAndroid { // Detaches this view from its parent. void RemoveFromParent(); + bool HasFocus(); + void RequestFocus(); + // Sets the layout relative to parent. Used to do hit testing against events. void SetLayout(LayoutParams params); @@ -143,6 +155,9 @@ class UI_ANDROID_EXPORT ViewAndroid { gfx::Size GetPhysicalBackingSize(); void OnPhysicalBackingSizeChanged(const gfx::Size& size); + void OnCursorChanged(int type, + const SkBitmap& custom_image, + const gfx::Point& hotspot); void OnBackgroundColorChanged(unsigned int color); void OnTopControlsChanged(float top_controls_offset, float top_content_offset); @@ -158,7 +173,11 @@ class UI_ANDROID_EXPORT ViewAndroid { base::android::ScopedJavaLocalRef<jobject> GetContainerView(); // Return the location of the container view in physical pixels. - gfx::Point GetLocationOfContainerViewOnScreen(); + gfx::Point GetLocationOfContainerViewInWindow(); + + // ViewAndroid does not own |observer|s. + void AddObserver(ViewAndroidObserver* observer); + void RemoveObserver(ViewAndroidObserver* observer); float GetDipScale(); @@ -170,12 +189,15 @@ class UI_ANDROID_EXPORT ViewAndroid { friend class ViewAndroidBoundsTest; bool OnDragEvent(const DragEventAndroid& event); - bool OnTouchEvent(const MotionEventAndroid& event, bool for_touch_handle); + bool OnTouchEvent(const MotionEventAndroid& event); bool OnMouseEvent(const MotionEventAndroid& event); bool OnMouseWheelEvent(const MotionEventAndroid& event); void RemoveChild(ViewAndroid* child); + void OnAttachedToWindow(); + void OnDetachedFromWindow(); + template <typename E> using ViewClientCallback = const base::Callback<bool(ViewClient*, const E&, const gfx::PointF&)>; @@ -188,8 +210,7 @@ class UI_ANDROID_EXPORT ViewAndroid { static bool SendDragEventToClient(ViewClient* client, const DragEventAndroid& event, const gfx::PointF& point); - static bool SendTouchEventToClient(bool for_touch_handle, - ViewClient* client, + static bool SendTouchEventToClient(ViewClient* client, const MotionEventAndroid& event, const gfx::PointF& point); static bool SendMouseEventToClient(ViewClient* client, @@ -215,6 +236,7 @@ class UI_ANDROID_EXPORT ViewAndroid { GetViewAndroidDelegate() const; std::list<ViewAndroid*> children_; + base::ObserverList<ViewAndroidObserver> observer_list_; scoped_refptr<cc::Layer> layer_; JavaObjectWeakGlobalRef delegate_; diff --git a/chromium/ui/android/view_android_observer.h b/chromium/ui/android/view_android_observer.h new file mode 100644 index 00000000000..cb8cbe51de1 --- /dev/null +++ b/chromium/ui/android/view_android_observer.h @@ -0,0 +1,28 @@ +// 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_ANDROID_VIEW_ANDROID_OBSERVER_H_ +#define UI_ANDROID_VIEW_ANDROID_OBSERVER_H_ + +#include "ui/android/ui_android_export.h" + +namespace ui { + +class UI_ANDROID_EXPORT ViewAndroidObserver { + public: + // Notifies that view gets attached to window. Note that the notification + // is not sent if view is already in attached state. + virtual void OnAttachedToWindow() = 0; + + // Notifies that view gets detached from window. Note that the notification + // is not sent if view is already in detached state. + virtual void OnDetachedFromWindow() = 0; + + protected: + virtual ~ViewAndroidObserver() {} +}; + +} // namespace ui + +#endif // UI_ANDROID_VIEW_ANDROID_OBSERVER_H_ diff --git a/chromium/ui/android/view_android_unittests.cc b/chromium/ui/android/view_android_unittests.cc index 78a01f68cd0..220566840e5 100644 --- a/chromium/ui/android/view_android_unittests.cc +++ b/chromium/ui/android/view_android_unittests.cc @@ -6,7 +6,9 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/android/event_forwarder.h" #include "ui/android/view_android.h" +#include "ui/android/view_android_observer.h" #include "ui/android/view_client.h" +#include "ui/android/window_android.h" #include "ui/events/android/motion_event_android.h" namespace ui { @@ -18,8 +20,7 @@ class TestViewClient : public ViewClient { TestViewClient() : handle_event_(true), called_(false) {} void SetHandleEvent(bool handle_event) { handle_event_ = handle_event; } - bool OnTouchEvent(const MotionEventAndroid& event, - bool for_touch_handle) override { + bool OnTouchEvent(const MotionEventAndroid& event) override { called_ = true; return handle_event_; } @@ -54,9 +55,9 @@ class ViewAndroidBoundsTest : public testing::Test { ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0); ui::MotionEventAndroid::Pointer pointer1(0, 0, 0, 0, 0, 0, 0, 0); ui::MotionEventAndroid event(nullptr, JavaParamRef<jobject>(nullptr), 1.f, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, false, &pointer0, &pointer1); - root_.OnTouchEvent(event, false); + root_.OnTouchEvent(event); } void ExpectHit(const TestViewClient& hitClient) { @@ -200,4 +201,60 @@ TEST(ViewAndroidTest, ChecksMultipleEventForwarders) { EXPECT_DCHECK_DEATH(rwhv2.GetEventForwarder()); } +class Observer : public ViewAndroidObserver { + public: + Observer() : attached_(false) {} + + void OnAttachedToWindow() override { attached_ = true; } + + void OnDetachedFromWindow() override { attached_ = false; } + + bool attached_; +}; + +TEST(ViewAndroidTest, Observer) { + std::unique_ptr<WindowAndroid> window(WindowAndroid::CreateForTesting()); + ViewAndroid top; + ViewAndroid bottom; + + Observer top_observer; + Observer bottom_observer; + + top.AddObserver(&top_observer); + bottom.AddObserver(&bottom_observer); + + top.AddChild(&bottom); + + EXPECT_FALSE(top_observer.attached_); + EXPECT_FALSE(bottom_observer.attached_); + + // Views in a tree all get notified of 'attached' event. + window->AddChild(&top); + EXPECT_TRUE(top_observer.attached_); + EXPECT_TRUE(bottom_observer.attached_); + + // Observer, upon addition, does not get notified of the current + // attached state. + Observer top_observer2; + top.AddObserver(&top_observer2); + EXPECT_FALSE(top_observer2.attached_); + + bottom.RemoveFromParent(); + EXPECT_FALSE(bottom_observer.attached_); + top.RemoveFromParent(); + EXPECT_FALSE(top_observer.attached_); + + window->AddChild(&top); + EXPECT_TRUE(top_observer.attached_); + + // View, upon addition to a tree in the attached state, should be notified. + top.AddChild(&bottom); + EXPECT_TRUE(bottom_observer.attached_); + + // Views in a tree all get notified of 'detached' event. + top.RemoveFromParent(); + EXPECT_FALSE(top_observer.attached_); + EXPECT_FALSE(bottom_observer.attached_); +} + } // namespace ui diff --git a/chromium/ui/android/view_client.cc b/chromium/ui/android/view_client.cc index 2fc2532f521..007ff62de21 100644 --- a/chromium/ui/android/view_client.cc +++ b/chromium/ui/android/view_client.cc @@ -6,8 +6,7 @@ namespace ui { -bool ViewClient::OnTouchEvent(const MotionEventAndroid& event, - bool for_touch_handle) { +bool ViewClient::OnTouchEvent(const MotionEventAndroid& event) { return false; } diff --git a/chromium/ui/android/view_client.h b/chromium/ui/android/view_client.h index 95bbc284017..6a01dff18b9 100644 --- a/chromium/ui/android/view_client.h +++ b/chromium/ui/android/view_client.h @@ -19,8 +19,7 @@ class MotionEventAndroid; // the processing. class UI_ANDROID_EXPORT ViewClient { public: - virtual bool OnTouchEvent(const MotionEventAndroid& event, - bool for_touch_handle); + virtual bool OnTouchEvent(const MotionEventAndroid& event); virtual bool OnMouseEvent(const MotionEventAndroid& event); virtual bool OnMouseWheelEvent(const MotionEventAndroid& event); virtual bool OnDragEvent(const DragEventAndroid& event); diff --git a/chromium/ui/android/window_android.cc b/chromium/ui/android/window_android.cc index 388d4c6daad..080ce9ae594 100644 --- a/chromium/ui/android/window_android.cc +++ b/chromium/ui/android/window_android.cc @@ -11,8 +11,8 @@ #include "base/android/scoped_java_ref.h" #include "base/observer_list.h" #include "base/stl_util.h" -#include "cc/output/begin_frame_args.h" -#include "cc/scheduler/begin_frame_source.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/frame_sinks/begin_frame_source.h" #include "jni/WindowAndroid_jni.h" #include "ui/android/window_android_compositor.h" #include "ui/android/window_android_observer.h" @@ -24,21 +24,21 @@ using base::android::JavaParamRef; using base::android::JavaRef; using base::android::ScopedJavaLocalRef; -class WindowAndroid::WindowBeginFrameSource : public cc::BeginFrameSource { +class WindowAndroid::WindowBeginFrameSource : public viz::BeginFrameSource { public: explicit WindowBeginFrameSource(WindowAndroid* window) : window_(window), observers_( - base::ObserverList<cc::BeginFrameObserver>::NOTIFY_EXISTING_ONLY), + base::ObserverList<viz::BeginFrameObserver>::NOTIFY_EXISTING_ONLY), observer_count_(0), - next_sequence_number_(cc::BeginFrameArgs::kStartingFrameNumber), + next_sequence_number_(viz::BeginFrameArgs::kStartingFrameNumber), paused_(false) {} ~WindowBeginFrameSource() override {} - // cc::BeginFrameSource implementation. - void AddObserver(cc::BeginFrameObserver* obs) override; - void RemoveObserver(cc::BeginFrameObserver* obs) override; - void DidFinishFrame(cc::BeginFrameObserver* obs) override {} + // viz::BeginFrameSource implementation. + void AddObserver(viz::BeginFrameObserver* obs) override; + void RemoveObserver(viz::BeginFrameObserver* obs) override; + void DidFinishFrame(viz::BeginFrameObserver* obs) override {} bool IsThrottled() const override { return true; } void OnVSync(base::TimeTicks frame_time, base::TimeDelta vsync_period); @@ -46,15 +46,15 @@ class WindowAndroid::WindowBeginFrameSource : public cc::BeginFrameSource { private: WindowAndroid* const window_; - base::ObserverList<cc::BeginFrameObserver> observers_; + base::ObserverList<viz::BeginFrameObserver> observers_; int observer_count_; - cc::BeginFrameArgs last_begin_frame_args_; + viz::BeginFrameArgs last_begin_frame_args_; uint64_t next_sequence_number_; bool paused_; }; void WindowAndroid::WindowBeginFrameSource::AddObserver( - cc::BeginFrameObserver* obs) { + viz::BeginFrameObserver* obs) { DCHECK(obs); DCHECK(!observers_.HasObserver(obs)); @@ -65,13 +65,13 @@ void WindowAndroid::WindowBeginFrameSource::AddObserver( // Send a MISSED BeginFrame if possible and necessary. if (last_begin_frame_args_.IsValid()) { - cc::BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); + viz::BeginFrameArgs last_args = obs->LastUsedBeginFrameArgs(); if (!last_args.IsValid() || last_args.frame_time < last_begin_frame_args_.frame_time) { DCHECK(last_args.sequence_number < last_begin_frame_args_.sequence_number || last_args.source_id != last_begin_frame_args_.source_id); - last_begin_frame_args_.type = cc::BeginFrameArgs::MISSED; + last_begin_frame_args_.type = viz::BeginFrameArgs::MISSED; // TODO(crbug.com/602485): A deadline doesn't make too much sense // for a missed BeginFrame (the intention rather is 'immediately'), // but currently the retro frame logic is very strict in discarding @@ -84,7 +84,7 @@ void WindowAndroid::WindowBeginFrameSource::AddObserver( } void WindowAndroid::WindowBeginFrameSource::RemoveObserver( - cc::BeginFrameObserver* obs) { + viz::BeginFrameObserver* obs) { DCHECK(obs); DCHECK(observers_.HasObserver(obs)); @@ -99,9 +99,9 @@ void WindowAndroid::WindowBeginFrameSource::OnVSync( base::TimeDelta vsync_period) { // frame time is in the past, so give the next vsync period as the deadline. base::TimeTicks deadline = frame_time + vsync_period; - last_begin_frame_args_ = cc::BeginFrameArgs::Create( + last_begin_frame_args_ = viz::BeginFrameArgs::Create( BEGINFRAME_FROM_HERE, source_id(), next_sequence_number_, frame_time, - deadline, vsync_period, cc::BeginFrameArgs::NORMAL); + deadline, vsync_period, viz::BeginFrameArgs::NORMAL); DCHECK(last_begin_frame_args_.IsValid()); next_sequence_number_++; @@ -131,10 +131,6 @@ ScopedJavaLocalRef<jobject> WindowAndroid::GetJavaObject() { return base::android::ScopedJavaLocalRef<jobject>(java_window_); } -bool WindowAndroid::RegisterWindowAndroid(JNIEnv* env) { - return RegisterNativesImpl(env); -} - WindowAndroid::~WindowAndroid() { DCHECK(parent_ == nullptr) << "WindowAndroid must be a root view."; DCHECK(!compositor_); @@ -147,10 +143,6 @@ WindowAndroid* WindowAndroid::CreateForTesting() { return reinterpret_cast<WindowAndroid*>(native_pointer); } -void WindowAndroid::DestroyForTesting() { - delete this; -} - void WindowAndroid::OnCompositingDidCommit() { for (WindowAndroidObserver& observer : observer_list_) observer.OnCompositingDidCommit(); @@ -169,7 +161,7 @@ void WindowAndroid::RemoveObserver(WindowAndroidObserver* observer) { observer_list_.RemoveObserver(observer); } -cc::BeginFrameSource* WindowAndroid::GetBeginFrameSource() { +viz::BeginFrameSource* WindowAndroid::GetBeginFrameSource() { return begin_frame_source_.get(); } @@ -217,7 +209,14 @@ void WindowAndroid::OnVSync(JNIEnv* env, const JavaParamRef<jobject>& obj, jlong time_micros, jlong period_micros) { - base::TimeTicks frame_time(base::TimeTicks::FromInternalValue(time_micros)); + // Warning: It is generally unsafe to manufacture TimeTicks values. The + // following assumption is being made, AND COULD EASILY BREAK AT ANY TIME: + // Upstream, Java code is providing "System.nanos() / 1000," and this is the + // same timestamp that would be provided by the CLOCK_MONOTONIC POSIX clock. + DCHECK_EQ(base::TimeTicks::GetClock(), + base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC); + base::TimeTicks frame_time = + base::TimeTicks() + base::TimeDelta::FromMicroseconds(time_micros); base::TimeDelta vsync_period( base::TimeDelta::FromMicroseconds(period_micros)); diff --git a/chromium/ui/android/window_android.h b/chromium/ui/android/window_android.h index efb267991de..eebe2003bf3 100644 --- a/chromium/ui/android/window_android.h +++ b/chromium/ui/android/window_android.h @@ -21,14 +21,14 @@ #include "ui/android/view_android.h" #include "ui/gfx/geometry/vector2d_f.h" -namespace cc { -class BeginFrameSource; -} // namespace cc - namespace display { class DisplayAndroidManager; } // namespace display +namespace viz { +class BeginFrameSource; +} // namespace viz + namespace ui { class WindowAndroidCompositor; @@ -40,12 +40,12 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid { public: WindowAndroid(JNIEnv* env, jobject obj, int display_id); + ~WindowAndroid() override; + void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); base::android::ScopedJavaLocalRef<jobject> GetJavaObject(); - static bool RegisterWindowAndroid(JNIEnv* env); - // Compositor callback relay. void OnCompositingDidCommit(); @@ -56,7 +56,7 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid { void RemoveObserver(WindowAndroidObserver* observer); WindowAndroidCompositor* GetCompositor() { return compositor_; } - cc::BeginFrameSource* GetBeginFrameSource(); + viz::BeginFrameSource* GetBeginFrameSource(); // Runs the provided callback as soon as the current vsync was handled. void AddVSyncCompleteCallback(const base::Closure& callback); @@ -84,7 +84,6 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid { bool CanRequestPermission(const std::string& permission); static WindowAndroid* CreateForTesting(); - void DestroyForTesting(); // Return the window token for this window, if one exists. base::android::ScopedJavaLocalRef<jobject> GetWindowToken(); @@ -94,8 +93,6 @@ class UI_ANDROID_EXPORT WindowAndroid : public ViewAndroid { friend class DisplayAndroidManager; friend class WindowBeginFrameSource; - ~WindowAndroid() override; - void SetNeedsBeginFrames(bool needs_begin_frames); void RequestVSyncUpdate(); diff --git a/chromium/ui/android/window_android_compositor.h b/chromium/ui/android/window_android_compositor.h index 00f88e33005..33967bfff20 100644 --- a/chromium/ui/android/window_android_compositor.h +++ b/chromium/ui/android/window_android_compositor.h @@ -7,7 +7,7 @@ #include <memory> -#include "cc/output/copy_output_request.h" +#include "components/viz/common/quads/copy_output_request.h" #include "components/viz/common/surfaces/frame_sink_id.h" #include "ui/android/ui_android_export.h" @@ -26,7 +26,7 @@ class UI_ANDROID_EXPORT WindowAndroidCompositor { virtual void AttachLayerForReadback(scoped_refptr<cc::Layer> layer) = 0; virtual void RequestCopyOfOutputOnRootLayer( - std::unique_ptr<cc::CopyOutputRequest> request) = 0; + std::unique_ptr<viz::CopyOutputRequest> request) = 0; virtual void SetNeedsAnimate() = 0; virtual ResourceManager& GetResourceManager() = 0; virtual viz::FrameSinkId GetFrameSinkId() = 0; diff --git a/chromium/ui/app_list/BUILD.gn b/chromium/ui/app_list/BUILD.gn index 75b61ee6f4c..b0092432551 100644 --- a/chromium/ui/app_list/BUILD.gn +++ b/chromium/ui/app_list/BUILD.gn @@ -156,7 +156,6 @@ component("app_list") { "views/search_result_container_view.h", "views/search_result_list_view.cc", "views/search_result_list_view.h", - "views/search_result_list_view_delegate.h", "views/search_result_page_view.cc", "views/search_result_page_view.h", "views/search_result_tile_item_list_view.cc", diff --git a/chromium/ui/app_list/presenter/app_list_presenter.mojom b/chromium/ui/app_list/presenter/app_list_presenter.mojom index cba536e9b39..2b54979b6b2 100644 --- a/chromium/ui/app_list/presenter/app_list_presenter.mojom +++ b/chromium/ui/app_list/presenter/app_list_presenter.mojom @@ -10,6 +10,24 @@ import "services/ui/public/interfaces/window_manager_constants.mojom"; // 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. @@ -40,10 +58,14 @@ interface AppListPresenter { // Starts or stops a voice interaction session based on the current state. ToggleVoiceInteractionSession(); - // Updates y position and opacity of app list. |is_end_gesture| means it is - // the end of the gesture dragging of app list from shelf and should restore - // the opacity of the app list. + // Updates y position and opacity of app list. UpdateYPositionAndOpacity(int32 y_position_in_screen, - float background_opacity, - bool is_end_gesture); + 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/arc/BUILD.gn b/chromium/ui/arc/BUILD.gn index b6af6d81c6c..6c5deab5ea7 100644 --- a/chromium/ui/arc/BUILD.gn +++ b/chromium/ui/arc/BUILD.gn @@ -62,10 +62,14 @@ test("ui_arc_unittests") { deps = [ ":arc", + "//ash:test_support_without_content", "//base", "//base/test:test_support", "//components/arc:arc_test_support", + "//components/exo", + "//components/exo:test_support", "//mojo/edk/system", + "//testing/gmock", "//testing/gtest", "//ui/aura:test_support", "//ui/base:test_support", diff --git a/chromium/ui/arc/notification/arc_notification_content_view.cc b/chromium/ui/arc/notification/arc_notification_content_view.cc index 8a3ad2e2281..f4bb12596ae 100644 --- a/chromium/ui/arc/notification/arc_notification_content_view.cc +++ b/chromium/ui/arc/notification/arc_notification_content_view.cc @@ -21,11 +21,11 @@ #include "ui/gfx/transform.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/views/notification_control_buttons_view.h" -#include "ui/message_center/views/toast_contents_view.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" #include "ui/wm/core/window_util.h" namespace arc { @@ -84,7 +84,8 @@ class ArcNotificationContentView::EventForwarder : public ui::EventHandler { // TODO(yoshiki): Use a better tigger (eg. focusing EditText on // notification) than clicking (crbug.com/697379). - if (event->type() == ui::ET_MOUSE_PRESSED) + if (event->type() == ui::ET_MOUSE_PRESSED || + event->type() == ui::ET_GESTURE_TAP) owner_->Activate(); views::Widget* widget = owner_->GetWidget(); @@ -134,8 +135,10 @@ class ArcNotificationContentView::SlideHelper // Reset opacity to 1 to handle to case when the surface is sliding before // getting managed by this class, e.g. sliding in a popup before showing // in a message center view. - if (owner_->surface_ && owner_->surface_->GetWindow()) + if (owner_->surface_) { + DCHECK(owner_->surface_->GetWindow()); owner_->surface_->GetWindow()->layer()->SetOpacity(1.0f); + } } ~SlideHelper() override { if (GetSlideOutLayer()) @@ -166,8 +169,9 @@ class ArcNotificationContentView::SlideHelper } void OnSlideStart() { - if (!owner_->surface_ || !owner_->surface_->GetWindow()) + if (!owner_->surface_) return; + DCHECK(owner_->surface_->GetWindow()); surface_copy_ = ::wm::RecreateLayers(owner_->surface_->GetWindow()); // |surface_copy_| is at (0, 0) in owner_->layer(). surface_copy_->root()->SetBounds(gfx::Rect(surface_copy_->root()->size())); @@ -176,8 +180,9 @@ class ArcNotificationContentView::SlideHelper } void OnSlideEnd() { - if (!owner_->surface_ || !owner_->surface_->GetWindow()) + if (!owner_->surface_) return; + DCHECK(owner_->surface_->GetWindow()); owner_->surface_->GetWindow()->layer()->SetOpacity(1.0f); owner_->Layout(); surface_copy_.reset(); @@ -231,6 +236,10 @@ class ArcNotificationContentView::ContentViewDelegate return owner_->control_buttons_view_; } + bool IsExpanded() const override { return owner_->IsExpanded(); } + + void SetExpanded(bool expanded) override { owner_->SetExpanded(expanded); } + private: ArcNotificationContentView* const owner_; @@ -247,6 +256,10 @@ ArcNotificationContentView::ArcNotificationContentView( notification_key_(item->GetNotificationKey()), event_forwarder_(new EventForwarder(this)), mouse_enter_exit_handler_(new MouseEnterExitHandler(this)) { + // kNotificationWidth must be 360, since this value is separately defiend in + // ArcNotificationWrapperView class in Android side. + DCHECK_EQ(360, message_center::kNotificationWidth); + SetFocusBehavior(FocusBehavior::ALWAYS); set_notify_enter_exit_on_child(true); @@ -312,7 +325,7 @@ void ArcNotificationContentView::MaybeCreateFloatingControlButtons() { GetControlButtonBackgroundColor(item_->GetShownContents())); control_buttons_view_->ShowSettingsButton( item_->IsOpeningSettingsSupported()); - control_buttons_view_->ShowCloseButton(!item_->GetPinned()); + control_buttons_view_->ShowCloseButton(!notification_view->GetPinned()); views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL); params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; @@ -343,8 +356,10 @@ void ArcNotificationContentView::SetSurface(ArcNotificationSurface* surface) { // Reset |floating_control_buttons_widget_| when |surface_| is changed. floating_control_buttons_widget_.reset(); - if (surface_ && surface_->GetWindow()) { - surface_->GetWindow()->RemoveObserver(this); + if (surface_) { + DCHECK(surface_->GetWindow()); + DCHECK(surface_->GetContentWindow()); + surface_->GetContentWindow()->RemoveObserver(this); surface_->GetWindow()->RemovePreTargetHandler(event_forwarder_.get()); if (surface_->GetAttachedHost() == this) { @@ -355,8 +370,10 @@ void ArcNotificationContentView::SetSurface(ArcNotificationSurface* surface) { surface_ = surface; - if (surface_ && surface_->GetWindow()) { - surface_->GetWindow()->AddObserver(this); + if (surface_) { + DCHECK(surface_->GetWindow()); + DCHECK(surface_->GetContentWindow()); + surface_->GetContentWindow()->AddObserver(this); surface_->GetWindow()->AddPreTargetHandler(event_forwarder_.get()); if (GetWidget()) { @@ -411,18 +428,6 @@ void ArcNotificationContentView::UpdateControlButtonsVisibility() { floating_control_buttons_widget_->Hide(); } -void ArcNotificationContentView::UpdatePinnedState() { - if (!item_) - return; - - // Surface is not attached yet. - if (!control_buttons_view_) - return; - - control_buttons_view_->ShowCloseButton(!item_->GetPinned()); - Layout(); -} - void ArcNotificationContentView::UpdateSnapshot() { // Bail if we have a |surface_| because it controls the sizes and paints UI. if (surface_) @@ -464,6 +469,21 @@ void ArcNotificationContentView::UpdateAccessibleName() { accessible_name_ = item_->GetAccessibleName(); } +bool ArcNotificationContentView::IsExpanded() const { + return item_->GetExpandState() == mojom::ArcNotificationExpandState::EXPANDED; +} + +void ArcNotificationContentView::SetExpanded(bool expanded) { + auto expand_state = item_->GetExpandState(); + if (expanded) { + if (expand_state == mojom::ArcNotificationExpandState::COLLAPSED) + item_->ToggleExpansion(); + } else { + if (expand_state == mojom::ArcNotificationExpandState::EXPANDED) + item_->ToggleExpansion(); + } +} + void ArcNotificationContentView::ViewHierarchyChanged( const views::View::ViewHierarchyChangedDetails& details) { views::Widget* widget = GetWidget(); @@ -504,11 +524,11 @@ void ArcNotificationContentView::Layout() { // Scale notification surface if necessary. gfx::Transform transform; const gfx::Size surface_size = surface_->GetSize(); - const gfx::Size contents_size = contents_bounds.size(); - if (!surface_size.IsEmpty() && !contents_size.IsEmpty()) { - transform.Scale( - static_cast<float>(contents_size.width()) / surface_size.width(), - static_cast<float>(contents_size.height()) / surface_size.height()); + if (!surface_size.IsEmpty()) { + const float factor = + static_cast<float>(message_center::kNotificationWidth) / + surface_size.width(); + transform.Scale(factor, factor); } // Apply the transform to the surface content so that close button can @@ -642,7 +662,6 @@ void ArcNotificationContentView::OnItemDestroying() { void ArcNotificationContentView::OnItemUpdated() { UpdateAccessibleName(); - UpdatePinnedState(); UpdateSnapshot(); if (control_buttons_view_) { DCHECK(floating_control_buttons_widget_); diff --git a/chromium/ui/arc/notification/arc_notification_content_view.h b/chromium/ui/arc/notification/arc_notification_content_view.h index 79c396bb579..3e2706d91b6 100644 --- a/chromium/ui/arc/notification/arc_notification_content_view.h +++ b/chromium/ui/arc/notification/arc_notification_content_view.h @@ -66,11 +66,12 @@ class ArcNotificationContentView void SetSurface(ArcNotificationSurface* surface); void UpdatePreferredSize(); void UpdateControlButtonsVisibility(); - void UpdatePinnedState(); void UpdateSnapshot(); void AttachSurface(); void Activate(); void UpdateAccessibleName(); + void SetExpanded(bool expanded); + bool IsExpanded() const; // views::NativeViewHost void ViewHierarchyChanged( diff --git a/chromium/ui/arc/notification/arc_notification_content_view_delegate.h b/chromium/ui/arc/notification/arc_notification_content_view_delegate.h index dec7ee848ea..a27cad2a68b 100644 --- a/chromium/ui/arc/notification/arc_notification_content_view_delegate.h +++ b/chromium/ui/arc/notification/arc_notification_content_view_delegate.h @@ -22,6 +22,8 @@ class ArcNotificationContentViewDelegate { virtual void OnSlideChanged() = 0; virtual message_center::NotificationControlButtonsView* GetControlButtonsView() const = 0; + virtual bool IsExpanded() const = 0; + virtual void SetExpanded(bool expanded) = 0; }; } // namespace arc 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 3394b011a17..d8eddfc96e0 100644 --- a/chromium/ui/arc/notification/arc_notification_content_view_unittest.cc +++ b/chromium/ui/arc/notification/arc_notification_content_view_unittest.cc @@ -8,14 +8,27 @@ #include <string> #include <utility> +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" #include "base/strings/utf_string_conversions.h" +#include "components/exo/buffer.h" +#include "components/exo/keyboard.h" +#include "components/exo/keyboard_delegate.h" +#include "components/exo/notification_surface.h" +#include "components/exo/surface.h" +#include "components/exo/test/exo_test_helper.h" +#include "components/exo/wm_helper_ash.h" +#include "testing/gmock/include/gmock/gmock.h" #include "ui/arc/notification/arc_notification_content_view.h" #include "ui/arc/notification/arc_notification_delegate.h" #include "ui/arc/notification/arc_notification_item.h" #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/aura/test/test_window_delegate.h" #include "ui/aura/window.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/test/event_generator.h" #include "ui/message_center/notification.h" #include "ui/message_center/views/message_center_controller.h" #include "ui/message_center/views/message_view_factory.h" @@ -28,85 +41,60 @@ namespace arc { namespace { constexpr char kNotificationIdPrefix[] = "ARC_NOTIFICATION_"; +constexpr gfx::Rect kNotificationSurfaceBounds(100, 100, 300, 300); -class MockNotificationSurface : public ArcNotificationSurface { +class MockKeyboardDelegate : public exo::KeyboardDelegate { public: - MockNotificationSurface(const std::string& notification_key, - std::unique_ptr<aura::Window> window) - : notification_key_(notification_key), window_(std::move(window)) {} - - gfx::Size GetSize() const override { return gfx::Size(100, 200); } - - void Attach(views::NativeViewHost* nvh) override { - native_view_host_ = nvh; - nvh->Attach(window_.get()); - } - - void Detach() override { - EXPECT_TRUE(native_view_host_); - EXPECT_EQ(window_.get(), native_view_host_->native_view()); - native_view_host_->Detach(); - native_view_host_ = nullptr; - } - - bool IsAttached() const override { return native_view_host_; } - views::NativeViewHost* GetAttachedHost() const override { - return native_view_host_; - } - - aura::Window* GetWindow() const override { return window_.get(); } - aura::Window* GetContentWindow() const override { return window_.get(); } - - const std::string& GetNotificationKey() const override { - return notification_key_; - } - - private: - std::string notification_key_; - std::unique_ptr<aura::Window> window_; - views::NativeViewHost* native_view_host_ = nullptr; - - DISALLOW_COPY_AND_ASSIGN(MockNotificationSurface); + MockKeyboardDelegate() = default; + + // Overridden from KeyboardDelegate: + MOCK_METHOD1(OnKeyboardDestroying, void(exo::Keyboard*)); + MOCK_CONST_METHOD1(CanAcceptKeyboardEventsForSurface, bool(exo::Surface*)); + MOCK_METHOD2(OnKeyboardEnter, + void(exo::Surface*, const std::vector<ui::DomCode>&)); + MOCK_METHOD1(OnKeyboardLeave, void(exo::Surface*)); + MOCK_METHOD3(OnKeyboardKey, uint32_t(base::TimeTicks, ui::DomCode, bool)); + MOCK_METHOD1(OnKeyboardModifiers, void(int)); }; -class TestNotificationSurfaceManager : public ArcNotificationSurfaceManager { +class TestWMHelper : public exo::WMHelper { public: - TestNotificationSurfaceManager() = default; - - void PrepareSurface(std::string& notification_key) { - auto surface_window = base::MakeUnique<aura::Window>(&window_delegate_); - surface_window->SetType(aura::client::WINDOW_TYPE_CONTROL); - surface_window->Init(ui::LAYER_NOT_DRAWN); - surface_window->set_owned_by_parent(false); - surface_window->SetBounds(gfx::Rect(0, 0, 100, 200)); + TestWMHelper() = default; + ~TestWMHelper() override = default; - surface_map_[notification_key] = base::MakeUnique<MockNotificationSurface>( - notification_key, std::move(surface_window)); + const display::ManagedDisplayInfo& GetDisplayInfo( + int64_t display_id) const override { + static const display::ManagedDisplayInfo info; + return info; } - size_t surface_found_count() const { return surface_found_count_; } - - ArcNotificationSurface* GetArcSurface( - const std::string& notification_key) const override { - auto it = surface_map_.find(notification_key); - if (it != surface_map_.end()) { - ++surface_found_count_; - return it->second.get(); - } + aura::Window* GetPrimaryDisplayContainer(int container_id) override { return nullptr; } - void AddObserver(Observer* observer) override {} - void RemoveObserver(Observer* observer) override {} + aura::Window* GetActiveWindow() const override { return nullptr; } + aura::Window* GetFocusedWindow() const override { return nullptr; } + ui::CursorSize GetCursorSize() const override { + return ui::CursorSize::kNormal; + } + const display::Display& GetCursorDisplay() const override { + static const display::Display display; + return display; + } + void AddPreTargetHandler(ui::EventHandler* handler) override {} + void PrependPreTargetHandler(ui::EventHandler* handler) override {} + void RemovePreTargetHandler(ui::EventHandler* handler) override {} + void AddPostTargetHandler(ui::EventHandler* handler) override {} + void RemovePostTargetHandler(ui::EventHandler* handler) override {} + bool IsTabletModeWindowManagerEnabled() const override { return false; } private: - // Mutable for modifying in const method. - mutable int surface_found_count_ = 0; - - aura::test::TestWindowDelegate window_delegate_; - std::map<std::string, std::unique_ptr<ArcNotificationSurface>> surface_map_; - - DISALLOW_COPY_AND_ASSIGN(TestNotificationSurfaceManager); + DISALLOW_COPY_AND_ASSIGN(TestWMHelper); }; +aura::Window* GetFocusedWindow() { + DCHECK(exo::WMHelper::HasInstance()); + return exo::WMHelper::GetInstance()->GetFocusedWindow(); +} + } // anonymous namespace class MockArcNotificationItem : public ArcNotificationItem { @@ -141,7 +129,6 @@ class MockArcNotificationItem : public ArcNotificationItem { void RemoveObserver(Observer* observer) override {} void IncrementWindowRefCount() override {} void DecrementWindowRefCount() override {} - bool GetPinned() const override { return false; } bool IsOpeningSettingsSupported() const override { return true; } mojom::ArcNotificationExpandState GetExpandState() const override { return mojom::ArcNotificationExpandState::FIXED_SIZE; @@ -222,15 +209,19 @@ class DummyEvent : public ui::Event { ~DummyEvent() override = default; }; -class ArcNotificationContentViewTest : public views::ViewsTestBase { +class ArcNotificationContentViewTest : public ash::AshTestBase { public: ArcNotificationContentViewTest() = default; ~ArcNotificationContentViewTest() override = default; void SetUp() override { - views::ViewsTestBase::SetUp(); + ash::AshTestBase::SetUp(); + + wm_helper_ = base::MakeUnique<exo::WMHelperAsh>(); + exo::WMHelper::SetInstance(wm_helper_.get()); + DCHECK(exo::WMHelper::HasInstance()); - surface_manager_ = base::MakeUnique<TestNotificationSurfaceManager>(); + surface_manager_ = base::MakeUnique<ArcNotificationSurfaceManagerImpl>(); } void TearDown() override { @@ -238,9 +229,18 @@ class ArcNotificationContentViewTest : public views::ViewsTestBase { EXPECT_FALSE(wrapper_widget_); EXPECT_FALSE(notification_view_); + // These may have been initialized in PrepareSurface(). + notification_surface_.reset(); + surface_.reset(); + surface_buffer_.reset(); + surface_manager_.reset(); - views::ViewsTestBase::TearDown(); + DCHECK(exo::WMHelper::HasInstance()); + exo::WMHelper::SetInstance(nullptr); + wm_helper_.reset(); + + ash::AshTestBase::TearDown(); } void PressCloseButton() { @@ -248,8 +248,7 @@ class ArcNotificationContentViewTest : public views::ViewsTestBase { auto* control_buttons_view = GetArcNotificationContentView()->control_buttons_view_; ASSERT_TRUE(control_buttons_view); - message_center::PaddedButton* close_button = - control_buttons_view->close_button(); + views::Button* close_button = control_buttons_view->close_button(); ASSERT_NE(nullptr, close_button); control_buttons_view->ButtonPressed(close_button, dummy_event); } @@ -272,10 +271,10 @@ class ArcNotificationContentViewTest : public views::ViewsTestBase { message_center::MessageViewFactory::Create(controller(), notification, true))); notification_view->set_owned_by_client(); - views::Widget::InitParams params( - CreateParams(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 = base::MakeUnique<views::Widget>(); wrapper_widget->Init(params); wrapper_widget->SetContentsView(notification_view.get()); @@ -292,6 +291,20 @@ class ArcNotificationContentViewTest : public views::ViewsTestBase { notification_view_.reset(); } + void PrepareSurface(const std::string& notification_key) { + surface_ = base::MakeUnique<exo::Surface>(); + notification_surface_ = base::MakeUnique<exo::NotificationSurface>( + surface_manager(), surface_.get(), notification_key); + + exo::test::ExoTestHelper exo_test_helper; + surface_buffer_ = + base::MakeUnique<exo::Buffer>(exo_test_helper.CreateGpuMemoryBuffer( + kNotificationSurfaceBounds.size())); + surface_->Attach(surface_buffer_.get()); + + surface_->Commit(); + } + message_center::Notification CreateNotification( MockArcNotificationItem* notification_item) { return message_center::Notification( @@ -306,24 +319,40 @@ class ArcNotificationContentViewTest : public views::ViewsTestBase { } TestMessageCenterController* controller() { return &controller_; } - TestNotificationSurfaceManager* surface_manager() { + ArcNotificationSurfaceManagerImpl* surface_manager() { return surface_manager_.get(); } views::Widget* widget() { return notification_view_->GetWidget(); } + exo::Surface* surface() { return surface_.get(); } + ArcNotificationView* notification_view() { return notification_view_.get(); } + + message_center::NotificationControlButtonsView* GetControlButtonsView() + const { + DCHECK(GetArcNotificationContentView()); + DCHECK(GetArcNotificationContentView()->control_buttons_view_); + return GetArcNotificationContentView()->control_buttons_view_; + } + views::Widget* GetControlButtonsWidget() const { + DCHECK(GetControlButtonsView()->GetWidget()); + return GetControlButtonsView()->GetWidget(); + } - ArcNotificationContentView* GetArcNotificationContentView() { + ArcNotificationContentView* GetArcNotificationContentView() const { views::View* view = notification_view_->contents_view_; EXPECT_EQ(ArcNotificationContentView::kViewClassName, view->GetClassName()); return static_cast<ArcNotificationContentView*>(view); } - - TestNotificationSurfaceManager* surface_manager() const { - return surface_manager_.get(); + void ActivateArcNotification() { + GetArcNotificationContentView()->Activate(); } private: TestMessageCenterController controller_; - std::unique_ptr<TestNotificationSurfaceManager> surface_manager_; + std::unique_ptr<exo::WMHelper> wm_helper_; + std::unique_ptr<ArcNotificationSurfaceManagerImpl> surface_manager_; + std::unique_ptr<exo::Buffer> surface_buffer_; + std::unique_ptr<exo::Surface> surface_; + std::unique_ptr<exo::NotificationSurface> notification_surface_; std::unique_ptr<ArcNotificationView> notification_view_; std::unique_ptr<views::Widget> wrapper_widget_; @@ -338,7 +367,7 @@ TEST_F(ArcNotificationContentViewTest, CreateSurfaceAfterNotification) { message_center::Notification notification = CreateNotification(notification_item.get()); - surface_manager()->PrepareSurface(notification_key); + PrepareSurface(notification_key); CreateAndShowNotificationView(notification); CloseNotificationView(); @@ -347,7 +376,7 @@ TEST_F(ArcNotificationContentViewTest, CreateSurfaceAfterNotification) { TEST_F(ArcNotificationContentViewTest, CreateSurfaceBeforeNotification) { std::string notification_key("notification id"); - surface_manager()->PrepareSurface(notification_key); + PrepareSurface(notification_key); auto notification_item = base::MakeUnique<MockArcNotificationItem>(notification_key); @@ -375,12 +404,11 @@ TEST_F(ArcNotificationContentViewTest, CloseButton) { auto notification_item = base::MakeUnique<MockArcNotificationItem>(notification_key); - surface_manager()->PrepareSurface(notification_key); + PrepareSurface(notification_key); message_center::Notification notification = CreateNotification(notification_item.get()); CreateAndShowNotificationView(notification); - EXPECT_EQ(1u, surface_manager()->surface_found_count()); EXPECT_FALSE(controller()->IsRemoved(notification_item->GetNotificationId())); PressCloseButton(); EXPECT_TRUE(controller()->IsRemoved(notification_item->GetNotificationId())); @@ -396,7 +424,7 @@ TEST_F(ArcNotificationContentViewTest, ReuseSurfaceAfterClosing) { message_center::Notification notification = CreateNotification(notification_item.get()); - surface_manager()->PrepareSurface(notification_key); + PrepareSurface(notification_key); // Use the created surface. CreateAndShowNotificationView(notification); @@ -419,7 +447,7 @@ TEST_F(ArcNotificationContentViewTest, ReuseAndCloseSurfaceBeforeClosing) { message_center::Notification notification = CreateNotification(notification_item.get()); - surface_manager()->PrepareSurface(notification_key); + PrepareSurface(notification_key); // Create the first view. auto result = CreateNotificationView(notification); @@ -446,7 +474,7 @@ TEST_F(ArcNotificationContentViewTest, ReuseSurfaceBeforeClosing) { message_center::Notification notification = CreateNotification(notification_item.get()); - surface_manager()->PrepareSurface(notification_key); + PrepareSurface(notification_key); // Create the first view. auto result = CreateNotificationView(notification); @@ -466,4 +494,211 @@ TEST_F(ArcNotificationContentViewTest, ReuseSurfaceBeforeClosing) { CloseNotificationView(); } +TEST_F(ArcNotificationContentViewTest, Activate) { + std::string key("notification id"); + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + auto notification = CreateNotification(notification_item.get()); + + PrepareSurface(key); + CreateAndShowNotificationView(notification); + + EXPECT_FALSE(GetFocusedWindow()); + ActivateArcNotification(); + EXPECT_EQ(surface()->window(), GetFocusedWindow()); + + CloseNotificationView(); +} + +TEST_F(ArcNotificationContentViewTest, ActivateOnClick) { + std::string key("notification id"); + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + auto notification = CreateNotification(notification_item.get()); + + PrepareSurface(key); + CreateAndShowNotificationView(notification); + + EXPECT_FALSE(GetFocusedWindow()); + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(), + kNotificationSurfaceBounds.CenterPoint()); + generator.PressLeftButton(); + EXPECT_EQ(surface()->window(), GetFocusedWindow()); + + CloseNotificationView(); +} + +TEST_F(ArcNotificationContentViewTest, AcceptInputTextWithActivate) { + std::string key("notification id"); + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + auto notification = CreateNotification(notification_item.get()); + + PrepareSurface(key); + CreateAndShowNotificationView(notification); + + EXPECT_FALSE(GetFocusedWindow()); + ActivateArcNotification(); + EXPECT_EQ(surface()->window(), GetFocusedWindow()); + + MockKeyboardDelegate delegate; + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface())) + .WillOnce(testing::Return(true)); + auto keyboard = base::MakeUnique<exo::Keyboard>(&delegate); + + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, ui::DomCode::US_A, true)); + generator.PressKey(ui::VKEY_A, 0); + + keyboard.reset(); + + CloseNotificationView(); +} + +TEST_F(ArcNotificationContentViewTest, NotAcceptInputTextWithoutActivate) { + std::string key("notification id"); + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + auto notification = CreateNotification(notification_item.get()); + + PrepareSurface(key); + CreateAndShowNotificationView(notification); + EXPECT_FALSE(GetFocusedWindow()); + + MockKeyboardDelegate delegate; + EXPECT_CALL(delegate, CanAcceptKeyboardEventsForSurface(surface())).Times(0); + auto keyboard = base::MakeUnique<exo::Keyboard>(&delegate); + + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + EXPECT_CALL(delegate, OnKeyboardKey(testing::_, testing::_, testing::_)) + .Times(0); + generator.PressKey(ui::VKEY_A, 0); + + keyboard.reset(); + + CloseNotificationView(); +} + +TEST_F(ArcNotificationContentViewTest, TraversalFocus) { + const bool reverse = false; + + std::string key("notification id"); + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + PrepareSurface(key); + auto notification = CreateNotification(notification_item.get()); + CreateAndShowNotificationView(notification); + + views::FocusManager* focus_manager = notification_view()->GetFocusManager(); + + views::View* view = + focus_manager->GetNextFocusableView(nullptr, widget(), reverse, true); + EXPECT_EQ(GetArcNotificationContentView(), view); + + view = focus_manager->GetNextFocusableView(view, nullptr, reverse, true); + EXPECT_EQ(GetControlButtonsView()->settings_button(), view); + + view = focus_manager->GetNextFocusableView(view, nullptr, reverse, true); + EXPECT_EQ(GetControlButtonsView()->close_button(), view); + + view = focus_manager->GetNextFocusableView(view, nullptr, reverse, true); + EXPECT_EQ(nullptr, view); + + CloseNotificationView(); +} + +TEST_F(ArcNotificationContentViewTest, TraversalFocusReverse) { + const bool reverse = true; + + std::string key("notification id"); + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + PrepareSurface(key); + auto notification = CreateNotification(notification_item.get()); + CreateAndShowNotificationView(notification); + + views::FocusManager* focus_manager = notification_view()->GetFocusManager(); + + views::View* view = + focus_manager->GetNextFocusableView(nullptr, widget(), reverse, true); + EXPECT_EQ(GetControlButtonsView()->close_button(), view); + + view = focus_manager->GetNextFocusableView(view, nullptr, reverse, true); + EXPECT_EQ(GetControlButtonsView()->settings_button(), view); + + view = focus_manager->GetNextFocusableView(view, nullptr, reverse, true); + EXPECT_EQ(GetArcNotificationContentView(), view); + + view = focus_manager->GetNextFocusableView(view, nullptr, reverse, true); + EXPECT_EQ(nullptr, view); + + CloseNotificationView(); +} + +TEST_F(ArcNotificationContentViewTest, TraversalFocusByTabKey) { + const std::string key("notification id"); + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + PrepareSurface(key); + auto notification = CreateNotification(notification_item.get()); + CreateAndShowNotificationView(notification); + ActivateArcNotification(); + + views::FocusManager* focus_manager = notification_view()->GetFocusManager(); + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + + focus_manager->ClearFocus(); + EXPECT_FALSE(focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, 0); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetArcNotificationContentView(), focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, 0); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetControlButtonsView()->settings_button(), + focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, 0); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetControlButtonsView()->close_button(), + focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, 0); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetArcNotificationContentView(), focus_manager->GetFocusedView()); + + CloseNotificationView(); +} + +TEST_F(ArcNotificationContentViewTest, TraversalFocusReverseByShiftTab) { + std::string key("notification id"); + + auto notification_item = base::MakeUnique<MockArcNotificationItem>(key); + PrepareSurface(key); + auto notification = CreateNotification(notification_item.get()); + CreateAndShowNotificationView(notification); + ActivateArcNotification(); + + views::FocusManager* focus_manager = notification_view()->GetFocusManager(); + ui::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow()); + + focus_manager->ClearFocus(); + EXPECT_FALSE(focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetControlButtonsView()->close_button(), + focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetControlButtonsView()->settings_button(), + focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetArcNotificationContentView(), focus_manager->GetFocusedView()); + + generator.PressKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); + EXPECT_TRUE(focus_manager->GetFocusedView()); + EXPECT_EQ(GetControlButtonsView()->close_button(), + focus_manager->GetFocusedView()); + + CloseNotificationView(); +} + } // namespace arc diff --git a/chromium/ui/arc/notification/arc_notification_item.h b/chromium/ui/arc/notification/arc_notification_item.h index 4024b3d30d8..5605598b82d 100644 --- a/chromium/ui/arc/notification/arc_notification_item.h +++ b/chromium/ui/arc/notification/arc_notification_item.h @@ -65,8 +65,6 @@ class ArcNotificationItem { // is sent when |window_ref_count_| goes from one to zero. virtual void DecrementWindowRefCount() = 0; - // Returns the current pinned state. - virtual bool GetPinned() const = 0; // Returns the current snapshot. virtual const gfx::ImageSkia& GetSnapshot() const = 0; // Returns the current expand state. diff --git a/chromium/ui/arc/notification/arc_notification_item_impl.cc b/chromium/ui/arc/notification/arc_notification_item_impl.cc index b0edb55058f..0d93235a203 100644 --- a/chromium/ui/arc/notification/arc_notification_item_impl.cc +++ b/chromium/ui/arc/notification/arc_notification_item_impl.cc @@ -100,7 +100,6 @@ void ArcNotificationItemImpl::OnUpdatedFromAndroid( new ArcNotificationDelegate(weak_ptr_factory_.GetWeakPtr())); notification->set_timestamp(base::Time::FromJavaTime(data->time)); - pinned_ = rich_data.pinned; expand_state_ = data->expand_state; shown_contents_ = data->shown_contents; @@ -175,10 +174,6 @@ void ArcNotificationItemImpl::DecrementWindowRefCount() { manager_->CloseNotificationWindow(notification_key_); } -bool ArcNotificationItemImpl::GetPinned() const { - return pinned_; -} - const gfx::ImageSkia& ArcNotificationItemImpl::GetSnapshot() const { return snapshot_; } diff --git a/chromium/ui/arc/notification/arc_notification_item_impl.h b/chromium/ui/arc/notification/arc_notification_item_impl.h index 9f69b57bbd7..a90deb0a790 100644 --- a/chromium/ui/arc/notification/arc_notification_item_impl.h +++ b/chromium/ui/arc/notification/arc_notification_item_impl.h @@ -42,7 +42,6 @@ class ArcNotificationItemImpl : public ArcNotificationItem { void RemoveObserver(Observer* observer) override; void IncrementWindowRefCount() override; void DecrementWindowRefCount() override; - bool GetPinned() const override; const gfx::ImageSkia& GetSnapshot() const override; mojom::ArcNotificationExpandState GetExpandState() const override; mojom::ArcNotificationShownContents GetShownContents() const override; @@ -57,8 +56,6 @@ class ArcNotificationItemImpl : public ArcNotificationItem { ArcNotificationManager* const manager_; message_center::MessageCenter* const message_center_; - // The pinned state of the latest notification. - bool pinned_ = false; // The snapshot of the latest notification. gfx::ImageSkia snapshot_; // The expand state of the latest notification. diff --git a/chromium/ui/arc/notification/arc_notification_manager.cc b/chromium/ui/arc/notification/arc_notification_manager.cc index 4e560108a87..dbdfd1d6009 100644 --- a/chromium/ui/arc/notification/arc_notification_manager.cc +++ b/chromium/ui/arc/notification/arc_notification_manager.cc @@ -76,12 +76,7 @@ ArcNotificationManager::ArcNotificationManager( } ArcNotificationManager::~ArcNotificationManager() { - // TODO(hidehiko): Currently, the lifetime of ArcBridgeService and - // BrowserContextKeyedService is not nested. - // If ArcServiceManager::Get() returns nullptr, it is already destructed, - // so do not touch it. - if (ArcServiceManager::Get()) - arc_bridge_service_->notifications()->RemoveObserver(this); + arc_bridge_service_->notifications()->RemoveObserver(this); } void ArcNotificationManager::OnInstanceReady() { diff --git a/chromium/ui/arc/notification/arc_notification_surface_impl.cc b/chromium/ui/arc/notification/arc_notification_surface_impl.cc index 140da96ae93..964de60692c 100644 --- a/chromium/ui/arc/notification/arc_notification_surface_impl.cc +++ b/chromium/ui/arc/notification/arc_notification_surface_impl.cc @@ -8,17 +8,85 @@ #include "components/exo/notification_surface.h" #include "components/exo/surface.h" #include "ui/aura/window.h" +#include "ui/aura/window_delegate.h" +#include "ui/base/cursor/cursor.h" +#include "ui/base/hit_test.h" #include "ui/views/controls/native/native_view_host.h" +#include "ui/views/widget/widget.h" namespace arc { +namespace { + +class CustomWindowDelegate : public aura::WindowDelegate { + public: + explicit CustomWindowDelegate(exo::NotificationSurface* notification_surface) + : notification_surface_(notification_surface) {} + ~CustomWindowDelegate() override {} + + // Overridden from aura::WindowDelegate: + gfx::Size GetMinimumSize() const override { return gfx::Size(); } + gfx::Size GetMaximumSize() const override { return gfx::Size(); } + void OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) override {} + gfx::NativeCursor GetCursor(const gfx::Point& point) override { + return notification_surface_->GetCursor(point); + } + int GetNonClientComponent(const gfx::Point& point) const override { + return HTNOWHERE; + } + bool ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) override { + return true; + } + bool CanFocus() override { return true; } + void OnCaptureLost() override {} + void OnPaint(const ui::PaintContext& context) override {} + void OnDeviceScaleFactorChanged(float device_scale_factor) override {} + void OnWindowDestroying(aura::Window* window) override {} + void OnWindowDestroyed(aura::Window* window) override { delete this; } + void OnWindowTargetVisibilityChanged(bool visible) override {} + bool HasHitTestMask() const override { + return notification_surface_->HasHitTestMask(); + } + void GetHitTestMask(gfx::Path* mask) const override { + notification_surface_->GetHitTestMask(mask); + } + void OnKeyEvent(ui::KeyEvent* event) override { + // Propagates the key event upto the top-level views Widget so that we can + // trigger proper events in the views/ash level there. Event handling for + // Surfaces is done in a post event handler in keyboard.cc. + views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView( + notification_surface_->host_window()); + if (widget) + widget->OnKeyEvent(event); + } + + private: + exo::NotificationSurface* const notification_surface_; + + DISALLOW_COPY_AND_ASSIGN(CustomWindowDelegate); +}; + +} // namespace + // Handles notification surface role of a given surface. ArcNotificationSurfaceImpl::ArcNotificationSurfaceImpl( exo::NotificationSurface* surface) : surface_(surface) { DCHECK(surface); + native_view_ = + base::MakeUnique<aura::Window>(new CustomWindowDelegate(surface)); + native_view_->SetType(aura::client::WINDOW_TYPE_CONTROL); + native_view_->set_owned_by_parent(false); + native_view_->Init(ui::LAYER_NOT_DRAWN); + native_view_->AddChild(surface_->host_window()); + native_view_->Show(); } +ArcNotificationSurfaceImpl::~ArcNotificationSurfaceImpl() = default; + gfx::Size ArcNotificationSurfaceImpl::GetSize() const { return surface_->GetContentSize(); } @@ -28,12 +96,12 @@ void ArcNotificationSurfaceImpl::Attach( DCHECK(!native_view_host_); DCHECK(native_view_host); native_view_host_ = native_view_host; - native_view_host->Attach(surface_->host_window()); + native_view_host->Attach(native_view_.get()); } void ArcNotificationSurfaceImpl::Detach() { DCHECK(native_view_host_); - DCHECK_EQ(surface_->host_window(), native_view_host_->native_view()); + DCHECK_EQ(native_view_.get(), native_view_host_->native_view()); native_view_host_->Detach(); native_view_host_ = nullptr; } @@ -47,17 +115,12 @@ views::NativeViewHost* ArcNotificationSurfaceImpl::GetAttachedHost() const { } aura::Window* ArcNotificationSurfaceImpl::GetWindow() const { - return surface_->host_window(); + return native_view_.get(); } aura::Window* ArcNotificationSurfaceImpl::GetContentWindow() const { DCHECK(surface_->host_window()); - DCHECK(!surface_->host_window()->children().empty()); - - aura::Window* content_window = surface_->host_window()->children()[0]; - - DCHECK(!content_window); - return content_window; + return surface_->host_window(); } const std::string& ArcNotificationSurfaceImpl::GetNotificationKey() const { diff --git a/chromium/ui/arc/notification/arc_notification_surface_impl.h b/chromium/ui/arc/notification/arc_notification_surface_impl.h index 122c6d73fe0..3714b073e41 100644 --- a/chromium/ui/arc/notification/arc_notification_surface_impl.h +++ b/chromium/ui/arc/notification/arc_notification_surface_impl.h @@ -5,6 +5,8 @@ #ifndef UI_ARC_NOTIFICATION_ARC_NOTIFICATION_SURFACE_IMPL_H_ #define UI_ARC_NOTIFICATION_ARC_NOTIFICATION_SURFACE_IMPL_H_ +#include <memory> + #include "ui/arc/notification/arc_notification_surface.h" namespace exo { @@ -17,6 +19,7 @@ namespace arc { class ArcNotificationSurfaceImpl : public ArcNotificationSurface { public: explicit ArcNotificationSurfaceImpl(exo::NotificationSurface* surface); + ~ArcNotificationSurfaceImpl() override; // ArcNotificationSurface overrides: gfx::Size GetSize() const override; @@ -34,6 +37,7 @@ class ArcNotificationSurfaceImpl : public ArcNotificationSurface { private: exo::NotificationSurface* surface_; views::NativeViewHost* native_view_host_ = nullptr; + std::unique_ptr<aura::Window> native_view_; DISALLOW_COPY_AND_ASSIGN(ArcNotificationSurfaceImpl); }; diff --git a/chromium/ui/arc/notification/arc_notification_view.cc b/chromium/ui/arc/notification/arc_notification_view.cc index ef892e6ee82..85f8e818c6a 100644 --- a/chromium/ui/arc/notification/arc_notification_view.cc +++ b/chromium/ui/arc/notification/arc_notification_view.cc @@ -14,6 +14,7 @@ #include "ui/gfx/geometry/size.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/views/message_center_controller.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/controls/image_view.h" @@ -44,6 +45,8 @@ ArcNotificationView::ArcNotificationView( contents_view_->background()->get_color()); } + UpdateControlButtonsVisibilityWithNotification(notification); + focus_painter_ = views::Painter::CreateSolidFocusPainter( message_center::kFocusBorderColor, gfx::Insets(0, 1, 3, 2)); } @@ -58,6 +61,13 @@ void ArcNotificationView::OnContentBlured() { SchedulePaint(); } +void ArcNotificationView::UpdateWithNotification( + const message_center::Notification& notification) { + message_center::MessageView::UpdateWithNotification(notification); + + UpdateControlButtonsVisibilityWithNotification(notification); +} + void ArcNotificationView::SetDrawBackgroundAsActive(bool active) { // Do nothing if |contents_view_| has a background. if (contents_view_->background()) @@ -96,6 +106,17 @@ ArcNotificationView::GetControlButtonsView() const { return contents_view_delegate_->GetControlButtonsView(); } +bool ArcNotificationView::IsExpanded() const { + if (contents_view_delegate_) + return contents_view_delegate_->IsExpanded(); + return false; +} + +void ArcNotificationView::SetExpanded(bool expanded) { + if (contents_view_delegate_) + contents_view_delegate_->SetExpanded(expanded); +} + void ArcNotificationView::OnSlideChanged() { if (contents_view_delegate_) contents_view_delegate_->OnSlideChanged(); @@ -103,18 +124,18 @@ void ArcNotificationView::OnSlideChanged() { gfx::Size ArcNotificationView::CalculatePreferredSize() const { const gfx::Insets insets = GetInsets(); - const int contents_width = - message_center::kNotificationWidth - insets.width(); + const int contents_width = message_center::kNotificationWidth; const int contents_height = contents_view_->GetHeightForWidth(contents_width); - return gfx::Size(message_center::kNotificationWidth, + return gfx::Size(contents_width + insets.width(), contents_height + insets.height()); } void ArcNotificationView::Layout() { - message_center::MessageView::Layout(); - + // Setting the bounds before calling the parent to prevent double Layout. contents_view_->SetBoundsRect(GetContentsBounds()); + message_center::MessageView::Layout(); + // If the content view claims focus, defer focus handling to the content view. if (contents_view_->IsFocusable()) SetFocusBehavior(FocusBehavior::NEVER); @@ -176,4 +197,18 @@ bool ArcNotificationView::HandleAccessibleAction( return false; } +// 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.delegate() && + notification.delegate()->ShouldDisplaySettingsButton()); + 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 216361f2f67..7705c4172e0 100644 --- a/chromium/ui/arc/notification/arc_notification_view.h +++ b/chromium/ui/arc/notification/arc_notification_view.h @@ -36,6 +36,8 @@ class ArcNotificationView : public message_center::MessageView { void OnContentBlured(); // Overridden from MessageView: + void UpdateWithNotification( + const message_center::Notification& notification) override; void SetDrawBackgroundAsActive(bool active) override; bool IsCloseButtonFocused() const override; void RequestFocusOnCloseButton() override; @@ -43,6 +45,8 @@ class ArcNotificationView : public message_center::MessageView { void GetAccessibleNodeData(ui::AXNodeData* node_data) override; message_center::NotificationControlButtonsView* GetControlButtonsView() const override; + bool IsExpanded() const override; + void SetExpanded(bool expanded) override; // views::SlideOutController::Delegate: void OnSlideChanged() override; @@ -62,6 +66,10 @@ class ArcNotificationView : public message_center::MessageView { friend class ArcNotificationContentViewTest; friend class ArcNotificationViewTest; + // TODO(yoshiki): Mmove this to message_center::MessageView. + void UpdateControlButtonsVisibilityWithNotification( + const message_center::Notification& notification); + // The view for the custom content. Owned by view hierarchy. views::View* contents_view_ = nullptr; std::unique_ptr<ArcNotificationContentViewDelegate> contents_view_delegate_; diff --git a/chromium/ui/arc/notification/arc_notification_view_unittest.cc b/chromium/ui/arc/notification/arc_notification_view_unittest.cc index 2130c3d3ca1..dd8646b45c9 100644 --- a/chromium/ui/arc/notification/arc_notification_view_unittest.cc +++ b/chromium/ui/arc/notification/arc_notification_view_unittest.cc @@ -82,6 +82,8 @@ class TestContentViewDelegate : public ArcNotificationContentViewDelegate { const override { return nullptr; } + bool IsExpanded() const override { return false; } + void SetExpanded(bool expanded) override {} }; class TestNotificationDelegate : public message_center::NotificationDelegate { diff --git a/chromium/ui/aura/BUILD.gn b/chromium/ui/aura/BUILD.gn index 2d225353ac2..4bd8c3a3d2b 100644 --- a/chromium/ui/aura/BUILD.gn +++ b/chromium/ui/aura/BUILD.gn @@ -15,6 +15,7 @@ component("aura") { "client/cursor_client_observer.h", "client/default_capture_client.h", "client/drag_drop_client.h", + "client/drag_drop_client_observer.h", "client/drag_drop_delegate.h", "client/event_client.h", "client/focus_change_observer.h", @@ -43,6 +44,7 @@ component("aura") { "mus/focus_synchronizer.h", "mus/focus_synchronizer_delegate.h", "mus/focus_synchronizer_observer.h", + "mus/hit_test_data_provider_aura.h", "mus/in_flight_change.h", "mus/input_method_mus.h", "mus/mus_context_factory.h", @@ -72,7 +74,6 @@ component("aura") { "window_targeter.h", "window_tracker.h", "window_tree_host.h", - "window_tree_host_mac.h", "window_tree_host_observer.h", "window_tree_host_platform.h", ] @@ -105,6 +106,7 @@ component("aura") { "mus/client_surface_embedder.cc", "mus/drag_drop_controller_mus.cc", "mus/focus_synchronizer.cc", + "mus/hit_test_data_provider_aura.cc", "mus/in_flight_change.cc", "mus/input_method_mus.cc", "mus/mus_context_factory.cc", @@ -139,7 +141,6 @@ component("aura") { "//base:i18n", "//base/third_party/dynamic_annotations", "//cc", - "//cc/surfaces", "//components/discardable_memory/client", "//components/discardable_memory/public/interfaces", "//components/viz/client", @@ -335,6 +336,8 @@ executable("demo") { test("aura_unittests") { sources = [ "gestures/gesture_recognizer_unittest.cc", + "mus/drag_drop_controller_mus_unittest.cc", + "mus/hit_test_data_provider_aura_unittest.cc", "mus/input_method_mus_unittest.cc", "mus/os_exchange_data_provider_mus_unittest.cc", "mus/property_converter_unittest.cc", @@ -351,6 +354,7 @@ test("aura_unittests") { deps = [ ":test_support", "//base/test:test_support", + "//components/viz/client", "//mojo/common", "//mojo/edk/system", "//net", diff --git a/chromium/ui/aura/DEPS b/chromium/ui/aura/DEPS index cad39bb3678..10caa1bfa7f 100644 --- a/chromium/ui/aura/DEPS +++ b/chromium/ui/aura/DEPS @@ -1,7 +1,6 @@ include_rules = [ "+cc/base", "+cc/output", - "+cc/surfaces", "+components/viz/common", "+components/viz/host", "+mojo/common", diff --git a/chromium/ui/aura/client/aura_constants.cc b/chromium/ui/aura/client/aura_constants.cc index 2792820cd45..1fb0328e221 100644 --- a/chromium/ui/aura/client/aura_constants.cc +++ b/chromium/ui/aura/client/aura_constants.cc @@ -9,6 +9,7 @@ #include "ui/gfx/geometry/rect.h" DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, bool) +DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, base::TimeDelta) DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, base::string16*) DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, ui::ModalType) DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, gfx::ImageSkia*) @@ -24,6 +25,7 @@ DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, int64_t) DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, aura::client::WindowEmbedType) DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, aura::client::FocusClient*) +DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, aura::Window*) namespace aura { namespace client { @@ -50,6 +52,7 @@ DEFINE_UI_CLASS_PROPERTY_KEY(bool, kMirroringEnabledKey, false); DEFINE_UI_CLASS_PROPERTY_KEY(WindowEmbedType, kEmbedType, WindowEmbedType::NONE); +DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kChildModalParentKey, nullptr); DEFINE_UI_CLASS_PROPERTY_KEY(ui::ModalType, kModalKey, ui::MODAL_TYPE_NONE); DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kNameKey, nullptr); DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Size, kPreferredSize, nullptr); diff --git a/chromium/ui/aura/client/aura_constants.h b/chromium/ui/aura/client/aura_constants.h index 151777dbec1..c6d67043895 100644 --- a/chromium/ui/aura/client/aura_constants.h +++ b/chromium/ui/aura/client/aura_constants.h @@ -93,6 +93,9 @@ AURA_EXPORT extern const WindowProperty<gfx::Size*>* const kMinimumSize; // should render regardless of its actual visibility state. AURA_EXPORT extern const WindowProperty<bool>* const kMirroringEnabledKey; +// The modal parent of a child modal window. +AURA_EXPORT extern const WindowProperty<Window*>* const kChildModalParentKey; + // A property key to store the window modality. AURA_EXPORT extern const WindowProperty<ui::ModalType>* const kModalKey; diff --git a/chromium/ui/aura/client/drag_drop_client.h b/chromium/ui/aura/client/drag_drop_client.h index 4015f0f1a93..8e9634807d3 100644 --- a/chromium/ui/aura/client/drag_drop_client.h +++ b/chromium/ui/aura/client/drag_drop_client.h @@ -21,6 +21,8 @@ namespace aura { class Window; namespace client { +class DragDropClientObserver; + // An interface implemented by an object that controls a drag and drop session. class AURA_EXPORT DragDropClient { public: @@ -28,7 +30,8 @@ class AURA_EXPORT DragDropClient { // Initiates a drag and drop session. Returns the drag operation that was // applied at the end of the drag drop session. |screen_location| is in - // screen coordinates. + // screen coordinates. At most one drag and drop operation is allowed. + // It must not start drag operation while |IsDragDropInProgress| returns true. virtual int StartDragAndDrop(const ui::OSExchangeData& data, aura::Window* root_window, aura::Window* source_window, @@ -41,6 +44,9 @@ class AURA_EXPORT DragDropClient { // Returns true if a drag and drop session is in progress. virtual bool IsDragDropInProgress() = 0; + + virtual void AddObserver(DragDropClientObserver* observer) = 0; + virtual void RemoveObserver(DragDropClientObserver* observer) = 0; }; AURA_EXPORT void SetDragDropClient(Window* root_window, diff --git a/chromium/ui/aura/client/drag_drop_client_observer.h b/chromium/ui/aura/client/drag_drop_client_observer.h new file mode 100644 index 00000000000..e285050b360 --- /dev/null +++ b/chromium/ui/aura/client/drag_drop_client_observer.h @@ -0,0 +1,28 @@ +// 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_AURA_CLIENT_DRAG_DROP_CLIENT_OBSERVER_H_ +#define UI_AURA_CLIENT_DRAG_DROP_CLIENT_OBSERVER_H_ + +#include "ui/aura/aura_export.h" + +namespace aura { +namespace client { + +class AURA_EXPORT DragDropClientObserver { + public: + // Called when dragging started. + virtual void OnDragStarted() = 0; + + // Called when dragging ended. + virtual void OnDragEnded() = 0; + + protected: + virtual ~DragDropClientObserver() = default; +}; + +} // namespace client +} // namespace aura + +#endif // UI_AURA_CLIENT_DRAG_DROP_CLIENT_OBSERVER_H_ diff --git a/chromium/ui/aura/env.cc b/chromium/ui/aura/env.cc index 4493d687a08..5c040ed88b2 100644 --- a/chromium/ui/aura/env.cc +++ b/chromium/ui/aura/env.cc @@ -174,7 +174,13 @@ void Env::Init() { // The ozone platform can provide its own event source. So initialize the // platform before creating the default event source. If running inside mus // let the mus process initialize ozone instead. - ui::OzonePlatform::InitializeForUI(); + ui::OzonePlatform::InitParams params; + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + // TODO(kylechar): Pass in single process information to Env::CreateInstance() + // instead of checking flags here. + params.single_process = command_line->HasSwitch("single-process") || + command_line->HasSwitch("in-process-gpu"); + ui::OzonePlatform::InitializeForUI(params); gfx::ClientNativePixmapFactory::SetInstance(native_pixmap_factory_.get()); #endif if (!ui::PlatformEventSource::GetInstance()) diff --git a/chromium/ui/aura/env_input_state_controller.h b/chromium/ui/aura/env_input_state_controller.h index 688774c6769..4ceb29e4db5 100644 --- a/chromium/ui/aura/env_input_state_controller.h +++ b/chromium/ui/aura/env_input_state_controller.h @@ -20,6 +20,9 @@ class TouchEvent; } namespace aura { +namespace test { +class EnvTestHelper; +} class Window; @@ -35,6 +38,7 @@ class AURA_EXPORT EnvInputStateController { const gfx::Point& location_in_root) const; private: + friend class test::EnvTestHelper; // Touch ids that are currently down. uint32_t touch_ids_down_; diff --git a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc index 8d4bf832546..4294c4fafbd 100644 --- a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc +++ b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc @@ -1700,8 +1700,8 @@ TEST_F(GestureRecognizerTest, GestureTapFollowedByScroll) { EXPECT_TRUE(delegate->tap_cancel()); EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); - EXPECT_EQ(15, delegate->scroll_x_hint()); - EXPECT_EQ(15, delegate->scroll_y_hint()); + EXPECT_EQ(10, delegate->scroll_x_hint()); + EXPECT_EQ(10, delegate->scroll_y_hint()); delegate->Reset(); @@ -3709,7 +3709,7 @@ TEST_F(GestureRecognizerTest, NoDriftInScroll) { EXPECT_TRUE(delegate->scroll_update()); // 3 px consumed by touch slop region. EXPECT_EQ(-1, delegate->scroll_y()); - EXPECT_EQ(-4, delegate->scroll_y_hint()); + EXPECT_EQ(-1, delegate->scroll_y_hint()); delegate->Reset(); @@ -4096,7 +4096,7 @@ TEST_F(GestureRecognizerTest, TestExceedingSlopSlowly) { EXPECT_TRUE(delegate->scroll_begin()); EXPECT_TRUE(delegate->scroll_update()); EXPECT_NEAR(0.1, delegate->scroll_x(), 0.0001); - EXPECT_FLOAT_EQ(3.1f, delegate->scroll_x_hint()); + EXPECT_NEAR(0.1, delegate->scroll_x_hint(), 0.0001); delegate->Reset(); ui::TouchEvent move4( diff --git a/chromium/ui/aura/local/DEPS b/chromium/ui/aura/local/DEPS index c0790cb5a17..152d4ee35af 100644 --- a/chromium/ui/aura/local/DEPS +++ b/chromium/ui/aura/local/DEPS @@ -1,7 +1,6 @@ include_rules = [ "+cc/output", "+cc/scheduler", - "+cc/surfaces", "+components/viz/service/frame_sinks", ] diff --git a/chromium/ui/aura/local/layer_tree_frame_sink_local.cc b/chromium/ui/aura/local/layer_tree_frame_sink_local.cc index d87672e5e63..d535bcb4f65 100644 --- a/chromium/ui/aura/local/layer_tree_frame_sink_local.cc +++ b/chromium/ui/aura/local/layer_tree_frame_sink_local.cc @@ -5,6 +5,7 @@ #include "ui/aura/local/layer_tree_frame_sink_local.h" #include "cc/output/layer_tree_frame_sink_client.h" +#include "components/viz/common/surfaces/surface_info.h" #include "components/viz/host/host_frame_sink_manager.h" #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h" #include "ui/aura/client/cursor_client.h" @@ -21,9 +22,14 @@ LayerTreeFrameSinkLocal::LayerTreeFrameSinkLocal( viz::HostFrameSinkManager* host_frame_sink_manager) : cc::LayerTreeFrameSink(nullptr, nullptr, nullptr, nullptr), frame_sink_id_(frame_sink_id), - host_frame_sink_manager_(host_frame_sink_manager) {} + host_frame_sink_manager_(host_frame_sink_manager), + weak_factory_(this) { + host_frame_sink_manager_->RegisterFrameSinkId(frame_sink_id_, this); +} -LayerTreeFrameSinkLocal::~LayerTreeFrameSinkLocal() {} +LayerTreeFrameSinkLocal::~LayerTreeFrameSinkLocal() { + host_frame_sink_manager_->InvalidateFrameSinkId(frame_sink_id_); +} bool LayerTreeFrameSinkLocal::BindToClient( cc::LayerTreeFrameSinkClient* client) { @@ -34,9 +40,8 @@ bool LayerTreeFrameSinkLocal::BindToClient( support_ = host_frame_sink_manager_->CreateCompositorFrameSinkSupport( this, frame_sink_id_, false /* is_root */, - true /* handles_frame_sink_id_invalidation */, true /* needs_sync_points */); - begin_frame_source_ = base::MakeUnique<cc::ExternalBeginFrameSource>(this); + begin_frame_source_ = base::MakeUnique<viz::ExternalBeginFrameSource>(this); client->SetBeginFrameSource(begin_frame_source_.get()); return true; } @@ -47,6 +52,10 @@ void LayerTreeFrameSinkLocal::SetSurfaceChangedCallback( surface_changed_callback_ = callback; } +base::WeakPtr<LayerTreeFrameSinkLocal> LayerTreeFrameSinkLocal::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + void LayerTreeFrameSinkLocal::DetachFromClient() { DCHECK(thread_checker_); DCHECK(thread_checker_->CalledOnValidThread()); @@ -58,42 +67,37 @@ void LayerTreeFrameSinkLocal::DetachFromClient() { cc::LayerTreeFrameSink::DetachFromClient(); } +void LayerTreeFrameSinkLocal::SetLocalSurfaceId( + const viz::LocalSurfaceId& local_surface_id) { + DCHECK(local_surface_id.is_valid()); + local_surface_id_ = local_surface_id; +} + void LayerTreeFrameSinkLocal::SubmitCompositorFrame(cc::CompositorFrame frame) { DCHECK(thread_checker_); DCHECK(thread_checker_->CalledOnValidThread()); DCHECK(frame.metadata.begin_frame_ack.has_damage); - DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber, + DCHECK_LE(viz::BeginFrameArgs::kStartingFrameNumber, frame.metadata.begin_frame_ack.sequence_number); - viz::LocalSurfaceId old_local_surface_id = local_surface_id_; - const auto& frame_size = frame.render_pass_list.back()->output_rect.size(); - if (frame_size != surface_size_ || - frame.metadata.device_scale_factor != device_scale_factor_ || - !local_surface_id_.is_valid()) { - surface_size_ = frame_size; - device_scale_factor_ = frame.metadata.device_scale_factor; - local_surface_id_ = id_allocator_.GenerateId(); - } + DCHECK(local_surface_id_.is_valid()); + bool result = support_->SubmitCompositorFrame(local_surface_id_, std::move(frame)); DCHECK(result); - - if (local_surface_id_ != old_local_surface_id) { - surface_changed_callback_.Run( - viz::SurfaceId(frame_sink_id_, local_surface_id_), surface_size_); - } } -void LayerTreeFrameSinkLocal::DidNotProduceFrame(const cc::BeginFrameAck& ack) { +void LayerTreeFrameSinkLocal::DidNotProduceFrame( + const viz::BeginFrameAck& ack) { DCHECK(thread_checker_); DCHECK(thread_checker_->CalledOnValidThread()); DCHECK(!ack.has_damage); - DCHECK_LE(cc::BeginFrameArgs::kStartingFrameNumber, ack.sequence_number); + DCHECK_LE(viz::BeginFrameArgs::kStartingFrameNumber, ack.sequence_number); support_->DidNotProduceFrame(ack); } void LayerTreeFrameSinkLocal::DidReceiveCompositorFrameAck( - const std::vector<cc::ReturnedResource>& resources) { + const std::vector<viz::ReturnedResource>& resources) { DCHECK(thread_checker_); DCHECK(thread_checker_->CalledOnValidThread()); if (!client_) @@ -103,7 +107,7 @@ void LayerTreeFrameSinkLocal::DidReceiveCompositorFrameAck( client_->DidReceiveCompositorFrameAck(); } -void LayerTreeFrameSinkLocal::OnBeginFrame(const cc::BeginFrameArgs& args) { +void LayerTreeFrameSinkLocal::OnBeginFrame(const viz::BeginFrameArgs& args) { DCHECK(thread_checker_); DCHECK(thread_checker_->CalledOnValidThread()); begin_frame_source_->OnBeginFrame(args); @@ -116,7 +120,7 @@ void LayerTreeFrameSinkLocal::OnBeginFramePausedChanged(bool paused) { } void LayerTreeFrameSinkLocal::ReclaimResources( - const std::vector<cc::ReturnedResource>& resources) { + const std::vector<viz::ReturnedResource>& resources) { DCHECK(thread_checker_); DCHECK(thread_checker_->CalledOnValidThread()); if (!client_) @@ -130,4 +134,9 @@ void LayerTreeFrameSinkLocal::OnNeedsBeginFrames(bool needs_begin_frames) { support_->SetNeedsBeginFrame(needs_begin_frames); } +void LayerTreeFrameSinkLocal::OnFirstSurfaceActivation( + const viz::SurfaceInfo& surface_info) { + surface_changed_callback_.Run(surface_info); +} + } // namespace aura diff --git a/chromium/ui/aura/local/layer_tree_frame_sink_local.h b/chromium/ui/aura/local/layer_tree_frame_sink_local.h index ab074b03a8c..116d30e478c 100644 --- a/chromium/ui/aura/local/layer_tree_frame_sink_local.h +++ b/chromium/ui/aura/local/layer_tree_frame_sink_local.h @@ -7,10 +7,11 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "cc/output/layer_tree_frame_sink.h" -#include "cc/scheduler/begin_frame_source.h" +#include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/surfaces/frame_sink_id.h" -#include "components/viz/common/surfaces/local_surface_id_allocator.h" +#include "components/viz/host/host_frame_sink_client.h" #include "components/viz/service/frame_sinks/compositor_frame_sink_support_client.h" #include "ui/aura/window_port.h" #include "ui/base/property_data.h" @@ -28,47 +29,53 @@ namespace aura { // aura::Window's ui::Layer. class LayerTreeFrameSinkLocal : public cc::LayerTreeFrameSink, public viz::CompositorFrameSinkSupportClient, - public cc::ExternalBeginFrameSourceClient { + public viz::ExternalBeginFrameSourceClient, + public viz::HostFrameSinkClient { public: LayerTreeFrameSinkLocal(const viz::FrameSinkId& frame_sink_id, viz::HostFrameSinkManager* host_frame_sink_manager); ~LayerTreeFrameSinkLocal() override; - using SurfaceChangedCallback = - base::Callback<void(const viz::SurfaceId&, const gfx::Size&)>; + using SurfaceChangedCallback = base::Callback<void(const viz::SurfaceInfo&)>; + // Set a callback which will be called when the surface is changed. void SetSurfaceChangedCallback(const SurfaceChangedCallback& callback); + base::WeakPtr<LayerTreeFrameSinkLocal> GetWeakPtr(); + // cc::LayerTreeFrameSink: bool BindToClient(cc::LayerTreeFrameSinkClient* client) override; void DetachFromClient() override; + void SetLocalSurfaceId(const viz::LocalSurfaceId& local_surface_id) override; void SubmitCompositorFrame(cc::CompositorFrame frame) override; - void DidNotProduceFrame(const cc::BeginFrameAck& ack) override; + void DidNotProduceFrame(const viz::BeginFrameAck& ack) override; // viz::CompositorFrameSinkSupportClient: void DidReceiveCompositorFrameAck( - const std::vector<cc::ReturnedResource>& resources) override; - void OnBeginFrame(const cc::BeginFrameArgs& args) override; + const std::vector<viz::ReturnedResource>& resources) override; + void OnBeginFrame(const viz::BeginFrameArgs& args) override; void ReclaimResources( - const std::vector<cc::ReturnedResource>& resources) override; + const std::vector<viz::ReturnedResource>& resources) override; void WillDrawSurface(const viz::LocalSurfaceId& local_surface_id, const gfx::Rect& damage_rect) override {} void OnBeginFramePausedChanged(bool paused) override; - // cc::ExternalBeginFrameSourceClient: + // viz::ExternalBeginFrameSourceClient: void OnNeedsBeginFrames(bool needs_begin_frames) override; private: + // public viz::HostFrameSinkClient: + void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override; + const viz::FrameSinkId frame_sink_id_; viz::HostFrameSinkManager* const host_frame_sink_manager_; std::unique_ptr<viz::CompositorFrameSinkSupport> support_; gfx::Size surface_size_; - float device_scale_factor_ = 0; - viz::LocalSurfaceIdAllocator id_allocator_; viz::LocalSurfaceId local_surface_id_; - std::unique_ptr<cc::ExternalBeginFrameSource> begin_frame_source_; + std::unique_ptr<viz::ExternalBeginFrameSource> begin_frame_source_; std::unique_ptr<base::ThreadChecker> thread_checker_; SurfaceChangedCallback surface_changed_callback_; + base::WeakPtrFactory<LayerTreeFrameSinkLocal> weak_factory_; DISALLOW_COPY_AND_ASSIGN(LayerTreeFrameSinkLocal); }; diff --git a/chromium/ui/aura/local/window_port_local.cc b/chromium/ui/aura/local/window_port_local.cc index 2ed5ca8134a..fc60d26d5c8 100644 --- a/chromium/ui/aura/local/window_port_local.cc +++ b/chromium/ui/aura/local/window_port_local.cc @@ -10,6 +10,7 @@ #include "ui/aura/local/layer_tree_frame_sink_local.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" +#include "ui/base/layout.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -65,6 +66,14 @@ WindowPortLocal::~WindowPortLocal() {} void WindowPortLocal::OnPreInit(Window* window) {} void WindowPortLocal::OnDeviceScaleFactorChanged(float device_scale_factor) { + if (last_device_scale_factor_ != device_scale_factor && + local_surface_id_.is_valid()) { + last_device_scale_factor_ = device_scale_factor; + local_surface_id_ = local_surface_id_allocator_.GenerateId(); + if (frame_sink_) + frame_sink_->SetLocalSurfaceId(local_surface_id_); + } + ScopedCursorHider hider(window_); if (window_->delegate()) window_->delegate()->OnDeviceScaleFactorChanged(device_scale_factor); @@ -80,7 +89,14 @@ void WindowPortLocal::OnWillMoveChild(size_t current_index, size_t dest_index) { void WindowPortLocal::OnVisibilityChanged(bool visible) {} void WindowPortLocal::OnDidChangeBounds(const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) {} + const gfx::Rect& new_bounds) { + if (last_size_ != new_bounds.size() && local_surface_id_.is_valid()) { + last_size_ = new_bounds.size(); + local_surface_id_ = local_surface_id_allocator_.GenerateId(); + if (frame_sink_) + frame_sink_->SetLocalSurfaceId(local_surface_id_); + } +} void WindowPortLocal::OnDidChangeTransform( const gfx::Transform& old_transform, @@ -106,6 +122,9 @@ WindowPortLocal::CreateLayerTreeFrameSink() { frame_sink_id_, context_factory_private->GetHostFrameSinkManager()); frame_sink->SetSurfaceChangedCallback(base::Bind( &WindowPortLocal::OnSurfaceChanged, weak_factory_.GetWeakPtr())); + frame_sink_ = frame_sink->GetWeakPtr(); + local_surface_id_ = local_surface_id_allocator_.GenerateId(); + frame_sink->SetLocalSurfaceId(local_surface_id_); if (window_->GetRootWindow()) window_->layer()->GetCompositor()->AddFrameSink(frame_sink_id_); return std::move(frame_sink); @@ -115,6 +134,25 @@ viz::SurfaceId WindowPortLocal::GetSurfaceId() const { return viz::SurfaceId(frame_sink_id_, local_surface_id_); } +void WindowPortLocal::AllocateLocalSurfaceId() { + local_surface_id_ = local_surface_id_allocator_.GenerateId(); + if (frame_sink_) + frame_sink_->SetLocalSurfaceId(local_surface_id_); +} + +const viz::LocalSurfaceId& WindowPortLocal::GetLocalSurfaceId() { + if (!local_surface_id_.is_valid()) { + last_device_scale_factor_ = ui::GetScaleFactorForNativeView(window_); + last_size_ = window_->bounds().size(); + local_surface_id_ = local_surface_id_allocator_.GenerateId(); + } + return local_surface_id_; +} + +viz::FrameSinkId WindowPortLocal::GetFrameSinkId() const { + return frame_sink_id_; +} + void WindowPortLocal::OnWindowAddedToRootWindow() { if (frame_sink_id_.is_valid()) window_->layer()->GetCompositor()->AddFrameSink(frame_sink_id_); @@ -125,11 +163,11 @@ void WindowPortLocal::OnWillRemoveWindowFromRootWindow() { window_->layer()->GetCompositor()->RemoveFrameSink(frame_sink_id_); } -void WindowPortLocal::OnSurfaceChanged(const viz::SurfaceId& surface_id, - const gfx::Size& surface_size) { - DCHECK_EQ(surface_id.frame_sink_id(), frame_sink_id_); - local_surface_id_ = surface_id.local_surface_id(); - viz::SurfaceInfo surface_info(surface_id, 1.0f, surface_size); +void WindowPortLocal::OnEventTargetingPolicyChanged() {} + +void WindowPortLocal::OnSurfaceChanged(const viz::SurfaceInfo& surface_info) { + DCHECK_EQ(surface_info.id().frame_sink_id(), frame_sink_id_); + DCHECK_EQ(surface_info.id().local_surface_id(), local_surface_id_); scoped_refptr<viz::SurfaceReferenceFactory> reference_factory = aura::Env::GetInstance() ->context_factory_private() diff --git a/chromium/ui/aura/local/window_port_local.h b/chromium/ui/aura/local/window_port_local.h index 7ab9ea1f0eb..68a29a2fc4a 100644 --- a/chromium/ui/aura/local/window_port_local.h +++ b/chromium/ui/aura/local/window_port_local.h @@ -8,8 +8,11 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "components/viz/common/surfaces/frame_sink_id.h" +#include "components/viz/common/surfaces/local_surface_id_allocator.h" +#include "ui/aura/local/layer_tree_frame_sink_local.h" #include "ui/aura/window_port.h" #include "ui/base/property_data.h" +#include "ui/gfx/geometry/size.h" namespace gfx { class Size; @@ -43,16 +46,23 @@ class AURA_EXPORT WindowPortLocal : public WindowPort { std::unique_ptr<ui::PropertyData> data) override; std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink() override; viz::SurfaceId GetSurfaceId() const override; + void AllocateLocalSurfaceId() override; + const viz::LocalSurfaceId& GetLocalSurfaceId() override; + viz::FrameSinkId GetFrameSinkId() const override; void OnWindowAddedToRootWindow() override; void OnWillRemoveWindowFromRootWindow() override; + void OnEventTargetingPolicyChanged() override; private: - void OnSurfaceChanged(const viz::SurfaceId& surface_id, - const gfx::Size& surface_size); + void OnSurfaceChanged(const viz::SurfaceInfo& surface_info); Window* const window_; viz::FrameSinkId frame_sink_id_; + gfx::Size last_size_; + float last_device_scale_factor_ = 1.0f; viz::LocalSurfaceId local_surface_id_; + viz::LocalSurfaceIdAllocator local_surface_id_allocator_; + base::WeakPtr<aura::LayerTreeFrameSinkLocal> frame_sink_; base::WeakPtrFactory<WindowPortLocal> weak_factory_; diff --git a/chromium/ui/aura/mus/DEPS b/chromium/ui/aura/mus/DEPS index 2f08e4b543d..1f379237e61 100644 --- a/chromium/ui/aura/mus/DEPS +++ b/chromium/ui/aura/mus/DEPS @@ -4,23 +4,15 @@ include_rules = [ "+cc/output/layer_tree_frame_sink_client.h", "+cc/output/layer_tree_frame_sink.h", "+cc/scheduler/begin_frame_source.h", - "+cc/surfaces/local_surface_id.h", - "+cc/surfaces/local_surface_id_allocator.h", - "+cc/surfaces/surface_info.h", - "+cc/surfaces/surface_manager.h", - "+cc/surfaces/surface_reference_factory.h", "+components/discardable_memory/client/client_discardable_shared_memory_manager.h", "+components/viz/client", - "+components/viz/common/gpu", + "+components/viz/common", "+gpu/command_buffer/client/gpu_memory_buffer_manager.h", "+gpu/ipc/client/gpu_channel_host.h", "+mojo/public/cpp/system/buffer.h", "+mojo/public/cpp/system/platform_handle.h", "+services/ui/common/accelerator_util.h", "+services/ui/common/task_runner_test_base.h", - "+services/ui/public/cpp/gpu", - "+services/ui/public/cpp/property_type_converters.h", - "+services/ui/public/cpp/raster_thread_helper.h", - "+services/ui/public/cpp/client_layer_tree_frame_sink.h", + "+services/ui/public", "+ui/gl/gl_bindings.h", ] diff --git a/chromium/ui/aura/mus/client_surface_embedder.cc b/chromium/ui/aura/mus/client_surface_embedder.cc index cf439ce2375..e14dcd5254d 100644 --- a/chromium/ui/aura/mus/client_surface_embedder.cc +++ b/chromium/ui/aura/mus/client_surface_embedder.cc @@ -5,7 +5,7 @@ #include "ui/aura/mus/client_surface_embedder.h" #include "base/memory/ptr_util.h" -#include "cc/surfaces/stub_surface_reference_factory.h" +#include "components/viz/common/surfaces/stub_surface_reference_factory.h" #include "ui/aura/window.h" #include "ui/gfx/geometry/dip_util.h" @@ -27,7 +27,7 @@ ClientSurfaceEmbedder::ClientSurfaceEmbedder( // this is the case with window decorations provided by Window Manager. // This content should appear underneath the content of the embedded client. window_->layer()->StackAtTop(surface_layer_.get()); - ref_factory_ = new cc::StubSurfaceReferenceFactory(); + ref_factory_ = new viz::StubSurfaceReferenceFactory(); } ClientSurfaceEmbedder::~ClientSurfaceEmbedder() = default; diff --git a/chromium/ui/aura/mus/drag_drop_controller_mus.cc b/chromium/ui/aura/mus/drag_drop_controller_mus.cc index 0d64f7b0258..fea4fc0554f 100644 --- a/chromium/ui/aura/mus/drag_drop_controller_mus.cc +++ b/chromium/ui/aura/mus/drag_drop_controller_mus.cc @@ -15,7 +15,9 @@ #include "mojo/public/cpp/bindings/map.h" #include "services/ui/public/interfaces/window_tree.mojom.h" #include "services/ui/public/interfaces/window_tree_constants.mojom.h" +#include "ui/aura/client/drag_drop_client_observer.h" #include "ui/aura/client/drag_drop_delegate.h" +#include "ui/aura/env.h" #include "ui/aura/mus/drag_drop_controller_host.h" #include "ui/aura/mus/mus_types.h" #include "ui/aura/mus/os_exchange_data_provider_mus.h" @@ -116,8 +118,11 @@ uint32_t DragDropControllerMus::OnCompleteDrop( void DragDropControllerMus::OnPerformDragDropCompleted(uint32_t action_taken) { DCHECK(current_drag_state_); + for (client::DragDropClientObserver& observer : observers_) + observer.OnDragEnded(); current_drag_state_->completed_action = action_taken; current_drag_state_->runloop_quit_closure.Run(); + current_drag_state_ = nullptr; } void DragDropControllerMus::OnDragDropDone() { @@ -140,8 +145,10 @@ int DragDropControllerMus::StartDragAndDrop( CurrentDragState current_drag_state = {root_window_mus->server_id(), change_id, ui::mojom::kDropEffectNone, data, run_loop.QuitClosure()}; - base::AutoReset<CurrentDragState*> resetter(¤t_drag_state_, - ¤t_drag_state); + + // current_drag_state_ will be reset in |OnPerformDragDropCompleted| before + // run_loop.Run() quits. + current_drag_state_ = ¤t_drag_state; base::MessageLoop* loop = base::MessageLoop::current(); base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); @@ -155,6 +162,10 @@ int DragDropControllerMus::StartDragAndDrop( std::map<std::string, std::vector<uint8_t>> drag_data = static_cast<const aura::OSExchangeDataProviderMus&>(data.provider()) .GetData(); + + for (client::DragDropClientObserver& observer : observers_) + observer.OnDragStarted(); + window_tree_->PerformDragDrop( change_id, root_window_mus->server_id(), screen_location, mojo::MapToUnorderedMap(drag_data), @@ -162,7 +173,6 @@ int DragDropControllerMus::StartDragAndDrop( data.provider().GetDragImageOffset(), drag_operations, mojo_source); run_loop.Run(); - return current_drag_state.completed_action; } @@ -176,6 +186,16 @@ bool DragDropControllerMus::IsDragDropInProgress() { return current_drag_state_ != nullptr; } +void DragDropControllerMus::AddObserver( + client::DragDropClientObserver* observer) { + observers_.AddObserver(observer); +} + +void DragDropControllerMus::RemoveObserver( + client::DragDropClientObserver* observer) { + observers_.RemoveObserver(observer); +} + uint32_t DragDropControllerMus::HandleDragEnterOrOver( WindowMus* window, uint32_t event_flags, diff --git a/chromium/ui/aura/mus/drag_drop_controller_mus.h b/chromium/ui/aura/mus/drag_drop_controller_mus.h index 92700dcb992..e161339ded1 100644 --- a/chromium/ui/aura/mus/drag_drop_controller_mus.h +++ b/chromium/ui/aura/mus/drag_drop_controller_mus.h @@ -11,6 +11,8 @@ #include <memory> #include <vector> +#include "base/observer_list.h" +#include "ui/aura/aura_export.h" #include "ui/aura/client/drag_drop_client.h" #include "ui/aura/window_tracker.h" #include "ui/base/dragdrop/drag_drop_types.h" @@ -32,7 +34,7 @@ class WindowMus; // DragDropControllerMus acts as the DragDropClient for aura as well as // handling all drag operations from the server. Drag operations are forwarded // to the client::DragDropDelegate set on the target Window. -class DragDropControllerMus : public client::DragDropClient { +class AURA_EXPORT DragDropControllerMus : public client::DragDropClient { public: DragDropControllerMus(DragDropControllerHost* drag_drop_controller_host, ui::mojom::WindowTree* window_tree); @@ -70,6 +72,8 @@ class DragDropControllerMus : public client::DragDropClient { ui::DragDropTypes::DragEventSource source) override; void DragCancel() override; bool IsDragDropInProgress() override; + void AddObserver(client::DragDropClientObserver* observer) override; + void RemoveObserver(client::DragDropClientObserver* observer) override; private: struct CurrentDragState; @@ -105,6 +109,8 @@ class DragDropControllerMus : public client::DragDropClient { // Used to track the current drop target. WindowTracker drop_target_window_tracker_; + base::ObserverList<client::DragDropClientObserver> observers_; + DISALLOW_COPY_AND_ASSIGN(DragDropControllerMus); }; diff --git a/chromium/ui/aura/mus/drag_drop_controller_mus_unittest.cc b/chromium/ui/aura/mus/drag_drop_controller_mus_unittest.cc new file mode 100644 index 00000000000..55964c70bd8 --- /dev/null +++ b/chromium/ui/aura/mus/drag_drop_controller_mus_unittest.cc @@ -0,0 +1,170 @@ +// 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/aura/mus/drag_drop_controller_mus.h" + +#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" +#include "ui/aura/client/drag_drop_client_observer.h" +#include "ui/aura/client/drag_drop_delegate.h" +#include "ui/aura/mus/drag_drop_controller_host.h" +#include "ui/aura/mus/window_mus.h" +#include "ui/aura/test/aura_mus_test_base.h" +#include "ui/aura/test/mus/test_window_tree.h" +#include "ui/base/dragdrop/drop_target_event.h" +#include "ui/events/event_utils.h" + +namespace aura { +namespace { + +class DragDropControllerMusTest : public test::AuraMusWmTestBase { + public: + DragDropControllerMusTest() = default; + + // test::AuraMusWmTestBase + void SetUp() override { + AuraMusWmTestBase::SetUp(); + controller_ = base::MakeUnique<DragDropControllerMus>(&controller_host_, + window_tree()); + window_ = std::unique_ptr<aura::Window>( + CreateNormalWindow(0, root_window(), nullptr)); + } + + void TearDown() override { + window_.reset(); + controller_.reset(); + AuraMusWmTestBase::TearDown(); + } + + protected: + void PostDragMoveAndDrop() { + // Posted task will be run when the inner loop runs in StartDragAndDrop. + ASSERT_TRUE(base::ThreadTaskRunnerHandle::IsSet()); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(&DragDropControllerMusTest::DragMoveAndDrop, + base::Unretained(this))); + } + + void StartDragAndDrop() { + controller_->StartDragAndDrop( + ui::OSExchangeData(), window_->GetRootWindow(), window_.get(), + gfx::Point(5, 5), ui::DragDropTypes::DRAG_MOVE, + ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE); + } + + std::unique_ptr<DragDropControllerMus> controller_; + std::unique_ptr<aura::Window> window_; + + private: + void DragMoveAndDrop() { + WindowMus* const window_mus = WindowMus::Get(window_.get()); + controller_->OnDragEnter(window_mus, 0, gfx::Point(5, 20), 0); + controller_->OnDragOver(window_mus, 0, gfx::Point(5, 20), 0); + controller_->OnCompleteDrop(window_mus, 0, gfx::Point(5, 20), 0); + controller_->OnPerformDragDropCompleted(0); + } + + class TestDragDropControllerHost : public DragDropControllerHost { + public: + TestDragDropControllerHost() : serial_(0u) {} + + // DragDropControllerHost + uint32_t CreateChangeIdForDrag(WindowMus* window) override { + return serial_++; + } + + private: + uint32_t serial_; + + } controller_host_; + + DISALLOW_COPY_AND_ASSIGN(DragDropControllerMusTest); +}; + +TEST_F(DragDropControllerMusTest, DragStartedAndEndedEvents) { + enum class State { kNotInvoked, kDragStartInvoked, kDragEndInvoked }; + + class TestObserver : public client::DragDropClientObserver { + public: + TestObserver() = default; + State state() const { return state_; } + + // Overrides from client::DragDropClientObserver: + void OnDragStarted() override { + EXPECT_EQ(State::kNotInvoked, state_); + state_ = State::kDragStartInvoked; + } + void OnDragEnded() override { + EXPECT_EQ(State::kDragStartInvoked, state_); + state_ = State::kDragEndInvoked; + } + + private: + State state_{State::kNotInvoked}; + + DISALLOW_COPY_AND_ASSIGN(TestObserver); + } observer; + + controller_->AddObserver(&observer); + PostDragMoveAndDrop(); + StartDragAndDrop(); + EXPECT_EQ(State::kDragEndInvoked, observer.state()); + controller_->RemoveObserver(&observer); +} + +TEST_F(DragDropControllerMusTest, EventTarget) { + enum class State { + kNotInvoked, + kDragEnteredInvoked, + kDragUpdateInvoked, + kPerformDropInvoked + }; + + class TestDelegate : public client::DragDropDelegate { + public: + TestDelegate(aura::Window* window) : window_(window) {} + State state() const { return state_; } + + // Overrides from client::DragDropClientObserver: + void OnDragEntered(const ui::DropTargetEvent& event) override { + EXPECT_EQ(State::kNotInvoked, state_); + EXPECT_EQ(window_, event.target()); + state_ = State::kDragEnteredInvoked; + } + int OnDragUpdated(const ui::DropTargetEvent& event) override { + EXPECT_TRUE(State::kDragEnteredInvoked == state_ || + State::kDragUpdateInvoked == state_); + EXPECT_EQ(window_, event.target()); + state_ = State::kDragUpdateInvoked; + return ui::DragDropTypes::DRAG_MOVE; + } + void OnDragExited() override { ADD_FAILURE(); } + int OnPerformDrop(const ui::DropTargetEvent& event) override { + EXPECT_EQ(State::kDragUpdateInvoked, state_); + EXPECT_EQ(window_, event.target()); + state_ = State::kPerformDropInvoked; + return ui::DragDropTypes::DRAG_MOVE; + } + + private: + aura::Window* const window_; + State state_{State::kNotInvoked}; + + DISALLOW_COPY_AND_ASSIGN(TestDelegate); + } delegate(window_.get()); + + client::SetDragDropDelegate(window_.get(), &delegate); + PostDragMoveAndDrop(); + StartDragAndDrop(); + EXPECT_EQ(State::kPerformDropInvoked, delegate.state()); + client::SetDragDropDelegate(window_.get(), nullptr); +} + +} // namespace +} // namespace aura diff --git a/chromium/ui/aura/mus/hit_test_data_provider_aura.cc b/chromium/ui/aura/mus/hit_test_data_provider_aura.cc new file mode 100644 index 00000000000..db915077e08 --- /dev/null +++ b/chromium/ui/aura/mus/hit_test_data_provider_aura.cc @@ -0,0 +1,133 @@ +// 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/aura/mus/hit_test_data_provider_aura.h" + +#include "base/containers/adapters.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" +#include "ui/aura/window.h" +#include "ui/aura/window_targeter.h" + +namespace { + +viz::mojom::HitTestRegionPtr CreateHitTestRegion(const aura::Window* window, + uint32_t flags, + const gfx::Rect& rect) { + const ui::Layer* layer = window->layer(); + DCHECK(layer); + + auto hit_test_region = viz::mojom::HitTestRegion::New(); + DCHECK(window->GetFrameSinkId().is_valid()); + hit_test_region->frame_sink_id = window->GetFrameSinkId(); + if (layer->GetPrimarySurfaceInfo()) { + DCHECK(window->GetFrameSinkId() == + layer->GetPrimarySurfaceInfo()->id().frame_sink_id()); + hit_test_region->local_surface_id = + layer->GetPrimarySurfaceInfo()->id().local_surface_id(); + } + hit_test_region->flags = flags; + hit_test_region->rect = rect; + hit_test_region->transform = layer->transform(); + + return hit_test_region; +} + +} // namespace + +namespace aura { + +HitTestDataProviderAura::HitTestDataProviderAura(aura::Window* window) + : window_(window) {} + +HitTestDataProviderAura::~HitTestDataProviderAura() {} + +viz::mojom::HitTestRegionListPtr HitTestDataProviderAura::GetHitTestData() + const { + const ui::mojom::EventTargetingPolicy event_targeting_policy = + window_->event_targeting_policy(); + if (event_targeting_policy == ui::mojom::EventTargetingPolicy::NONE) + return nullptr; + + auto hit_test_region_list = viz::mojom::HitTestRegionList::New(); + hit_test_region_list->flags = + event_targeting_policy == + ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY + ? viz::mojom::kHitTestIgnore + : viz::mojom::kHitTestMine; + hit_test_region_list->bounds = window_->bounds(); + + GetHitTestDataRecursively(window_, hit_test_region_list.get()); + return hit_test_region_list; +} + +void HitTestDataProviderAura::GetHitTestDataRecursively( + aura::Window* window, + viz::mojom::HitTestRegionList* hit_test_region_list) const { + WindowTargeter* targeter = + static_cast<WindowTargeter*>(window->GetEventTargeter()); + + // TODO(varkha): Figure out if we need to add hit-test regions for |window|. + // Walk the children in Z-order (reversed order of children()) to produce + // the hit-test data. Each child's hit test data is added before the hit-test + // data from the child's descendants because the child could clip its + // descendants for the purpose of event handling. + for (aura::Window* child : base::Reversed(window->children())) { + const ui::mojom::EventTargetingPolicy event_targeting_policy = + child->event_targeting_policy(); + if (event_targeting_policy == ui::mojom::EventTargetingPolicy::NONE) + continue; + if (event_targeting_policy != + ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY) { + gfx::Rect rect_mouse(child->bounds()); + gfx::Rect rect_touch; + bool touch_and_mouse_are_same = true; + uint32_t flags = child->layer()->GetPrimarySurfaceInfo() + ? viz::mojom::kHitTestChildSurface + : viz::mojom::kHitTestMine; + // Use the |targeter| to query for possibly expanded hit-test area. + // Use the |child| bounds with mouse and touch flags when there is no + // |targeter|. + if (targeter && + targeter->GetHitTestRects(child, &rect_mouse, &rect_touch)) { + touch_and_mouse_are_same = rect_mouse == rect_touch; + } + + auto shape_rects = + targeter ? targeter->GetExtraHitTestShapeRects(child) : nullptr; + if (shape_rects) { + // The |child| has a complex shape. Clip it to |rect_mouse|. + const gfx::Vector2d offset = child->bounds().OffsetFromOrigin(); + for (const gfx::Rect& shape_rect : *shape_rects) { + gfx::Rect rect = shape_rect; + rect.Offset(offset); + rect.Intersect(rect_mouse); + if (rect.IsEmpty()) + continue; + hit_test_region_list->regions.push_back(CreateHitTestRegion( + child, + flags | viz::mojom::kHitTestMouse | viz::mojom::kHitTestTouch, + rect)); + } + } else { + // The |child| has possibly same mouse and touch hit-test areas. + if (!rect_mouse.IsEmpty()) { + hit_test_region_list->regions.push_back(CreateHitTestRegion( + child, + flags | (touch_and_mouse_are_same ? (viz::mojom::kHitTestMouse | + viz::mojom::kHitTestTouch) + : viz::mojom::kHitTestMouse), + rect_mouse)); + } + if (!touch_and_mouse_are_same && !rect_touch.IsEmpty()) { + hit_test_region_list->regions.push_back(CreateHitTestRegion( + child, flags | viz::mojom::kHitTestTouch, rect_touch)); + } + } + } + if (event_targeting_policy != ui::mojom::EventTargetingPolicy::TARGET_ONLY) + GetHitTestDataRecursively(child, hit_test_region_list); + } +} + +} // namespace aura diff --git a/chromium/ui/aura/mus/hit_test_data_provider_aura.h b/chromium/ui/aura/mus/hit_test_data_provider_aura.h new file mode 100644 index 00000000000..04b7748b494 --- /dev/null +++ b/chromium/ui/aura/mus/hit_test_data_provider_aura.h @@ -0,0 +1,41 @@ +// 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_AURA_MUS_HIT_TEST_DATA_PROVIDER_AURA_H_ +#define UI_AURA_MUS_HIT_TEST_DATA_PROVIDER_AURA_H_ + +#include "base/macros.h" +#include "components/viz/client/hit_test_data_provider.h" + +namespace aura { + +class Window; + +// A HitTestDataProvider that captures hit-test areas from a aura::Window tree +// and packages it to be submitted to compositor frame sink. The |window| used +// when creating the HitTestDataProviderAura should outlive the data provider. +class HitTestDataProviderAura : public viz::HitTestDataProvider { + public: + explicit HitTestDataProviderAura(Window* window); + ~HitTestDataProviderAura() override; + + // HitTestDataProvider: + viz::mojom::HitTestRegionListPtr GetHitTestData() const override; + + private: + // Recursively walks the children of |window| and uses |window|'s + // EventTargeter to generate hit-test data for the |window|'s descendants. + // Populates |hit_test_region_list|. + void GetHitTestDataRecursively( + aura::Window* window, + viz::mojom::HitTestRegionList* hit_test_region_list) const; + + aura::Window* const window_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(HitTestDataProviderAura); +}; + +} // namespace aura + +#endif // UI_AURA_MUS_HIT_TEST_DATA_PROVIDER_AURA_H_
\ No newline at end of file diff --git a/chromium/ui/aura/mus/hit_test_data_provider_aura_unittest.cc b/chromium/ui/aura/mus/hit_test_data_provider_aura_unittest.cc new file mode 100644 index 00000000000..5f8611fe1b2 --- /dev/null +++ b/chromium/ui/aura/mus/hit_test_data_provider_aura_unittest.cc @@ -0,0 +1,290 @@ +// 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/aura/mus/window_tree_client.h" + +#include "components/viz/client/hit_test_data_provider.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/mus/window_port_mus.h" +#include "ui/aura/test/aura_mus_test_base.h" +#include "ui/aura/window.h" +#include "ui/aura/window_targeter.h" +#include "ui/gfx/geometry/rect.h" + +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. +// ---------------------- +// | hit hit | +// | ---------- | +// | | | | +// | | No hit | hit | +// | | | | +// | hit | | | +// | ---------- | +// | hit hit | +// ---------------------- +class TestHoleWindowTargeter : public aura::WindowTargeter { + public: + TestHoleWindowTargeter() = default; + ~TestHoleWindowTargeter() override {} + + private: + // aura::WindowTargeter: + std::unique_ptr<aura::WindowTargeter::HitTestRects> GetExtraHitTestShapeRects( + aura::Window* target) const override { + gfx::Rect bounds = target->bounds(); + int x0 = 0; + int x1 = bounds.width() / 3; + int x2 = bounds.width() - bounds.width() / 3; + int x3 = bounds.width(); + int y0 = 0; + int y1 = bounds.height() / 3; + int y2 = bounds.height() - bounds.height() / 3; + int y3 = bounds.height(); + auto shape_rects = base::MakeUnique<aura::WindowTargeter::HitTestRects>(); + shape_rects->emplace_back(x0, y0, bounds.width(), y1 - y0); + shape_rects->emplace_back(x0, y1, x1 - x0, y2 - y1); + shape_rects->emplace_back(x2, y1, x3 - x2, y2 - y1); + shape_rects->emplace_back(x0, y2, bounds.width(), y3 - y2); + return shape_rects; + } + + DISALLOW_COPY_AND_ASSIGN(TestHoleWindowTargeter); +}; + +} // namespace + +// Creates a root window and child windows. Maintains a cc:LayerTreeFrameSink +// to help exercise its viz::HitTestDataProvider. +class HitTestDataProviderAuraTest : public test::AuraTestBaseMus { + public: + HitTestDataProviderAuraTest() {} + ~HitTestDataProviderAuraTest() override {} + + void SetUp() override { + test::AuraTestBaseMus::SetUp(); + + root_ = base::MakeUnique<Window>(nullptr); + root_->Init(ui::LAYER_NOT_DRAWN); + root_->SetEventTargeter(base::MakeUnique<WindowTargeter>()); + root_->SetBounds(gfx::Rect(0, 0, 300, 200)); + + window2_ = new Window(nullptr); + window2_->Init(ui::LAYER_TEXTURED); + window2_->SetBounds(gfx::Rect(20, 30, 40, 60)); + + window3_ = new Window(nullptr); + window3_->Init(ui::LAYER_TEXTURED); + window3_->SetEventTargeter(base::MakeUnique<WindowTargeter>()); + window3_->SetBounds(gfx::Rect(50, 60, 100, 40)); + + window4_ = new Window(nullptr); + window4_->Init(ui::LAYER_TEXTURED); + window4_->SetBounds(gfx::Rect(20, 10, 60, 30)); + + window3_->AddChild(window4_); + root_->AddChild(window2_); + root_->AddChild(window3_); + + WindowPort* port = WindowPortMus::Get(root_.get()); + sink_ = port->CreateLayerTreeFrameSink(); + } + + protected: + const viz::HitTestDataProvider* hit_test_data_provider() const { + // TODO(varkha): Find a way to get the HitTestDataProvider without depending + // on WindowPortMus + WindowPortMus* port = WindowPortMus::Get(root_.get()); + return port->local_layer_tree_frame_sink_->hit_test_data_provider(); + } + + Window* root() { return root_.get(); } + Window* window2() { return window2_; } + Window* window3() { return window3_; } + Window* window4() { return window4_; } + + private: + std::unique_ptr<cc::LayerTreeFrameSink> sink_; + std::unique_ptr<Window> root_; + Window* window2_; + Window* window3_; + Window* window4_; + + DISALLOW_COPY_AND_ASSIGN(HitTestDataProviderAuraTest); +}; + +// TODO(riajiang): Add test cases for kHitTestChildSurface to ensure +// that local_surface_id is set and used correctly. + +// Tests that the order of reported hit-test regions matches windows Z-order. +TEST_F(HitTestDataProviderAuraTest, Stacking) { + const auto hit_test_data_1 = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data_1); + EXPECT_EQ(hit_test_data_1->flags, viz::mojom::kHitTestMine); + EXPECT_EQ(hit_test_data_1->bounds, root()->bounds()); + Window* expected_order_1[] = {window3(), window4(), window2()}; + EXPECT_EQ(hit_test_data_1->regions.size(), arraysize(expected_order_1)); + int i = 0; + for (const auto& region : hit_test_data_1->regions) { + EXPECT_EQ(region->flags, viz::mojom::kHitTestMine | + viz::mojom::kHitTestMouse | + viz::mojom::kHitTestTouch); + EXPECT_EQ(region->frame_sink_id, expected_order_1[i]->GetFrameSinkId()); + EXPECT_EQ(region->rect.ToString(), + expected_order_1[i]->bounds().ToString()); + i++; + } + + root()->StackChildAbove(window2(), window3()); + const auto hit_test_data_2 = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data_2); + EXPECT_EQ(hit_test_data_2->flags, viz::mojom::kHitTestMine); + EXPECT_EQ(hit_test_data_2->bounds, root()->bounds()); + + Window* expected_order_2[] = {window2(), window3(), window4()}; + EXPECT_EQ(hit_test_data_2->regions.size(), arraysize(expected_order_2)); + i = 0; + for (const auto& region : hit_test_data_2->regions) { + EXPECT_EQ(region->flags, viz::mojom::kHitTestMine | + viz::mojom::kHitTestMouse | + viz::mojom::kHitTestTouch); + EXPECT_EQ(region->frame_sink_id, expected_order_2[i]->GetFrameSinkId()); + EXPECT_EQ(region->rect.ToString(), + expected_order_2[i]->bounds().ToString()); + i++; + } +} + +// Tests that the hit-test regions get expanded with a custom event targeter. +TEST_F(HitTestDataProviderAuraTest, CustomTargeter) { + window3()->SetEventTargeter(base::MakeUnique<TestWindowTargeter>()); + const auto hit_test_data = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data); + EXPECT_EQ(hit_test_data->flags, viz::mojom::kHitTestMine); + EXPECT_EQ(hit_test_data->bounds, root()->bounds()); + + // Children of a container that has the custom targeter installed will get + // reported twice, once with hit-test bounds optimized for mouse events and + // another time with bounds expanded more for touch input. + Window* expected_windows[] = {window3(), window4(), window4(), window2()}; + uint32_t expected_flags[] = { + viz::mojom::kHitTestMine | viz::mojom::kHitTestMouse | + viz::mojom::kHitTestTouch, + viz::mojom::kHitTestMine | viz::mojom::kHitTestMouse, + viz::mojom::kHitTestMine | viz::mojom::kHitTestTouch, + viz::mojom::kHitTestMine | viz::mojom::kHitTestMouse | + viz::mojom::kHitTestTouch}; + int expected_insets[] = {0, kMouseInset, kTouchInset, 0}; + ASSERT_EQ(hit_test_data->regions.size(), arraysize(expected_windows)); + ASSERT_EQ(hit_test_data->regions.size(), arraysize(expected_flags)); + ASSERT_EQ(hit_test_data->regions.size(), arraysize(expected_insets)); + int i = 0; + for (const auto& region : hit_test_data->regions) { + EXPECT_EQ(region->frame_sink_id, expected_windows[i]->GetFrameSinkId()); + EXPECT_EQ(region->flags, expected_flags[i]); + gfx::Rect expected_bounds = expected_windows[i]->bounds(); + expected_bounds.Inset(gfx::Insets(expected_insets[i])); + EXPECT_EQ(region->rect.ToString(), expected_bounds.ToString()); + i++; + } +} + +// Tests that the complex hit-test shape can be set with a custom targeter. +TEST_F(HitTestDataProviderAuraTest, HoleTargeter) { + window3()->SetEventTargeter(base::MakeUnique<TestHoleWindowTargeter>()); + const auto hit_test_data = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data); + EXPECT_EQ(hit_test_data->flags, viz::mojom::kHitTestMine); + EXPECT_EQ(hit_test_data->bounds, root()->bounds()); + + // Children of a container that has the custom targeter installed will get + // reported 4 times for each of the hit test regions defined by the custom + // targeter. + Window* expected_windows[] = {window3(), window4(), window4(), + window4(), window4(), window2()}; + uint32_t expected_flags = viz::mojom::kHitTestMine | + viz::mojom::kHitTestMouse | + viz::mojom::kHitTestTouch; + std::vector<gfx::Rect> expected_bounds; + expected_bounds.push_back(window3()->bounds()); + + // original window4 is at gfx::Rect(20, 10, 60, 30). + expected_bounds.emplace_back(20, 10, 60, 10); + expected_bounds.emplace_back(20, 20, 20, 10); + expected_bounds.emplace_back(60, 20, 20, 10); + expected_bounds.emplace_back(20, 30, 60, 10); + + expected_bounds.push_back(window2()->bounds()); + + ASSERT_EQ(hit_test_data->regions.size(), arraysize(expected_windows)); + ASSERT_EQ(hit_test_data->regions.size(), expected_bounds.size()); + int i = 0; + for (const auto& region : hit_test_data->regions) { + EXPECT_EQ(region->frame_sink_id, expected_windows[i]->GetFrameSinkId()); + EXPECT_EQ(region->flags, expected_flags); + EXPECT_EQ(region->rect.ToString(), expected_bounds[i].ToString()); + i++; + } +} + +TEST_F(HitTestDataProviderAuraTest, TargetingPolicies) { + root()->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); + auto hit_test_data = hit_test_data_provider()->GetHitTestData(); + ASSERT_FALSE(hit_test_data); + + root()->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::TARGET_ONLY); + window3()->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); + hit_test_data = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data); + EXPECT_EQ(hit_test_data->flags, viz::mojom::kHitTestMine); + EXPECT_EQ(hit_test_data->regions.size(), 3U); + + root()->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::TARGET_ONLY); + window3()->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_ONLY); + hit_test_data = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data); + EXPECT_EQ(hit_test_data->flags, viz::mojom::kHitTestMine); + EXPECT_EQ(hit_test_data->regions.size(), 2U); + + root()->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY); + window3()->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY); + hit_test_data = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data); + EXPECT_EQ(hit_test_data->flags, viz::mojom::kHitTestIgnore); + EXPECT_EQ(hit_test_data->regions.size(), 2U); + + root()->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); + window3()->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); + hit_test_data = hit_test_data_provider()->GetHitTestData(); + ASSERT_TRUE(hit_test_data); + EXPECT_EQ(hit_test_data->flags, viz::mojom::kHitTestMine); + EXPECT_EQ(hit_test_data->regions.size(), 3U); +} + +} // namespace aura diff --git a/chromium/ui/aura/mus/in_flight_change.h b/chromium/ui/aura/mus/in_flight_change.h index 40b3dcbcc6c..0422d2f20be 100644 --- a/chromium/ui/aura/mus/in_flight_change.h +++ b/chromium/ui/aura/mus/in_flight_change.h @@ -41,6 +41,7 @@ enum class ChangeType { ADD_TRANSIENT_WINDOW, BOUNDS, CAPTURE, + CHILD_MODAL_PARENT, DELETE_WINDOW, DRAG_LOOP, FOCUS, diff --git a/chromium/ui/aura/mus/mus_context_factory.cc b/chromium/ui/aura/mus/mus_context_factory.cc index ed99ef60c9f..6cab93e4813 100644 --- a/chromium/ui/aura/mus/mus_context_factory.cc +++ b/chromium/ui/aura/mus/mus_context_factory.cc @@ -8,11 +8,11 @@ #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" #include "services/ui/public/cpp/gpu/gpu.h" #include "ui/aura/mus/window_port_mus.h" #include "ui/aura/window_tree_host.h" #include "ui/compositor/compositor_switches.h" -#include "ui/compositor/compositor_util.h" #include "ui/display/display_switches.h" #include "ui/gfx/switches.h" #include "ui/gl/gl_bindings.h" @@ -39,8 +39,8 @@ viz::BufferToTextureTargetMap CreateBufferToTextureTargetMap() { MusContextFactory::MusContextFactory(ui::Gpu* gpu) : gpu_(gpu), - renderer_settings_( - ui::CreateRendererSettings(CreateBufferToTextureTargetMap())), + resource_settings_( + viz::CreateResourceSettings(CreateBufferToTextureTargetMap())), weak_ptr_factory_(this) {} MusContextFactory::~MusContextFactory() {} @@ -104,7 +104,7 @@ cc::TaskGraphRunner* MusContextFactory::GetTaskGraphRunner() { } const viz::ResourceSettings& MusContextFactory::GetResourceSettings() const { - return renderer_settings_.resource_settings; + return resource_settings_; } } // namespace aura diff --git a/chromium/ui/aura/mus/mus_context_factory.h b/chromium/ui/aura/mus/mus_context_factory.h index 3634f7b4c0c..ab1b1950f62 100644 --- a/chromium/ui/aura/mus/mus_context_factory.h +++ b/chromium/ui/aura/mus/mus_context_factory.h @@ -10,7 +10,6 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "cc/surfaces/surface_manager.h" #include "components/viz/common/display/renderer_settings.h" #include "components/viz/common/gpu/context_provider.h" #include "services/ui/public/cpp/raster_thread_helper.h" @@ -58,7 +57,7 @@ class AURA_EXPORT MusContextFactory : public ui::ContextFactory { ui::RasterThreadHelper raster_thread_helper_; ui::Gpu* gpu_; - const viz::RendererSettings renderer_settings_; + const viz::ResourceSettings resource_settings_; scoped_refptr<viz::ContextProvider> shared_main_thread_context_provider_; base::WeakPtrFactory<MusContextFactory> weak_ptr_factory_; diff --git a/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc b/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc index 04e7539f04a..cfdf82b7ef0 100644 --- a/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc +++ b/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc @@ -251,7 +251,7 @@ bool OSExchangeDataProviderMus::HasCustomFormat( // These methods were added in an ad-hoc way to different operating // systems. We need to support them until they get cleaned up. -#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN) +#if defined(USE_X11) || defined(OS_WIN) void OSExchangeDataProviderMus::SetFileContents( const base::FilePath& filename, const std::string& file_contents) {} diff --git a/chromium/ui/aura/mus/os_exchange_data_provider_mus.h b/chromium/ui/aura/mus/os_exchange_data_provider_mus.h index 777da5ac293..27a541950dd 100644 --- a/chromium/ui/aura/mus/os_exchange_data_provider_mus.h +++ b/chromium/ui/aura/mus/os_exchange_data_provider_mus.h @@ -68,7 +68,7 @@ class AURA_EXPORT OSExchangeDataProviderMus // per operating system. Now we have to have at least two providers per OS, // leading to the following warts, which will remain until we clean all the // callsites up. -#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN) +#if defined(USE_X11) || defined(OS_WIN) void SetFileContents(const base::FilePath& filename, const std::string& file_contents) override; #endif diff --git a/chromium/ui/aura/mus/user_activity_forwarder.h b/chromium/ui/aura/mus/user_activity_forwarder.h index d1890424b88..58d28529ed1 100644 --- a/chromium/ui/aura/mus/user_activity_forwarder.h +++ b/chromium/ui/aura/mus/user_activity_forwarder.h @@ -24,7 +24,7 @@ namespace aura { // downstream of ui::UserActivityDetector) instead observe UserActivityMonitor // directly: http://crbug.com/626899 class AURA_EXPORT UserActivityForwarder - : NON_EXPORTED_BASE(public ui::mojom::UserActivityObserver) { + : public ui::mojom::UserActivityObserver { public: UserActivityForwarder(ui::mojom::UserActivityMonitorPtr monitor, ui::UserActivityDetector* detector); diff --git a/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc b/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc index dc2fd59f428..9d011c0e723 100644 --- a/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc +++ b/chromium/ui/aura/mus/user_activity_forwarder_unittest.cc @@ -71,7 +71,8 @@ TEST_F(UserActivityForwarderTest, ForwardActivityToDetector) { // Run pending tasks so |monitor| receives |forwarder|'s registration. RunUntilIdle(); - base::TimeTicks now = base::TimeTicks::FromInternalValue(1000); + base::TimeTicks now = + base::TimeTicks() + base::TimeDelta::FromMicroseconds(1000); detector.set_now_for_test(now); monitor.NotifyUserActivityObservers(); RunUntilIdle(); diff --git a/chromium/ui/aura/mus/window_manager_delegate.cc b/chromium/ui/aura/mus/window_manager_delegate.cc index e2f42eb1fab..0bf4087a847 100644 --- a/chromium/ui/aura/mus/window_manager_delegate.cc +++ b/chromium/ui/aura/mus/window_manager_delegate.cc @@ -15,4 +15,6 @@ ui::mojom::EventResult WindowManagerDelegate::OnAccelerator( return ui::mojom::EventResult::UNHANDLED; } +void WindowManagerDelegate::OnEventBlockedByModalWindow(Window* window) {} + } // namespace aura diff --git a/chromium/ui/aura/mus/window_manager_delegate.h b/chromium/ui/aura/mus/window_manager_delegate.h index d3df4610aa2..bd3332ef53e 100644 --- a/chromium/ui/aura/mus/window_manager_delegate.h +++ b/chromium/ui/aura/mus/window_manager_delegate.h @@ -41,6 +41,12 @@ class WindowTreeHostMus; struct WindowTreeHostMusInitParams; +// This mirrors ui::mojom::BlockingContainers. See it for details. +struct BlockingContainers { + aura::Window* system_modal_container = nullptr; + aura::Window* min_container = nullptr; +}; + // See the mojom with the same name for details on the functions in this // interface. class AURA_EXPORT WindowManagerClient { @@ -56,7 +62,6 @@ class AURA_EXPORT WindowManagerClient { virtual void RemoveAccelerator(uint32_t id) = 0; virtual void AddActivationParent(Window* window) = 0; virtual void RemoveActivationParent(Window* window) = 0; - virtual void ActivateNextWindow() = 0; virtual void SetExtendedHitRegionForChildren( Window* window, const gfx::Insets& mouse_area, @@ -80,6 +85,12 @@ class AURA_EXPORT WindowManagerClient { virtual void SetGlobalOverrideCursor( base::Optional<ui::CursorData> cursor) = 0; + // Sets whether the cursor is visible because the user touched the + // screen. This bit is separate from SetCursorVisible(), as it implicitly is + // set in the window server when a touch event occurs, and is implicitly + // cleared when the mouse moves. + virtual void SetCursorTouchVisible(bool enabled) = 0; + // Sets the list of keys which don't hide the cursor. virtual void SetKeyEventsThatDontHideCursor( std::vector<ui::mojom::EventMatcherPtr> cursor_key_list) = 0; @@ -89,6 +100,11 @@ class AURA_EXPORT WindowManagerClient { // this does nothing. virtual void RequestClose(Window* window) = 0; + // See mojom::WindowManager::SetBlockingContainers() and + // mojom::BlockingContainers for details on what this does. + virtual void SetBlockingContainers( + const std::vector<BlockingContainers>& all_blocking_containers) = 0; + // Blocks until the initial displays have been received, or if displays are // not automatically created until the connection to mus has been // established. @@ -217,6 +233,10 @@ class AURA_EXPORT WindowManagerDelegate { const ui::Event& event, std::unordered_map<std::string, std::vector<uint8_t>>* properties); + // Called when the mouse cursor is shown or hidden in response to a touch + // event or window manager call. + virtual void OnCursorTouchVisibleChanged(bool enabled) = 0; + virtual void OnWmPerformMoveLoop( Window* window, ui::mojom::MoveLoopSource source, @@ -238,6 +258,10 @@ class AURA_EXPORT WindowManagerDelegate { // window. virtual void OnWmDeactivateWindow(Window* window) = 0; + // Called when an event is blocked by a modal window. |window| is the modal + // window that blocked the event. + virtual void OnEventBlockedByModalWindow(Window* window); + protected: virtual ~WindowManagerDelegate() {} }; diff --git a/chromium/ui/aura/mus/window_mus.h b/chromium/ui/aura/mus/window_mus.h index acf13fd17aa..4b734983e1e 100644 --- a/chromium/ui/aura/mus/window_mus.h +++ b/chromium/ui/aura/mus/window_mus.h @@ -14,10 +14,6 @@ #include "ui/aura/aura_export.h" #include "ui/aura/mus/mus_types.h" -namespace cc { -class SurfaceInfo; -} - namespace gfx { class Rect; class Transform; @@ -29,6 +25,12 @@ enum class OrderDirection; } } +namespace viz { +class FrameSinkId; +class LocalSurfaceId; +class SurfaceInfo; +} + namespace aura { class Window; @@ -60,6 +62,9 @@ class AURA_EXPORT WindowMus { virtual ~WindowMus() {} // Returns the WindowMus associated with |window|. + static const WindowMus* Get(const Window* window) { + return const_cast<const WindowMus*>(Get(const_cast<Window*>(window))); + } static WindowMus* Get(Window* window); Id server_id() const { return server_id_; } diff --git a/chromium/ui/aura/mus/window_port_mus.cc b/chromium/ui/aura/mus/window_port_mus.cc index 4915a5e13db..3b937f13e8b 100644 --- a/chromium/ui/aura/mus/window_port_mus.cc +++ b/chromium/ui/aura/mus/window_port_mus.cc @@ -10,6 +10,7 @@ #include "ui/aura/client/transient_window_client.h" #include "ui/aura/env.h" #include "ui/aura/mus/client_surface_embedder.h" +#include "ui/aura/mus/hit_test_data_provider_aura.h" #include "ui/aura/mus/property_converter.h" #include "ui/aura/mus/window_tree_client.h" #include "ui/aura/mus/window_tree_client_delegate.h" @@ -107,11 +108,11 @@ std::unique_ptr<viz::ClientLayerTreeFrameSink> WindowPortMus::RequestLayerTreeFrameSink( scoped_refptr<viz::ContextProvider> context_provider, gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) { - cc::mojom::CompositorFrameSinkPtrInfo sink_info; - cc::mojom::CompositorFrameSinkRequest sink_request = + viz::mojom::CompositorFrameSinkPtrInfo sink_info; + viz::mojom::CompositorFrameSinkRequest sink_request = mojo::MakeRequest(&sink_info); - cc::mojom::CompositorFrameSinkClientPtr client; - cc::mojom::CompositorFrameSinkClientRequest client_request = + viz::mojom::CompositorFrameSinkClientPtr client; + viz::mojom::CompositorFrameSinkClientRequest client_request = mojo::MakeRequest(&client); constexpr bool enable_surface_synchronization = true; auto layer_tree_frame_sink = base::MakeUnique<viz::ClientLayerTreeFrameSink>( @@ -119,6 +120,7 @@ WindowPortMus::RequestLayerTreeFrameSink( gpu_memory_buffer_manager, nullptr /* shared_bitmap_manager */, nullptr /* synthetic_begin_frame_source */, std::move(sink_info), std::move(client_request), + base::MakeUnique<HitTestDataProviderAura>(window_), base::MakeUnique<viz::DefaultLocalSurfaceIdProvider>(), enable_surface_synchronization); window_tree_client_->AttachCompositorFrameSink( @@ -126,6 +128,12 @@ WindowPortMus::RequestLayerTreeFrameSink( return layer_tree_frame_sink; } +viz::FrameSinkId WindowPortMus::GetFrameSinkId() const { + if (embed_frame_sink_id_.is_valid()) + return embed_frame_sink_id_; + return viz::FrameSinkId(0, server_id()); +} + WindowPortMus::ServerChangeIdType WindowPortMus::ScheduleChange( const ServerChangeType type, const ServerChangeData& data) { @@ -293,7 +301,7 @@ void WindowPortMus::SetFrameSinkIdFromServer( const viz::FrameSinkId& frame_sink_id) { DCHECK(window_mus_type() == WindowMusType::TOP_LEVEL_IN_WM || window_mus_type() == WindowMusType::EMBED_IN_OWNER); - frame_sink_id_ = frame_sink_id; + embed_frame_sink_id_ = frame_sink_id; UpdatePrimarySurfaceInfo(); } @@ -311,7 +319,7 @@ const viz::LocalSurfaceId& WindowPortMus::GetOrAllocateLocalSurfaceId( // The newly generated frame by the embedder will block in the display // compositor until the child submits a corresponding CompositorFrame or a // deadline hits. - if (frame_sink_id_.is_valid()) + if (embed_frame_sink_id_.is_valid()) UpdatePrimarySurfaceInfo(); if (local_layer_tree_frame_sink_) @@ -322,19 +330,21 @@ const viz::LocalSurfaceId& WindowPortMus::GetOrAllocateLocalSurfaceId( void WindowPortMus::SetFallbackSurfaceInfo( const viz::SurfaceInfo& surface_info) { - if (!frame_sink_id_.is_valid()) { + if (!embed_frame_sink_id_.is_valid()) { // |primary_surface_info_| shold not be valid, since we didn't know the - // |frame_sink_id_|. + // |embed_frame_sink_id_|. DCHECK(!primary_surface_info_.is_valid()); - frame_sink_id_ = surface_info.id().frame_sink_id(); + embed_frame_sink_id_ = surface_info.id().frame_sink_id(); UpdatePrimarySurfaceInfo(); } // The frame sink id should never be changed. - DCHECK_EQ(surface_info.id().frame_sink_id(), frame_sink_id_); + DCHECK_EQ(surface_info.id().frame_sink_id(), embed_frame_sink_id_); fallback_surface_info_ = surface_info; UpdateClientSurfaceEmbedder(); + if (window_->delegate()) + window_->delegate()->OnFirstSurfaceActivation(fallback_surface_info_); } void WindowPortMus::DestroyFromServer() { @@ -392,6 +402,10 @@ WindowPortMus::ChangeSource WindowPortMus::OnTransientChildRemoved( : ChangeSource::LOCAL; } +void WindowPortMus::AllocateLocalSurfaceId() { + local_surface_id_ = local_surface_id_allocator_.GenerateId(); +} + const viz::LocalSurfaceId& WindowPortMus::GetLocalSurfaceId() { return local_surface_id_; } @@ -451,6 +465,16 @@ void WindowPortMus::OnPreInit(Window* window) { } void WindowPortMus::OnDeviceScaleFactorChanged(float device_scale_factor) { + // TODO(fsamuel): If we don't have a LayerTreeFrameSinkLocal then we should + // let the window server know about the device scale factor change and + // the new LocalSurfaceId allocated. + if (last_device_scale_factor_ != device_scale_factor && + local_surface_id_.is_valid() && local_layer_tree_frame_sink_) { + last_device_scale_factor_ = device_scale_factor; + local_surface_id_ = local_surface_id_allocator_.GenerateId(); + local_layer_tree_frame_sink_->SetLocalSurfaceId(local_surface_id_); + } + if (window_->delegate()) window_->delegate()->OnDeviceScaleFactorChanged(device_scale_factor); } @@ -546,12 +570,20 @@ WindowPortMus::CreateLayerTreeFrameSink() { nullptr, aura::Env::GetInstance()->context_factory()->GetGpuMemoryBufferManager()); local_layer_tree_frame_sink_ = frame_sink->GetWeakPtr(); + local_surface_id_ = local_surface_id_allocator_.GenerateId(); return std::move(frame_sink); } viz::SurfaceId WindowPortMus::GetSurfaceId() const { - // This is only used by WindowPortLocal in unit tests. - return viz::SurfaceId(); + return viz::SurfaceId(embed_frame_sink_id_, local_surface_id_); +} + +void WindowPortMus::OnWindowAddedToRootWindow() {} + +void WindowPortMus::OnWillRemoveWindowFromRootWindow() {} + +void WindowPortMus::OnEventTargetingPolicyChanged() { + SetEventTargetingPolicy(window_->event_targeting_policy()); } void WindowPortMus::UpdatePrimarySurfaceInfo() { @@ -562,15 +594,13 @@ void WindowPortMus::UpdatePrimarySurfaceInfo() { return; } - if (!frame_sink_id_.is_valid() || !local_surface_id_.is_valid()) + if (!embed_frame_sink_id_.is_valid() || !local_surface_id_.is_valid()) return; primary_surface_info_ = viz::SurfaceInfo( - viz::SurfaceId(frame_sink_id_, local_surface_id_), + viz::SurfaceId(embed_frame_sink_id_, local_surface_id_), ScaleFactorForDisplay(window_), last_surface_size_in_pixels_); UpdateClientSurfaceEmbedder(); - if (window_->delegate()) - window_->delegate()->OnWindowSurfaceChanged(primary_surface_info_); } void WindowPortMus::UpdateClientSurfaceEmbedder() { diff --git a/chromium/ui/aura/mus/window_port_mus.h b/chromium/ui/aura/mus/window_port_mus.h index 68936714fd1..b0d5d62cd92 100644 --- a/chromium/ui/aura/mus/window_port_mus.h +++ b/chromium/ui/aura/mus/window_port_mus.h @@ -93,11 +93,17 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { scoped_refptr<viz::ContextProvider> context_provider, gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager); + // WindowPort: + // Returns either the FrameSinkId set by window server or its server_id with + // the client id part 0. + viz::FrameSinkId GetFrameSinkId() const override; + private: friend class WindowPortMusTestApi; friend class WindowTreeClient; friend class WindowTreeClientPrivate; friend class WindowTreeHostMus; + friend class HitTestDataProviderAuraTest; using ServerChangeIdType = uint8_t; @@ -242,7 +248,6 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { void RemoveTransientChildFromServer(WindowMus* child) override; ChangeSource OnTransientChildAdded(WindowMus* child) override; ChangeSource OnTransientChildRemoved(WindowMus* child) override; - const viz::LocalSurfaceId& GetLocalSurfaceId() override; std::unique_ptr<WindowMusChangeData> PrepareForServerBoundsChange( const gfx::Rect& bounds) override; std::unique_ptr<WindowMusChangeData> PrepareForServerVisibilityChange( @@ -271,8 +276,11 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { std::unique_ptr<ui::PropertyData> data) override; std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink() override; viz::SurfaceId GetSurfaceId() const override; - void OnWindowAddedToRootWindow() override {} - void OnWillRemoveWindowFromRootWindow() override {} + void AllocateLocalSurfaceId() override; + const viz::LocalSurfaceId& GetLocalSurfaceId() override; + void OnWindowAddedToRootWindow() override; + void OnWillRemoveWindowFromRootWindow() override; + void OnEventTargetingPolicyChanged() override; void UpdatePrimarySurfaceInfo(); void UpdateClientSurfaceEmbedder(); @@ -287,13 +295,15 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { ServerChangeIdType next_server_change_id_ = 0; ServerChanges server_changes_; - viz::FrameSinkId frame_sink_id_; + // Only set when it is embedding another client inside. + viz::FrameSinkId embed_frame_sink_id_; viz::SurfaceInfo primary_surface_info_; viz::SurfaceInfo fallback_surface_info_; viz::LocalSurfaceId local_surface_id_; viz::LocalSurfaceIdAllocator local_surface_id_allocator_; + float last_device_scale_factor_ = 1.0f; gfx::Size last_surface_size_in_pixels_; ui::CursorData cursor_; diff --git a/chromium/ui/aura/mus/window_tree_client.cc b/chromium/ui/aura/mus/window_tree_client.cc index 8d64f8deada..a06044dba27 100644 --- a/chromium/ui/aura/mus/window_tree_client.cc +++ b/chromium/ui/aura/mus/window_tree_client.cc @@ -72,14 +72,6 @@ namespace aura { namespace { -// This serves to document the places that rely on bounds changes to the -// root window being ignored. -constexpr bool kRootWindowBoundsChangesAreIgnored = true; - -Id MakeTransportId(ClientSpecificId client_id, ClientSpecificId local_id) { - return (client_id << 16) | local_id; -} - inline uint16_t HiWord(uint32_t id) { return static_cast<uint16_t>((id >> 16) & 0xFFFF); } @@ -135,7 +127,7 @@ WindowTreeHostMus* GetWindowTreeHostMus(WindowMus* window) { } bool IsInternalProperty(const void* key) { - return key == client::kModalKey; + return key == client::kModalKey || key == client::kChildModalParentKey; } void SetWindowTypeFromProperties( @@ -158,21 +150,6 @@ float ScaleFactorForDisplay(Window* window) { return ui::GetScaleFactorForNativeView(window); } -void ConvertEventLocationToDip(int64_t display_id, ui::LocatedEvent* event) { - display::Screen* screen = display::Screen::GetScreen(); - display::Display display; - if (!screen->GetDisplayWithDisplayId(display_id, &display) || - display.device_scale_factor() == 1.f) { - return; - } - const gfx::Point host_location = - gfx::ConvertPointToDIP(display.device_scale_factor(), event->location()); - event->set_location(host_location); - const gfx::Point root_location = gfx::ConvertPointToDIP( - display.device_scale_factor(), event->root_location()); - event->set_root_location(root_location); -} - // Create and return a MouseEvent or TouchEvent from |event| if |event| is a // PointerEvent, otherwise return the copy of |event|. std::unique_ptr<ui::Event> MapEvent(const ui::Event& event) { @@ -200,6 +177,10 @@ void OnAckMustSucceed(bool success) { CHECK(success); } +Id GetServerIdForWindow(Window* window) { + return window ? WindowMus::Get(window)->server_id() : kInvalidServerId; +} + } // namespace WindowTreeClient::WindowTreeClient( @@ -210,7 +191,6 @@ WindowTreeClient::WindowTreeClient( scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, bool create_discardable_memory) : connector_(connector), - client_id_(0), next_window_id_(1), next_change_id_(1), delegate_(delegate), @@ -290,9 +270,6 @@ WindowTreeClient::~WindowTreeClient() { } void WindowTreeClient::ConnectViaWindowTreeFactory() { - // The client id doesn't really matter, we use 101 purely for debugging. - client_id_ = 101; - ui::mojom::WindowTreeFactoryPtr factory; connector_->BindInterface(ui::mojom::kServiceName, &factory); ui::mojom::WindowTreePtr window_tree; @@ -368,8 +345,8 @@ void WindowTreeClient::Embed( void WindowTreeClient::AttachCompositorFrameSink( Id window_id, - cc::mojom::CompositorFrameSinkRequest compositor_frame_sink, - cc::mojom::CompositorFrameSinkClientPtr client) { + viz::mojom::CompositorFrameSinkRequest compositor_frame_sink, + viz::mojom::CompositorFrameSinkClientPtr client) { DCHECK(tree_); tree_->AttachCompositorFrameSink(window_id, std::move(compositor_frame_sink), std::move(client)); @@ -390,6 +367,63 @@ bool WindowTreeClient::IsWindowKnown(aura::Window* window) { return windows_.count(window_mus->server_id()) > 0; } +void WindowTreeClient::ConvertPointerEventLocationToDip( + int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const { + // PointerEvents shouldn't have the target set. + DCHECK(!event->target()); + if (window_manager_delegate_) { + ConvertPointerEventLocationToDipInWindowManager(display_id, window, event); + return; + } + display::Screen* screen = display::Screen::GetScreen(); + display::Display display; + // TODO(sky): this needs to take into account the ui display scale. + // http://crbug.com/758399. + if (!screen->GetDisplayWithDisplayId(display_id, &display) || + display.device_scale_factor() == 1.f) { + return; + } + const gfx::Point root_location = gfx::ConvertPointToDIP( + display.device_scale_factor(), event->root_location()); + event->set_root_location(root_location); + if (window) { + const gfx::Point host_location = gfx::ConvertPointToDIP( + display.device_scale_factor(), event->location()); + event->set_location(host_location); + } else { + // When there is no window force the root and location to be the same. They + // may differ it |window| was valid at the time of the event, but was since + // deleted. + event->set_location(root_location); + } +} + +void WindowTreeClient::ConvertPointerEventLocationToDipInWindowManager( + int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const { + const WindowTreeHostMus* window_tree_host = + GetWindowTreeHostForDisplayId(display_id); + if (!window_tree_host) + return; + + ui::Event::DispatcherApi dispatcher_api(event); + if (window) { + dispatcher_api.set_target(window->GetWindow()); + } else { + // UpdateForRootTransform() in the case of no target uses |location_|. + // |location_| may be relative to a window that wasn't found. To ensure we + // convert from the root, reset |location_| to |root_location_|. + event->set_location_f(event->root_location_f()); + } + event->UpdateForRootTransform( + window_tree_host->GetInverseRootTransform(), + window_tree_host->GetInverseRootTransformForLocalEventCoordinates()); + dispatcher_api.set_target(nullptr); +} + InFlightChange* WindowTreeClient::GetOldestInFlightChangeMatching( const InFlightChange& change) { for (const auto& pair : in_flight_map_) { @@ -481,6 +515,20 @@ void WindowTreeClient::SetLocalPropertiesFromServerProperties( window->SetPropertyFromServer(pair.first, &pair.second); } +const WindowTreeHostMus* WindowTreeClient::GetWindowTreeHostForDisplayId( + int64_t display_id) const { + if (!window_manager_delegate_) + return nullptr; + + for (WindowMus* window : roots_) { + WindowTreeHostMus* window_tree_host = + static_cast<WindowTreeHostMus*>(window->GetWindow()->GetHost()); + if (window_tree_host->display_id() == display_id) + return window_tree_host; + } + return nullptr; +} + std::unique_ptr<WindowTreeHostMus> WindowTreeClient::CreateWindowTreeHost( WindowMusType window_mus_type, const ui::mojom::WindowData& window_data, @@ -567,28 +615,36 @@ void WindowTreeClient::OnConnectionLost() { bool WindowTreeClient::HandleInternalPropertyChanged(WindowMus* window, const void* key, int64_t old_value) { - if (key != client::kModalKey) - return false; - - const uint32_t change_id = - ScheduleInFlightChange(base::MakeUnique<InFlightSetModalTypeChange>( - window, static_cast<ui::ModalType>(old_value))); - tree_->SetModalType(change_id, window->server_id(), - window->GetWindow()->GetProperty(client::kModalKey)); - return true; + if (key == client::kModalKey) { + const uint32_t change_id = + ScheduleInFlightChange(base::MakeUnique<InFlightSetModalTypeChange>( + window, static_cast<ui::ModalType>(old_value))); + tree_->SetModalType(change_id, window->server_id(), + window->GetWindow()->GetProperty(client::kModalKey)); + return true; + } + if (key == client::kChildModalParentKey) { + const uint32_t change_id = + ScheduleInFlightChange(base::MakeUnique<CrashInFlightChange>( + window, ChangeType::CHILD_MODAL_PARENT)); + Window* child_modal_parent = + window->GetWindow()->GetProperty(client::kChildModalParentKey); + tree_->SetChildModalParent( + change_id, window->server_id(), + child_modal_parent ? WindowMus::Get(child_modal_parent)->server_id() + : kInvalidServerId); + return true; + } + return false; } void WindowTreeClient::OnEmbedImpl( ui::mojom::WindowTree* window_tree, - ClientSpecificId client_id, ui::mojom::WindowDataPtr root_data, int64_t display_id, Id focused_window_id, bool drawn, const base::Optional<viz::LocalSurfaceId>& local_surface_id) { - // WARNING: this is only called if WindowTreeClient was created as the - // result of an embedding. - client_id_ = client_id; WindowTreeConnectionEstablished(window_tree); DCHECK(roots_.empty()); @@ -601,19 +657,6 @@ void WindowTreeClient::OnEmbedImpl( delegate_->OnEmbed(std::move(window_tree_host)); } -void WindowTreeClient::OnSetDisplayRootDone( - Id window_id, - const base::Optional<viz::LocalSurfaceId>& local_surface_id) { - // The only way SetDisplayRoot() should fail is if we've done something wrong. - CHECK(local_surface_id.has_value()); - WindowMus* window = GetWindowByServerId(window_id); - if (!window) - return; // Display was already deleted. - - ui::Compositor* compositor = window->GetWindow()->GetHost()->compositor(); - compositor->SetLocalSurfaceId(*local_surface_id); -} - WindowTreeHostMus* WindowTreeClient::WmNewDisplayAddedImpl( const display::Display& display, ui::mojom::WindowDataPtr root_data, @@ -713,7 +756,7 @@ void WindowTreeClient::OnWindowMusCreated(WindowMus* window) { if (window->server_id() != kInvalidServerId) return; - window->set_server_id(MakeTransportId(client_id_, next_window_id_++)); + window->set_server_id(next_window_id_++); RegisterWindowMus(window); DCHECK(window_manager_delegate_ || !IsRoot(window)); @@ -737,6 +780,11 @@ void WindowTreeClient::OnWindowMusCreated(WindowMus* window) { base::MakeUnique<CrashInFlightChange>(window, ChangeType::NEW_WINDOW)); tree_->NewWindow(change_id, window->server_id(), std::move(transport_properties)); + if (window->GetWindow()->event_targeting_policy() != + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS) { + SetEventTargetingPolicy(window, + window->GetWindow()->event_targeting_policy()); + } if (window->window_mus_type() == WindowMusType::DISPLAY_MANUALLY_CREATED) { WindowTreeHostMus* window_tree_host = GetWindowTreeHostMus(window); std::unique_ptr<DisplayInitParams> display_init_params = @@ -755,7 +803,6 @@ void WindowTreeClient::OnWindowMusCreated(WindowMus* window) { // bounds changes are routed through OnWindowTreeHostBoundsWillChange()). // But the display is created with an initial bounds, and we need to push // that to the server. - DCHECK(kRootWindowBoundsChangesAreIgnored); ScheduleInFlightBoundsChange( window, gfx::Rect(), gfx::Rect( @@ -765,8 +812,7 @@ void WindowTreeClient::OnWindowMusCreated(WindowMus* window) { window_manager_client_->SetDisplayRoot( display, display_init_params->viewport_metrics.Clone(), display_init_params->is_primary_display, window->server_id(), - base::Bind(&WindowTreeClient::OnSetDisplayRootDone, - base::Unretained(this), window->server_id())); + base::Bind(&OnAckMustSucceed)); } } } @@ -816,7 +862,21 @@ void WindowTreeClient::OnWindowMusBoundsChanged(WindowMus* window, // OnWindowTreeHostBoundsWillChange(). Any bounds that happen here are a side // effect of those and can be ignored. if (IsRoot(window)) { - DCHECK(kRootWindowBoundsChangesAreIgnored); + // NOTE: this has to happen to here as during the call to + // OnWindowTreeHostBoundsWillChange() the compositor hasn't been updated + // yet. + if (window->window_mus_type() == WindowMusType::DISPLAY_MANUALLY_CREATED) { + WindowTreeHost* window_tree_host = window->GetWindow()->GetHost(); + // |window_tree_host| may be null if this is called during creation of + // the window associated with the WindowTreeHostMus. + if (window_tree_host) { + viz::LocalSurfaceId local_surface_id = + window->GetOrAllocateLocalSurfaceId( + window_tree_host->GetBoundsInPixels().size()); + DCHECK(local_surface_id.is_valid()); + window_tree_host->compositor()->SetLocalSurfaceId(local_surface_id); + } + } return; } @@ -947,9 +1007,9 @@ std::set<Window*> WindowTreeClient::GetRoots() { } bool WindowTreeClient::WasCreatedByThisClient(const WindowMus* window) const { - // Windows created via CreateTopLevelWindow() are not owned by us, but have - // our client id. const_cast is required by set. - return HiWord(window->server_id()) == client_id_ && + // Windows created via CreateTopLevelWindow() are not owned by us, but don't + // have high-word set. const_cast is required by set. + return !HiWord(window->server_id()) && roots_.count(const_cast<WindowMus*>(window)) == 0; } @@ -1008,7 +1068,6 @@ void WindowTreeClient::SetEventTargetingPolicy( } void WindowTreeClient::OnEmbed( - ClientSpecificId client_id, ui::mojom::WindowDataPtr root_data, ui::mojom::WindowTreePtr tree, int64_t display_id, @@ -1025,8 +1084,7 @@ void WindowTreeClient::OnEmbed( MakeRequest(&window_manager_internal_client_)); window_manager_client_ = window_manager_internal_client_.get(); } - - OnEmbedImpl(tree_ptr_.get(), client_id, std::move(root_data), display_id, + OnEmbedImpl(tree_ptr_.get(), std::move(root_data), display_id, focused_window_id, drawn, local_surface_id); } @@ -1334,7 +1392,8 @@ void WindowTreeClient::OnWindowInputEvent(uint32_t event_id, if (matches_pointer_watcher && has_pointer_watcher_) { DCHECK(event->IsPointerEvent()); std::unique_ptr<ui::Event> event_in_dip(ui::Event::Clone(*event)); - ConvertEventLocationToDip(display_id, event_in_dip->AsLocatedEvent()); + ConvertPointerEventLocationToDip(display_id, window, + event_in_dip->AsLocatedEvent()); delegate_->OnPointerEventObserved(*event_in_dip->AsPointerEvent(), window ? window->GetWindow() : nullptr); } @@ -1368,8 +1427,31 @@ void WindowTreeClient::OnWindowInputEvent(uint32_t event_id, // TODO(moshayedi): crbug.com/617222. No need to convert to ui::MouseEvent or // ui::TouchEvent once we have proper support for pointer events. std::unique_ptr<ui::Event> mapped_event = MapEvent(*event.get()); - DispatchEventToTarget(mapped_event.get(), window); - ack_handler.set_handled(mapped_event->handled()); + ui::Event* event_to_dispatch = mapped_event.get(); +// Ash wants the native events in one place (see ExtendedMouseWarpController). +// By using the constructor that takes a MouseEvent we ensure the MouseEvent +// has a NativeEvent that can be used to extract the pixel coordinates. +// +// TODO: this should really be covered by |root_location|. See 608547 for +// details. +#if defined(USE_OZONE) + std::unique_ptr<ui::MouseEvent> mapped_event_with_native; + if (mapped_event->type() == ui::ET_MOUSE_MOVED || + mapped_event->type() == ui::ET_MOUSE_DRAGGED) { + mapped_event_with_native = base::MakeUnique<ui::MouseEvent>( + static_cast<const base::NativeEvent&>(mapped_event.get())); + // MouseEvent(NativeEvent) sets the root_location to location. + mapped_event_with_native->set_root_location_f( + mapped_event->AsMouseEvent()->root_location_f()); + // |mapped_event| is now the NativeEvent. It's expected the location of the + // NativeEvent is the same as root_location. + mapped_event->AsMouseEvent()->set_location_f( + mapped_event->AsMouseEvent()->root_location_f()); + event_to_dispatch = mapped_event_with_native.get(); + } +#endif + DispatchEventToTarget(event_to_dispatch, window); + ack_handler.set_handled(event_to_dispatch->handled()); } void WindowTreeClient::OnPointerEventObserved(std::unique_ptr<ui::Event> event, @@ -1380,8 +1462,9 @@ void WindowTreeClient::OnPointerEventObserved(std::unique_ptr<ui::Event> event, if (!has_pointer_watcher_) return; - ConvertEventLocationToDip(display_id, event->AsLocatedEvent()); WindowMus* target_window = GetWindowByServerId(window_id); + ConvertPointerEventLocationToDip(display_id, target_window, + event->AsLocatedEvent()); delegate_->OnPointerEventObserved( *event->AsPointerEvent(), target_window ? target_window->GetWindow() : nullptr); @@ -1500,6 +1583,29 @@ void WindowTreeClient::OnChangeCompleted(uint32_t change_id, bool success) { } } +void WindowTreeClient::SetBlockingContainers( + const std::vector<BlockingContainers>& all_blocking_containers) { + std::vector<ui::mojom::BlockingContainersPtr> + transport_all_blocking_containers; + for (const BlockingContainers& blocking_containers : + all_blocking_containers) { + ui::mojom::BlockingContainersPtr transport_blocking_containers = + ui::mojom::BlockingContainers::New(); + // The |system_modal_container| must be specified, |min_container| may be + // null. + DCHECK(blocking_containers.system_modal_container); + transport_blocking_containers->system_modal_container_id = + GetServerIdForWindow(blocking_containers.system_modal_container); + transport_blocking_containers->min_container_id = + GetServerIdForWindow(blocking_containers.min_container); + transport_all_blocking_containers.push_back( + std::move(transport_blocking_containers)); + } + window_manager_client_->SetBlockingContainers( + std::move(transport_all_blocking_containers), + base::Bind(&OnAckMustSucceed)); +} + void WindowTreeClient::GetWindowManager( mojo::AssociatedInterfaceRequest<WindowManager> internal) { window_manager_internal_.reset( @@ -1537,8 +1643,7 @@ WindowTreeHostMusInitParams WindowTreeClient::CreateInitParamsForNewDisplay() { return init_params; } -void WindowTreeClient::OnConnect(ClientSpecificId client_id) { - client_id_ = client_id; +void WindowTreeClient::OnConnect() { got_initial_displays_ = true; if (window_manager_delegate_) window_manager_delegate_->OnWmConnected(); @@ -1811,6 +1916,20 @@ void WindowTreeClient::OnAccelerator(uint32_t ack_id, window_manager_client_->OnAcceleratorAck(ack_id, result, properties); } +void WindowTreeClient::OnCursorTouchVisibleChanged(bool enabled) { + if (window_manager_client_) + window_manager_delegate_->OnCursorTouchVisibleChanged(enabled); +} + +void WindowTreeClient::OnEventBlockedByModalWindow(Id window_id) { + if (!window_manager_delegate_) + return; + + WindowMus* window = GetWindowByServerId(window_id); + if (window) + window_manager_delegate_->OnEventBlockedByModalWindow(window->GetWindow()); +} + void WindowTreeClient::SetFrameDecorationValues( ui::mojom::FrameDecorationValuesPtr values) { if (window_manager_client_) { @@ -1855,11 +1974,6 @@ void WindowTreeClient::RemoveActivationParent(Window* window) { } } -void WindowTreeClient::ActivateNextWindow() { - if (window_manager_client_) - window_manager_client_->ActivateNextWindow(); -} - void WindowTreeClient::SetExtendedHitRegionForChildren( Window* window, const gfx::Insets& mouse_insets, @@ -1900,6 +2014,11 @@ void WindowTreeClient::SetGlobalOverrideCursor( window_manager_client_->WmSetGlobalOverrideCursor(std::move(cursor)); } +void WindowTreeClient::SetCursorTouchVisible(bool enabled) { + if (window_manager_client_) + window_manager_client_->WmSetCursorTouchVisible(enabled); +} + void WindowTreeClient::SetKeyEventsThatDontHideCursor( std::vector<ui::mojom::EventMatcherPtr> cursor_key_list) { if (window_manager_client_) { @@ -1943,9 +2062,10 @@ void WindowTreeClient::AddDisplayReusingWindowTreeHost( WindowMus* display_root_window = WindowMus::Get(window_tree_host->window()); window_manager_client_->SetDisplayRoot( display, std::move(viewport_metrics), is_primary_display, - display_root_window->server_id(), - base::Bind(&WindowTreeClient::OnSetDisplayRootDone, - base::Unretained(this), display_root_window->server_id())); + display_root_window->server_id(), base::Bind(&OnAckMustSucceed)); + window_tree_host->compositor()->SetLocalSurfaceId( + display_root_window->GetOrAllocateLocalSurfaceId( + window_tree_host->GetBoundsInPixels().size())); } } @@ -2076,13 +2196,23 @@ void WindowTreeClient::OnWindowTreeHostMoveCursorToDisplayLocation( } } +void WindowTreeClient::OnWindowTreeHostConfineCursorToBounds( + const gfx::Rect& bounds_in_pixels, + int64_t display_id) { + DCHECK(window_manager_client_); + if (window_manager_client_) { + window_manager_client_->WmConfineCursorToBounds(bounds_in_pixels, + display_id); + } +} + std::unique_ptr<WindowPortMus> WindowTreeClient::CreateWindowPortForTopLevel( const std::map<std::string, std::vector<uint8_t>>* properties) { std::unique_ptr<WindowPortMus> window_port = base::MakeUnique<WindowPortMus>(this, WindowMusType::TOP_LEVEL); roots_.insert(window_port.get()); - window_port->set_server_id(MakeTransportId(client_id_, next_window_id_++)); + window_port->set_server_id(next_window_id_++); RegisterWindowMus(window_port.get()); std::unordered_map<std::string, std::vector<uint8_t>> transport_properties; diff --git a/chromium/ui/aura/mus/window_tree_client.h b/chromium/ui/aura/mus/window_tree_client.h index fb4c741b568..4490bdcde2e 100644 --- a/chromium/ui/aura/mus/window_tree_client.h +++ b/chromium/ui/aura/mus/window_tree_client.h @@ -89,8 +89,8 @@ using EventResultCallback = base::Callback<void(ui::mojom::EventResult)>; // When WindowTreeClient is deleted all windows are deleted (and observers // notified). class AURA_EXPORT WindowTreeClient - : NON_EXPORTED_BASE(public ui::mojom::WindowTreeClient), - NON_EXPORTED_BASE(public ui::mojom::WindowManager), + : public ui::mojom::WindowTreeClient, + public ui::mojom::WindowManager, public CaptureSynchronizerDelegate, public FocusSynchronizerDelegate, public DragDropControllerHost, @@ -129,7 +129,6 @@ class AURA_EXPORT WindowTreeClient FocusSynchronizer* focus_synchronizer() { return focus_synchronizer_.get(); } bool connected() const { return tree_ != nullptr; } - ClientSpecificId client_id() const { return client_id_; } void SetCanFocus(Window* window, bool can_focus); void SetCanAcceptDrops(WindowMus* window, bool can_accept_drops); @@ -155,8 +154,8 @@ class AURA_EXPORT WindowTreeClient void AttachCompositorFrameSink( Id window_id, - cc::mojom::CompositorFrameSinkRequest compositor_frame_sink, - cc::mojom::CompositorFrameSinkClientPtr client); + viz::mojom::CompositorFrameSinkRequest compositor_frame_sink, + viz::mojom::CompositorFrameSinkClientPtr client); bool IsRoot(WindowMus* window) const { return roots_.count(window) > 0; } @@ -207,6 +206,22 @@ class AURA_EXPORT WindowTreeClient bool IsWindowKnown(aura::Window* window); + // Updates the coordinates of |event| to be in DIPs. |window| is the source + // of the event, and may be null. A null |window| means either there is no + // local window the event is targeted at *or* |window| was valid at the time + // the event was generated at the server but was deleted locally before the + // event was received. + void ConvertPointerEventLocationToDip(int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const; + + // Variant of ConvertPointerEventLocationToDip() that is used when in + // the window-manager. + void ConvertPointerEventLocationToDipInWindowManager( + int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const; + // Returns the oldest InFlightChange that matches |change|. InFlightChange* GetOldestInFlightChangeMatching(const InFlightChange& change); @@ -236,6 +251,9 @@ class AURA_EXPORT WindowTreeClient WindowMus* window, const ui::mojom::WindowData& window_data); + const WindowTreeHostMus* GetWindowTreeHostForDisplayId( + int64_t display_id) const; + // Creates a new WindowTreeHostMus. std::unique_ptr<WindowTreeHostMus> CreateWindowTreeHost( WindowMusType window_mus_type, @@ -264,18 +282,12 @@ class AURA_EXPORT WindowTreeClient // OnEmbed() calls into this. Exposed as a separate function for testing. void OnEmbedImpl(ui::mojom::WindowTree* window_tree, - ClientSpecificId client_id, ui::mojom::WindowDataPtr root_data, int64_t display_id, Id focused_window_id, bool drawn, const base::Optional<viz::LocalSurfaceId>& local_surface_id); - // Called once mus acks the call to SetDisplayRoot(). - void OnSetDisplayRootDone( - Id window_id, - const base::Optional<viz::LocalSurfaceId>& local_surface_id); - // Called by WmNewDisplayAdded(). WindowTreeHostMus* WmNewDisplayAddedImpl( const display::Display& display, @@ -331,7 +343,6 @@ class AURA_EXPORT WindowTreeClient // Overridden from WindowTreeClient: void OnEmbed( - ClientSpecificId client_id, ui::mojom::WindowDataPtr root, ui::mojom::WindowTreePtr tree, int64_t display_id, @@ -421,11 +432,13 @@ class AURA_EXPORT WindowTreeClient void OnDragDropDone() override; void OnChangeCompleted(uint32_t change_id, bool success) override; void RequestClose(uint32_t window_id) override; + void SetBlockingContainers( + const std::vector<BlockingContainers>& all_blocking_containers) override; void GetWindowManager( mojo::AssociatedInterfaceRequest<WindowManager> internal) override; // Overridden from WindowManager: - void OnConnect(ClientSpecificId client_id) override; + void OnConnect() override; void WmNewDisplayAdded( const display::Display& display, ui::mojom::WindowDataPtr root_data, @@ -468,6 +481,8 @@ class AURA_EXPORT WindowTreeClient void OnAccelerator(uint32_t ack_id, uint32_t accelerator_id, std::unique_ptr<ui::Event> event) override; + void OnCursorTouchVisibleChanged(bool enabled) override; + void OnEventBlockedByModalWindow(Id window_id) override; // Overridden from WindowManagerClient: void SetFrameDecorationValues( @@ -479,7 +494,6 @@ class AURA_EXPORT WindowTreeClient void RemoveAccelerator(uint32_t id) override; void AddActivationParent(Window* window) override; void RemoveActivationParent(Window* window) override; - void ActivateNextWindow() override; void SetExtendedHitRegionForChildren( Window* window, const gfx::Insets& mouse_insets, @@ -489,6 +503,7 @@ class AURA_EXPORT WindowTreeClient void SetCursorVisible(bool visible) override; void SetCursorSize(ui::CursorSize cursor_size) override; void SetGlobalOverrideCursor(base::Optional<ui::CursorData> cursor) override; + void SetCursorTouchVisible(bool enabled) override; void SetKeyEventsThatDontHideCursor( std::vector<ui::mojom::EventMatcherPtr> cursor_key_list) override; void RequestClose(Window* window) override; @@ -532,6 +547,8 @@ class AURA_EXPORT WindowTreeClient void OnWindowTreeHostMoveCursorToDisplayLocation( const gfx::Point& location_in_pixels, int64_t display_id) override; + void OnWindowTreeHostConfineCursorToBounds(const gfx::Rect& bounds_in_pixels, + int64_t display_id) override; std::unique_ptr<WindowPortMus> CreateWindowPortForTopLevel( const std::map<std::string, std::vector<uint8_t>>* properties) override; void OnWindowTreeHostCreated(WindowTreeHostMus* window_tree_host) override; @@ -573,10 +590,6 @@ class AURA_EXPORT WindowTreeClient // This may be null in tests. service_manager::Connector* connector_; - // This is set once and only once when we get OnEmbed(). It gives the unique - // id for this client. - ClientSpecificId client_id_; - // Id assigned to the next window created. ClientSpecificId next_window_id_; diff --git a/chromium/ui/aura/mus/window_tree_client_unittest.cc b/chromium/ui/aura/mus/window_tree_client_unittest.cc index f6ba1a71dfc..9fb47c75742 100644 --- a/chromium/ui/aura/mus/window_tree_client_unittest.cc +++ b/chromium/ui/aura/mus/window_tree_client_unittest.cc @@ -52,6 +52,7 @@ #include "ui/events/test/test_event_handler.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/transform.h" namespace aura { @@ -211,11 +212,35 @@ INSTANTIATE_TEST_CASE_P(/* no prefix */, WindowTreeClientWmTestSurfaceSync, ::testing::Bool()); +namespace { + +class FirstSurfaceActivationWindowDelegate : public test::TestWindowDelegate { + public: + FirstSurfaceActivationWindowDelegate() = default; + ~FirstSurfaceActivationWindowDelegate() override = default; + + const viz::SurfaceInfo& last_surface_info() const { + return last_surface_info_; + } + + void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override { + last_surface_info_ = surface_info; + } + + private: + viz::SurfaceInfo last_surface_info_; + + DISALLOW_COPY_AND_ASSIGN(FirstSurfaceActivationWindowDelegate); +}; + +} // namespace + // Verifies that a ClientSurfaceEmbedder is created for a window once it has // a bounds, and a valid FrameSinkId. TEST_P(WindowTreeClientWmTestSurfaceSync, ClientSurfaceEmbedderOnValidEmbedding) { - Window window(nullptr); + FirstSurfaceActivationWindowDelegate delegate; + Window window(&delegate); // TOP_LEVEL_IN_WM and EMBED_IN_OWNER windows allocate viz::LocalSurfaceIds // when their sizes change. window.SetProperty(aura::client::kEmbedType, @@ -245,6 +270,7 @@ TEST_P(WindowTreeClientWmTestSurfaceSync, ClientSurfaceEmbedder* client_surface_embedder = window_port_mus->client_surface_embedder(); ASSERT_NE(nullptr, client_surface_embedder); + EXPECT_FALSE(delegate.last_surface_info().is_valid()); // Until the fallback surface fills the window, we will have gutter. { @@ -261,6 +287,9 @@ TEST_P(WindowTreeClientWmTestSurfaceSync, // client lib. This should cause the gutter to go away, eliminating overdraw. window_tree_client()->OnWindowSurfaceChanged( server_id(&window), window_port_mus->PrimarySurfaceInfoForTesting()); + EXPECT_TRUE(delegate.last_surface_info().is_valid()); + EXPECT_EQ(delegate.last_surface_info(), + window_port_mus->PrimarySurfaceInfoForTesting()); // The gutter is gone. ASSERT_EQ(nullptr, client_surface_embedder->BottomGutterForTesting()); @@ -718,41 +747,52 @@ class InputEventBasicTestWindowDelegate : public test::TestWindowDelegate { : test_window_tree_(test_window_tree) {} ~InputEventBasicTestWindowDelegate() override {} - bool got_move() const { return got_move_; } - bool got_press() const { return got_press_; } - bool got_release() const { return got_release_; } + int move_count() const { return move_count_; } + int press_count() const { return press_count_; } + int release_count() const { return release_count_; } bool was_acked() const { return was_acked_; } const gfx::Point& last_event_location() const { return last_event_location_; } void set_event_id(uint32_t event_id) { event_id_ = event_id; } + bool last_mouse_event_had_native_event() const { + return last_mouse_event_had_native_event_; + } + const gfx::Point& last_native_event_location() const { + return last_native_event_location_; + } // TestWindowDelegate:: void OnMouseEvent(ui::MouseEvent* event) override { was_acked_ = test_window_tree_->WasEventAcked(event_id_); if (event->type() == ui::ET_MOUSE_MOVED) - got_move_ = true; + ++move_count_; else if (event->type() == ui::ET_MOUSE_PRESSED) - got_press_ = true; + ++press_count_; else if (event->type() == ui::ET_MOUSE_RELEASED) - got_release_ = true; + ++release_count_; last_event_location_ = event->location(); + last_mouse_event_had_native_event_ = event->HasNativeEvent(); + if (event->HasNativeEvent()) { + last_native_event_location_ = + ui::EventSystemLocationFromNative(event->native_event()); + } event->SetHandled(); } void OnTouchEvent(ui::TouchEvent* event) override { was_acked_ = test_window_tree_->WasEventAcked(event_id_); if (event->type() == ui::ET_TOUCH_PRESSED) - got_press_ = true; + ++press_count_; else if (event->type() == ui::ET_TOUCH_RELEASED) - got_release_ = true; + ++release_count_; last_event_location_ = event->location(); event->SetHandled(); } void reset() { was_acked_ = false; - got_move_ = false; - got_press_ = false; - got_release_ = false; + move_count_ = 0; + press_count_ = 0; + release_count_ = 0; last_event_location_ = gfx::Point(); event_id_ = 0; } @@ -760,11 +800,13 @@ class InputEventBasicTestWindowDelegate : public test::TestWindowDelegate { private: TestWindowTree* test_window_tree_; bool was_acked_ = false; - bool got_move_ = false; - bool got_press_ = false; - bool got_release_ = false; + int move_count_ = 0; + int press_count_ = 0; + int release_count_ = false; gfx::Point last_event_location_; uint32_t event_id_ = 0; + bool last_mouse_event_had_native_event_ = false; + gfx::Point last_native_event_location_; DISALLOW_COPY_AND_ASSIGN(InputEventBasicTestWindowDelegate); }; @@ -775,7 +817,7 @@ class InputEventBasicTestEventHandler : public ui::test::TestEventHandler { : target_window_(target_window) {} ~InputEventBasicTestEventHandler() override {} - bool got_move() const { return got_move_; } + int move_count() const { return move_count_; } const gfx::Point& last_event_location() const { return last_event_location_; } void set_event_id(uint32_t event_id) { event_id_ = event_id; } @@ -783,21 +825,21 @@ class InputEventBasicTestEventHandler : public ui::test::TestEventHandler { void OnMouseEvent(ui::MouseEvent* event) override { if (event->target() == target_window_) { if (event->type() == ui::ET_MOUSE_MOVED) - got_move_ = true; + ++move_count_; last_event_location_ = event->location(); event->SetHandled(); } } void reset() { - got_move_ = false; + move_count_ = 0; last_event_location_ = gfx::Point(); event_id_ = 0; } private: Window* target_window_ = nullptr; - bool got_move_ = false; + int move_count_ = 0; gfx::Point last_event_location_; uint32_t event_id_ = 0; @@ -822,7 +864,7 @@ TEST_F(WindowTreeClientClientTest, InputEventBasic) { top_level->AddChild(&child); child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_move()); + EXPECT_EQ(0, window_delegate.move_count()); EXPECT_FALSE(window_delegate.was_acked()); const gfx::Point event_location_in_child(2, 3); const uint32_t event_id = 1; @@ -836,7 +878,7 @@ TEST_F(WindowTreeClientClientTest, InputEventBasic) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_move()); + EXPECT_EQ(1, window_delegate.move_count()); EXPECT_FALSE(window_delegate.was_acked()); EXPECT_EQ(event_location_in_child, window_delegate.last_event_location()); } @@ -857,7 +899,7 @@ TEST_F(WindowTreeClientClientTest, InputEventPointerEvent) { top_level->AddChild(&child); child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_move()); + EXPECT_EQ(0, window_delegate.move_count()); const gfx::Point event_location(2, 3); const uint32_t event_id = 1; window_delegate.set_event_id(event_id); @@ -871,7 +913,7 @@ TEST_F(WindowTreeClientClientTest, InputEventPointerEvent) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_move()); + EXPECT_EQ(1, window_delegate.move_count()); EXPECT_EQ(event_location, window_delegate.last_event_location()); } @@ -899,8 +941,8 @@ TEST_F(WindowTreeClientClientTest, InputEventFindTargetAndConversion) { child2.SetBounds(gfx::Rect(20, 30, 100, 100)); child2.Show(); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); // child1 has a targeter set and event_location is (50, 60), child2 // should get the event even though mus-ws wants to send to child1. @@ -917,8 +959,8 @@ TEST_F(WindowTreeClientClientTest, InputEventFindTargetAndConversion) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_TRUE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(1, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(30, 30), window_delegate2.last_event_location()); window_delegate1.reset(); window_delegate2.reset(); @@ -938,8 +980,8 @@ TEST_F(WindowTreeClientClientTest, InputEventFindTargetAndConversion) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location()); } @@ -967,8 +1009,8 @@ TEST_F(WindowTreeClientClientTest, InputEventCustomWindowTargeter) { child2.SetBounds(gfx::Rect(20, 30, 100, 100)); child2.Show(); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); // child1 has a custom targeter set which would always return itself as the // target window therefore event should go to child1 unlike @@ -986,8 +1028,8 @@ TEST_F(WindowTreeClientClientTest, InputEventCustomWindowTargeter) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location()); window_delegate1.reset(); window_delegate2.reset(); @@ -1003,8 +1045,8 @@ TEST_F(WindowTreeClientClientTest, InputEventCustomWindowTargeter) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(70, 90), window_delegate1.last_event_location()); } @@ -1037,8 +1079,8 @@ TEST_F(WindowTreeClientClientTest, InputEventCaptureWindow) { child2->SetBounds(gfx::Rect(20, 30, 100, 100)); child2->Show(); - EXPECT_FALSE(window_delegate1->got_move()); - EXPECT_FALSE(window_delegate2->got_move()); + EXPECT_EQ(0, window_delegate1->move_count()); + EXPECT_EQ(0, window_delegate2->move_count()); // child1 has a custom targeter set which would always return itself as the // target window therefore event should go to child1. @@ -1055,8 +1097,8 @@ TEST_F(WindowTreeClientClientTest, InputEventCaptureWindow) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1->got_move()); - EXPECT_FALSE(window_delegate2->got_move()); + EXPECT_EQ(1, window_delegate1->move_count()); + EXPECT_EQ(0, window_delegate2->move_count()); EXPECT_EQ(gfx::Point(50, 60), window_delegate1->last_event_location()); window_delegate1->reset(); window_delegate2->reset(); @@ -1076,8 +1118,8 @@ TEST_F(WindowTreeClientClientTest, InputEventCaptureWindow) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate1->got_move()); - EXPECT_TRUE(window_delegate2->got_move()); + EXPECT_EQ(0, window_delegate1->move_count()); + EXPECT_EQ(1, window_delegate2->move_count()); EXPECT_EQ(gfx::Point(30, 30), window_delegate2->last_event_location()); child2.reset(); child1.reset(); @@ -1104,8 +1146,8 @@ TEST_F(WindowTreeClientClientTest, InputEventRootWindow) { child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(root_handler.got_move()); - EXPECT_FALSE(child_delegate.got_move()); + EXPECT_EQ(0, root_handler.move_count()); + EXPECT_EQ(0, child_delegate.move_count()); const gfx::Point event_location_in_child(20, 30); const uint32_t event_id = 1; @@ -1121,9 +1163,9 @@ TEST_F(WindowTreeClientClientTest, InputEventRootWindow) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(root_handler.got_move()); + EXPECT_EQ(1, root_handler.move_count()); EXPECT_EQ(gfx::Point(20, 30), root_handler.last_event_location()); - EXPECT_FALSE(child_delegate.got_move()); + EXPECT_EQ(0, child_delegate.move_count()); EXPECT_EQ(gfx::Point(), child_delegate.last_event_location()); } @@ -1145,7 +1187,7 @@ TEST_F(WindowTreeClientClientTest, InputMouseEventNoWindow) { child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_press()); + EXPECT_EQ(0, window_delegate.press_count()); EXPECT_FALSE(env->IsMouseButtonDown()); EXPECT_FALSE(env->mouse_button_flags()); EXPECT_EQ(gfx::Point(), env->last_mouse_location()); @@ -1154,7 +1196,7 @@ TEST_F(WindowTreeClientClientTest, InputMouseEventNoWindow) { uint32_t event_id = 1; window_delegate.set_event_id(event_id); ui::PointerEvent pointer_event_down( - ui::ET_POINTER_DOWN, event_location, gfx::Point(), + ui::ET_POINTER_DOWN, event_location, event_location, ui::EF_LEFT_MOUSE_BUTTON, 0, ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0), ui::EventTimeForNow()); @@ -1164,7 +1206,7 @@ TEST_F(WindowTreeClientClientTest, InputMouseEventNoWindow) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_press()); + EXPECT_EQ(1, window_delegate.press_count()); EXPECT_TRUE(env->IsMouseButtonDown()); EXPECT_EQ(1024, env->mouse_button_flags()); // ui::EF_LEFT_MOUSE_BUTTON EXPECT_EQ(event_location, env->last_mouse_location()); @@ -1174,7 +1216,7 @@ TEST_F(WindowTreeClientClientTest, InputMouseEventNoWindow) { event_id = 2; window_delegate.set_event_id(event_id); ui::PointerEvent pointer_event_up( - ui::ET_POINTER_UP, event_location1, gfx::Point(), + ui::ET_POINTER_UP, event_location1, event_location, ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON, ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0), ui::EventTimeForNow()); @@ -1187,7 +1229,7 @@ TEST_F(WindowTreeClientClientTest, InputMouseEventNoWindow) { // aura::Env, location shouldn't be updated. EXPECT_EQ(ui::mojom::EventResult::UNHANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate.got_release()); + EXPECT_EQ(0, window_delegate.release_count()); EXPECT_FALSE(env->IsMouseButtonDown()); EXPECT_FALSE(env->mouse_button_flags()); EXPECT_EQ(event_location, env->last_mouse_location()); @@ -1211,7 +1253,7 @@ TEST_F(WindowTreeClientClientTest, InputTouchEventNoWindow) { child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_press()); + EXPECT_EQ(0, window_delegate.press_count()); EXPECT_FALSE(env->is_touch_down()); const gfx::Point event_location(2, 3); @@ -1227,7 +1269,7 @@ TEST_F(WindowTreeClientClientTest, InputTouchEventNoWindow) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_press()); + EXPECT_EQ(1, window_delegate.press_count()); EXPECT_TRUE(env->is_touch_down()); window_delegate.reset(); @@ -1246,7 +1288,7 @@ TEST_F(WindowTreeClientClientTest, InputTouchEventNoWindow) { // aura::Env. EXPECT_EQ(ui::mojom::EventResult::UNHANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate.got_release()); + EXPECT_EQ(0, window_delegate.release_count()); EXPECT_FALSE(env->is_touch_down()); } @@ -1865,7 +1907,7 @@ class CloseWindowWindowTreeHostObserver : public aura::WindowTreeHostObserver { bool root_destroyed() const { return root_destroyed_; } // aura::WindowTreeHostObserver:: - void OnHostCloseRequested(const aura::WindowTreeHost* host) override { + void OnHostCloseRequested(aura::WindowTreeHost* host) override { root_destroyed_ = true; } @@ -2306,6 +2348,7 @@ TEST_F(WindowTreeClientWmTest, ManuallyCreateDisplay) { WindowTreeHostMusInitParams init_params = WindowTreeClientPrivate(window_tree_client_impl()) .CallCreateInitParamsForNewDisplay(); + init_params.display_id = display_params->display->id(); init_params.display_init_params = std::move(display_params); WindowTreeHostMus window_tree_host(std::move(init_params)); window_tree_host.InitHost(); @@ -2379,7 +2422,9 @@ TEST_F(WindowTreeClientClientTestHighDPI, PointerEventsInDip) { // Delegate received the event in Dips. const ui::PointerEvent* last_event = last_event_observed(); ASSERT_TRUE(last_event); - EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, location_pixels), + // NOTE: the root and location are the same as there was no window supplied to + // OnPointerEventObserved(). + EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, root_location_pixels), last_event->location()); EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, root_location_pixels), last_event->root_location()); @@ -2416,8 +2461,8 @@ TEST_F(WindowTreeClientClientTestHighDPI, InputEventsInDip) { child2.SetBounds(gfx::Rect(20, 30, 100, 100)); child2.Show(); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); // child1 has a custom targeter set which would always return itself as the // target window therefore event should go to child1 and should be in dip. @@ -2425,19 +2470,26 @@ TEST_F(WindowTreeClientClientTestHighDPI, InputEventsInDip) { uint32_t event_id = 1; window_delegate1.set_event_id(event_id); window_delegate2.set_event_id(event_id); - std::unique_ptr<ui::Event> ui_event( - new ui::MouseEvent(ui::ET_MOUSE_MOVED, event_location_in_pixels, - gfx::Point(), ui::EventTimeForNow(), ui::EF_NONE, 0)); + std::unique_ptr<ui::Event> ui_event(new ui::MouseEvent( + ui::ET_MOUSE_MOVED, event_location_in_pixels, event_location_in_pixels, + ui::EventTimeForNow(), ui::EF_NONE, 0)); window_tree_client()->OnWindowInputEvent( event_id, server_id(&child1), window_tree_host.display_id(), ui::Event::Clone(*ui_event.get()), 0); EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); const gfx::Point event_location_in_dip(25, 30); EXPECT_EQ(event_location_in_dip, window_delegate1.last_event_location()); +#if defined(USE_OZONE) + // For ozone there should be NativeEvent. + EXPECT_TRUE(window_delegate1.last_mouse_event_had_native_event()); + // And the location of the NativeEvent should be in pixels. + EXPECT_EQ(event_location_in_pixels, + window_delegate1.last_native_event_location()); +#endif window_delegate1.reset(); window_delegate2.reset(); @@ -2451,8 +2503,8 @@ TEST_F(WindowTreeClientClientTestHighDPI, InputEventsInDip) { EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); gfx::Point transformed_event_location_in_dip(event_location_in_dip.x() + 20, event_location_in_dip.y() + 30); EXPECT_EQ(transformed_event_location_in_dip, @@ -2473,4 +2525,76 @@ TEST_F(WindowTreeClientDestructionTest, Shutdown) { window2.Init(ui::LAYER_NOT_DRAWN); } +TEST_F(WindowTreeClientWmTest, ObservedPointerEvents) { + const gfx::Rect bounds(1, 2, 101, 102); + std::unique_ptr<DisplayInitParams> display_params = + base::MakeUnique<DisplayInitParams>(); + const int64_t display_id = 201; + float device_scale_factor = 2.0f; + float ui_scale_factor = 1.5f; + display_params->display = base::MakeUnique<display::Display>(display_id); + display_params->display->set_bounds(bounds); + display_params->viewport_metrics.bounds_in_pixels = bounds; + display_params->viewport_metrics.device_scale_factor = device_scale_factor; + display_params->viewport_metrics.ui_scale_factor = ui_scale_factor; + WindowTreeHostMusInitParams init_params = + WindowTreeClientPrivate(window_tree_client_impl()) + .CallCreateInitParamsForNewDisplay(); + init_params.display_id = display_id; + init_params.display_init_params = std::move(display_params); + + WindowTreeHostMus window_tree_host(std::move(init_params)); + window_tree_host.InitHost(); + gfx::Transform scale_transform; + scale_transform.Scale(ui_scale_factor, ui_scale_factor); + window_tree_host.window()->SetTransform(scale_transform); + window_tree_host.compositor()->SetScaleAndSize(device_scale_factor, + bounds.size()); + + // Start a pointer watcher for all events excluding move events. + window_tree_client_impl()->StartPointerWatcher(false /* want_moves */); + + // Simulate the server sending an observed event. + const gfx::Point location_pixels(10, 12); + const gfx::Point root_location_pixels(14, 16); + std::unique_ptr<ui::PointerEvent> pointer_event_down(new ui::PointerEvent( + ui::ET_POINTER_DOWN, location_pixels, root_location_pixels, + ui::EF_CONTROL_DOWN, 0, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1), + base::TimeTicks())); + std::unique_ptr<ui::PointerEvent> pointer_event_down2( + ui::Event::Clone(*pointer_event_down).release()->AsPointerEvent()); + window_tree_client()->OnPointerEventObserved(std::move(pointer_event_down), + 0u, display_id); + + ASSERT_FALSE(observed_pointer_events().empty()); + const ui::PointerEvent* last_event = observed_pointer_events().back().get(); + ASSERT_TRUE(last_event); + EXPECT_EQ(nullptr, last_event->target()); + // NOTE: the root and location are the same as there was no window supplied to + // OnPointerEventObserved(). + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor, + root_location_pixels), + last_event->location()); + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor, + root_location_pixels), + last_event->root_location()); + + observed_pointer_events().clear(); + window_tree_client()->OnPointerEventObserved( + std::move(pointer_event_down2), + WindowMus::Get(window_tree_host.window())->server_id(), display_id); + ASSERT_FALSE(observed_pointer_events().empty()); + last_event = observed_pointer_events().back().get(); + ASSERT_TRUE(last_event); + EXPECT_EQ(nullptr, last_event->target()); + // |location| from the server has already had |ui_scale_factor| applied, so + // it won't be reapplied here. + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor, location_pixels), + last_event->location()); + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor, + root_location_pixels), + last_event->root_location()); +} + } // 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 a529a0bcc3a..927258842c2 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus.cc +++ b/chromium/ui/aura/mus/window_tree_host_mus.cc @@ -41,8 +41,11 @@ WindowTreeHostMus::WindowTreeHostMus(WindowTreeHostMusInitParams init_params) delegate_(init_params.window_tree_client) { gfx::Rect bounds_in_pixels; display_init_params_ = std::move(init_params.display_init_params); - if (display_init_params_) + if (display_init_params_) { bounds_in_pixels = display_init_params_->viewport_metrics.bounds_in_pixels; + if (display_init_params_->display) + DCHECK_EQ(display_id_, display_init_params_->display->id()); + } window()->SetProperty(kWindowTreeHostMusKey, this); // TODO(sky): find a cleaner way to set this! Better solution is to likely // have constructor take aura::Window. @@ -84,6 +87,12 @@ WindowTreeHostMus::WindowTreeHostMus(WindowTreeHostMusInitParams init_params) // Mus windows are assumed hidden. compositor()->SetVisible(false); + + if (window_mus->window_mus_type() == + WindowMusType::DISPLAY_MANUALLY_CREATED) { + compositor()->SetLocalSurfaceId( + window_mus->GetOrAllocateLocalSurfaceId(bounds_in_pixels.size())); + } } WindowTreeHostMus::~WindowTreeHostMus() { @@ -151,6 +160,12 @@ void WindowTreeHostMus::CancelWindowMove() { delegate_->OnWindowTreeHostCancelWindowMove(this); } +void WindowTreeHostMus::ConfineCursorToBounds( + const gfx::Rect& bounds_in_pixels) { + delegate_->OnWindowTreeHostConfineCursorToBounds(bounds_in_pixels, + display_id_); +} + display::Display WindowTreeHostMus::GetDisplay() const { display::Display display; display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id_, &display); @@ -195,13 +210,23 @@ void WindowTreeHostMus::OnCloseRequest() { void WindowTreeHostMus::MoveCursorToScreenLocationInPixels( const gfx::Point& location_in_pixels) { - gfx::Point screen_location_in_pixels = location_in_pixels; - gfx::Point location = GetLocationOnScreenInPixels(); - screen_location_in_pixels.Offset(-location.x(), -location.y()); - delegate_->OnWindowTreeHostMoveCursorToDisplayLocation( - screen_location_in_pixels, display_id_); + // |location_in_pixels| is relative to the display. + delegate_->OnWindowTreeHostMoveCursorToDisplayLocation(location_in_pixels, + display_id_); +} - Env::GetInstance()->set_last_mouse_location(location_in_pixels); +gfx::Transform WindowTreeHostMus::GetRootTransformForLocalEventCoordinates() + const { + if (WindowMus::Get(window())->window_mus_type() != + WindowMusType::DISPLAY_MANUALLY_CREATED) { + return WindowTreeHost::GetRootTransformForLocalEventCoordinates(); + } + // Local events already have the transform set on the window applied, so + // don't apply it again. + gfx::Transform transform; + const float scale = window()->layer()->device_scale_factor(); + transform.Scale(scale, scale); + return transform; } } // namespace aura diff --git a/chromium/ui/aura/mus/window_tree_host_mus.h b/chromium/ui/aura/mus/window_tree_host_mus.h index 764a7b0e91d..2ab21f4c626 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus.h +++ b/chromium/ui/aura/mus/window_tree_host_mus.h @@ -80,6 +80,9 @@ class AURA_EXPORT WindowTreeHostMus : public aura::WindowTreeHostPlatform { // PerformWindowMove(). void CancelWindowMove(); + // Tells the window manager to confine the cursor to these specific bounds. + void ConfineCursorToBounds(const gfx::Rect& pixel_bounds); + // Used during initial setup. Returns the DisplayInitParams // supplied to the constructor. std::unique_ptr<DisplayInitParams> ReleaseDisplayInitParams(); @@ -98,6 +101,7 @@ class AURA_EXPORT WindowTreeHostMus : public aura::WindowTreeHostPlatform { void OnCloseRequest() override; void MoveCursorToScreenLocationInPixels( const gfx::Point& location_in_pixels) override; + gfx::Transform GetRootTransformForLocalEventCoordinates() const override; private: int64_t display_id_; diff --git a/chromium/ui/aura/mus/window_tree_host_mus_delegate.h b/chromium/ui/aura/mus/window_tree_host_mus_delegate.h index 4ea7c2bf60c..b80e3389182 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus_delegate.h +++ b/chromium/ui/aura/mus/window_tree_host_mus_delegate.h @@ -76,6 +76,12 @@ class AURA_EXPORT WindowTreeHostMusDelegate { const gfx::Point& location_in_pixels, int64_t display_id) = 0; + // Called to confine the cursor to a set of bounds in pixels. Only available + // to the window manager. + virtual void OnWindowTreeHostConfineCursorToBounds( + const gfx::Rect& bounds_in_pixels, + int64_t display_id) = 0; + // Called when a WindowTreeHostMus is created without a WindowPort. // TODO: this should take an unordered_map, see http://crbug.com/670515. virtual std::unique_ptr<WindowPortMus> CreateWindowPortForTopLevel( diff --git a/chromium/ui/aura/window.cc b/chromium/ui/aura/window.cc index 5697e03d5fd..e618ea8d511 100644 --- a/chromium/ui/aura/window.cc +++ b/chromium/ui/aura/window.cc @@ -20,6 +20,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "cc/output/layer_tree_frame_sink.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/cursor_client.h" @@ -64,7 +65,8 @@ Window::Window(WindowDelegate* delegate, visible_(false), id_(kInitialId), transparent_(false), - ignore_events_(false), + event_targeting_policy_( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS), // Don't notify newly added observers during notification. This causes // problems for code that adds an observer as part of an observer // notification (such as the workspace code). @@ -482,6 +484,15 @@ bool Window::HasObserver(const WindowObserver* observer) const { return observers_.HasObserver(observer); } +void Window::SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy policy) { + if (event_targeting_policy_ == policy) + return; + + event_targeting_policy_ = policy; + if (port_) + port_->OnEventTargetingPolicyChanged(); +} + bool Window::ContainsPointInRoot(const gfx::Point& point_in_root) const { const Window* root_window = GetRootWindow(); if (!root_window) @@ -745,8 +756,10 @@ Window* Window::GetWindowForPoint(const gfx::Point& local_point, Window* child = *it; if (for_event_handling) { - if (child->ignore_events_) + if (child->event_targeting_policy_ == + ui::mojom::EventTargetingPolicy::NONE) { continue; + } // The client may not allow events to be processed by certain subtrees. client::EventClient* client = client::GetEventClient(GetRootWindow()); @@ -764,8 +777,23 @@ Window* Window::GetWindowForPoint(const gfx::Point& local_point, Window* match = child->GetWindowForPoint(point_in_child_coords, return_tightest, for_event_handling); - if (match) - return match; + if (!match) + continue; + + switch (child->event_targeting_policy_) { + case ui::mojom::EventTargetingPolicy::TARGET_ONLY: + if (child->delegate_) + return child; + break; + case ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS: + return match; + case ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY: + if (match != child) + return match; + break; + case ui::mojom::EventTargetingPolicy::NONE: + NOTREACHED(); // This case is handled early on. + } } return delegate_ ? this : nullptr; @@ -990,6 +1018,18 @@ viz::SurfaceId Window::GetSurfaceId() const { return port_->GetSurfaceId(); } +void Window::AllocateLocalSurfaceId() { + port_->AllocateLocalSurfaceId(); +} + +const viz::LocalSurfaceId& Window::GetLocalSurfaceId() const { + return port_->GetLocalSurfaceId(); +} + +viz::FrameSinkId Window::GetFrameSinkId() const { + return port_->GetFrameSinkId(); +} + void Window::OnPaintLayer(const ui::PaintContext& context) { Paint(context); } diff --git a/chromium/ui/aura/window.h b/chromium/ui/aura/window.h index cd6d841fc59..15009ff8b4e 100644 --- a/chromium/ui/aura/window.h +++ b/chromium/ui/aura/window.h @@ -45,6 +45,9 @@ class Transform; namespace ui { class Layer; +namespace mojom { +enum class EventTargetingPolicy; +} } namespace aura { @@ -241,8 +244,10 @@ class AURA_EXPORT Window : public ui::LayerDelegate, void RemoveObserver(WindowObserver* observer); bool HasObserver(const WindowObserver* observer) const; - void set_ignore_events(bool ignore_events) { ignore_events_ = ignore_events; } - bool ignore_events() const { return ignore_events_; } + void SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy policy); + ui::mojom::EventTargetingPolicy event_targeting_policy() const { + return event_targeting_policy_; + } // Returns true if the |point_in_root| in root window's coordinate falls // within this window's bounds. Returns false if the window is detached @@ -312,9 +317,22 @@ class AURA_EXPORT Window : public ui::LayerDelegate, // Create a LayerTreeFrameSink for the aura::Window. std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink(); - // Get the current viz::SurfaceId. + // Gets the current viz::SurfaceId. viz::SurfaceId GetSurfaceId() const; + // Forces the window to allocate a new viz::LocalSurfaceId for the next + // CompositorFrame submission in anticipation of a synchronization operation + // that does not involve a resize or a device scale factor change. + void AllocateLocalSurfaceId(); + + // Gets the current viz::LocalSurfaceId. + const viz::LocalSurfaceId& GetLocalSurfaceId() const; + + // Returns the FrameSinkId. In LOCAL mode, this returns a valid FrameSinkId + // only if a LayerTreeFrameSink has been created. In MUS mode, this always + // return a valid FrameSinkId. + viz::FrameSinkId GetFrameSinkId() const; + protected: // Deletes (or removes if not owned by parent) all child windows. Intended for // use from the destructor. @@ -327,6 +345,7 @@ class AURA_EXPORT Window : public ui::LayerDelegate, int64_t old_value, std::unique_ptr<ui::PropertyData> data) override; private: + friend class HitTestDataProviderAura; friend class LayoutManager; friend class PropertyConverter; friend class WindowPort; @@ -480,7 +499,7 @@ class AURA_EXPORT Window : public ui::LayerDelegate, std::unique_ptr<ui::EventTargeter> targeter_; // Makes the window pass all events through to any windows behind it. - bool ignore_events_; + ui::mojom::EventTargetingPolicy event_targeting_policy_; base::ObserverList<WindowObserver, true> observers_; diff --git a/chromium/ui/aura/window_delegate.h b/chromium/ui/aura/window_delegate.h index 4ef1c7cfcbd..eac0589bf44 100644 --- a/chromium/ui/aura/window_delegate.h +++ b/chromium/ui/aura/window_delegate.h @@ -98,7 +98,9 @@ class AURA_EXPORT WindowDelegate : public ui::EventHandler { // above returns true. virtual void GetHitTestMask(gfx::Path* mask) const = 0; - virtual void OnWindowSurfaceChanged(const viz::SurfaceInfo& surface_info) {} + // Called when a child submits a CompositorFrame to a surface with the given + // |surface_info| for the first time. + virtual void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {} protected: ~WindowDelegate() override {} diff --git a/chromium/ui/aura/window_event_dispatcher.cc b/chromium/ui/aura/window_event_dispatcher.cc index b6088e10469..9b3aa8fe2ba 100644 --- a/chromium/ui/aura/window_event_dispatcher.cc +++ b/chromium/ui/aura/window_event_dispatcher.cc @@ -11,6 +11,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/client/event_client.h" @@ -68,13 +69,9 @@ void ConvertEventLocationToTarget(ui::EventTarget* event_target, return; gfx::Point location = event->AsLocatedEvent()->location(); - gfx::Point root_location = event->AsLocatedEvent()->root_location(); Window::ConvertPointToTarget(static_cast<Window*>(event_target), static_cast<Window*>(target), &location); - Window::ConvertPointToTarget(static_cast<Window*>(event_target), - static_cast<Window*>(target), &root_location); event->AsLocatedEvent()->set_location(location); - event->AsLocatedEvent()->set_root_location(root_location); } } // namespace @@ -260,7 +257,9 @@ const Window* WindowEventDispatcher::window() const { void WindowEventDispatcher::TransformEventForDeviceScaleFactor( ui::LocatedEvent* event) { - event->UpdateForRootTransform(host_->GetInverseRootTransform()); + event->UpdateForRootTransform( + host_->GetInverseRootTransform(), + host_->GetInverseRootTransformForLocalEventCoordinates()); } void WindowEventDispatcher::DispatchMouseExitToHidingWindow(Window* window) { @@ -584,7 +583,9 @@ void WindowEventDispatcher::DispatchSyntheticTouchEvent(ui::TouchEvent* event) { // the pointer, in dips. OnEventFromSource expects events with co-ordinates // in raw pixels, so we convert back to raw pixels here. DCHECK(event->type() == ui::ET_TOUCH_CANCELLED); - event->UpdateForRootTransform(host_->GetRootTransform()); + event->UpdateForRootTransform( + host_->GetRootTransform(), + host_->GetRootTransformForLocalEventCoordinates()); DispatchDetails details = OnEventFromSource(event); if (details.dispatcher_destroyed) return; @@ -670,7 +671,8 @@ void WindowEventDispatcher::OnWindowBoundsChanged(Window* window, synthesize_mouse_move_ = false; } - if (window->IsVisible() && !window->ignore_events()) { + if (window->IsVisible() && window->event_targeting_policy() != + ui::mojom::EventTargetingPolicy::NONE) { gfx::Rect old_bounds_in_root = old_bounds, new_bounds_in_root = new_bounds; Window::ConvertRectToTarget(window->parent(), host_->window(), &old_bounds_in_root); @@ -772,6 +774,14 @@ ui::EventDispatchDetails WindowEventDispatcher::SynthesizeMouseMoveEvent() { return details; synthesize_mouse_move_ = false; + // No need to generate mouse event if the cursor is invisible. + client::CursorClient* cursor_client = + client::GetCursorClient(host_->window()); + if (cursor_client && (!cursor_client->IsMouseEventsEnabled() || + !cursor_client->IsCursorVisible())) { + return details; + } + // If one of the mouse buttons is currently down, then do not synthesize a // mouse-move event. In such cases, aura could synthesize a DRAGGED event // instead of a MOVED event, but in multi-display/multi-host scenarios, the diff --git a/chromium/ui/aura/window_event_dispatcher_unittest.cc b/chromium/ui/aura/window_event_dispatcher_unittest.cc index b02fde1b611..2f5e9f1275a 100644 --- a/chromium/ui/aura/window_event_dispatcher_unittest.cc +++ b/chromium/ui/aura/window_event_dispatcher_unittest.cc @@ -18,6 +18,7 @@ #include "base/test/histogram_tester.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/event_client.h" @@ -1122,6 +1123,9 @@ TEST_P(WindowEventDispatcherTest, DoNotSynthesizeWhileButtonDown) { // Flaky on 32-bit Windows bots. http://crbug.com/388272 TEST_P(WindowEventDispatcherTest, MAYBE(SynthesizeMouseEventsOnWindowBoundsChanged)) { + test::TestCursorClient cursor_client(root_window()); + cursor_client.ShowCursor(); + test::TestWindowDelegate delegate; std::unique_ptr<aura::Window> window(CreateTestWindowWithDelegate( &delegate, 1234, gfx::Rect(5, 5, 100, 100), root_window())); @@ -1150,7 +1154,7 @@ TEST_P(WindowEventDispatcherTest, recorder.Reset(); // Set window to ignore events. - window->set_ignore_events(true); + window->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); // Update the window bounds so that cursor is back inside the window. // This should not trigger a synthetic event. @@ -1161,7 +1165,8 @@ TEST_P(WindowEventDispatcherTest, recorder.Reset(); // Set window to accept events but invisible. - window->set_ignore_events(false); + window->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); window->Hide(); recorder.Reset(); @@ -1170,6 +1175,31 @@ TEST_P(WindowEventDispatcherTest, window->SetBounds(bounds1); RunAllPendingInMessageLoop(); EXPECT_TRUE(recorder.events().empty()); + + // Hide the cursor. None of the following scenario should trigger + // a synthetic event. + cursor_client.HideCursor(); + + window->Show(); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(recorder.events().empty()); + + window->SetBounds(bounds2); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(recorder.events().empty()); + + window->SetBounds(bounds1); + RunAllPendingInMessageLoop(); + EXPECT_TRUE(recorder.events().empty()); + + cursor_client.ShowCursor(); + window->SetBounds(bounds2); + RunAllPendingInMessageLoop(); + ASSERT_FALSE(recorder.events().empty()); + ASSERT_FALSE(recorder.mouse_event_flags().empty()); + EXPECT_EQ(ui::ET_MOUSE_MOVED, recorder.events().back()); + EXPECT_EQ(ui::EF_IS_SYNTHESIZED, recorder.mouse_event_flags().back()); + recorder.Reset(); } // Tests that a mouse exit is dispatched to the last known cursor location @@ -2053,7 +2083,7 @@ class ExitMessageLoopOnMousePress : public ui::test::TestEventHandler { void OnMouseEvent(ui::MouseEvent* event) override { ui::test::TestEventHandler::OnMouseEvent(event); if (event->type() == ui::ET_MOUSE_PRESSED) - base::MessageLoopForUI::current()->QuitWhenIdle(); + base::RunLoop::QuitCurrentWhenIdleDeprecated(); } private: @@ -2877,6 +2907,65 @@ TEST_F(WindowEventDispatcherMusTest, UseDefaultTargeterToFindTarget2) { EXPECT_EQ(gfx::Point(), last_event_location_delegate2.last_mouse_location()); } +namespace { + +class LocationRecordingEventHandler : public ui::EventHandler { + public: + LocationRecordingEventHandler() = default; + ~LocationRecordingEventHandler() override = default; + + const gfx::Point& event_root_location() const { return event_root_location_; } + + const gfx::Point& env_root_location() const { return env_root_location_; } + + int mouse_event_count() const { return mouse_event_count_; } + + // ui::EventHandler: + void OnMouseEvent(ui::MouseEvent* event) override { + ++mouse_event_count_; + event_root_location_ = event->root_location(); + env_root_location_ = Env::GetInstance()->last_mouse_location(); + } + + private: + int mouse_event_count_ = 0; + gfx::Point event_root_location_; + gfx::Point env_root_location_; + + DISALLOW_COPY_AND_ASSIGN(LocationRecordingEventHandler); +}; + +} // namespace + +TEST_F(WindowEventDispatcherMusTest, RootLocationDoesntChange) { + std::unique_ptr<Window> window( + test::CreateTestWindowWithBounds(gfx::Rect(0, 0, 10, 20), root_window())); + std::unique_ptr<Window> child_window( + test::CreateTestWindowWithBounds(gfx::Rect(5, 6, 10, 20), window.get())); + + test::EnvTestHelper().SetAlwaysUseLastMouseLocation(false); + + LocationRecordingEventHandler event_handler; + root_window()->AddPreTargetHandler(&event_handler); + + const gfx::Point mouse_location(1, 2); + gfx::Point root_location(mouse_location); + + ui::MouseEvent mouse(ui::ET_MOUSE_PRESSED, mouse_location, root_location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); + ui::Event::DispatcherApi(&mouse).set_target(child_window.get()); + DispatchEventUsingWindowDispatcher(&mouse); + EXPECT_EQ(1, event_handler.mouse_event_count()); + + // The root location during dispatch of the event and Env should match the + // one that was dispatched. + EXPECT_EQ(root_location, event_handler.event_root_location()); + EXPECT_EQ(root_location, event_handler.env_root_location()); + + root_window()->RemovePreTargetHandler(&event_handler); +} + class NestedLocationDelegate : public test::TestWindowDelegate { public: NestedLocationDelegate() {} diff --git a/chromium/ui/aura/window_port.h b/chromium/ui/aura/window_port.h index 2834ff96319..47a6d22627d 100644 --- a/chromium/ui/aura/window_port.h +++ b/chromium/ui/aura/window_port.h @@ -87,9 +87,24 @@ class AURA_EXPORT WindowPort { // Get the current viz::SurfaceId. virtual viz::SurfaceId GetSurfaceId() const = 0; + // Forces the window to allocate a new viz::LocalSurfaceId for the next + // CompositorFrame submission in anticipation of a synchronization operation + // that does not involve a resize or a device scale factor change. + virtual void AllocateLocalSurfaceId() = 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. + virtual const viz::LocalSurfaceId& GetLocalSurfaceId() = 0; + + // This can return invalid FrameSinkId. + virtual viz::FrameSinkId GetFrameSinkId() const = 0; + virtual void OnWindowAddedToRootWindow() = 0; virtual void OnWillRemoveWindowFromRootWindow() = 0; + virtual void OnEventTargetingPolicyChanged() = 0; + protected: // Returns the WindowPort associated with a Window. static WindowPort* Get(Window* window); diff --git a/chromium/ui/aura/window_port_for_shutdown.cc b/chromium/ui/aura/window_port_for_shutdown.cc index 054342eb174..73fd7a98a80 100644 --- a/chromium/ui/aura/window_port_for_shutdown.cc +++ b/chromium/ui/aura/window_port_for_shutdown.cc @@ -60,8 +60,20 @@ viz::SurfaceId WindowPortForShutdown::GetSurfaceId() const { return viz::SurfaceId(); } +void WindowPortForShutdown::AllocateLocalSurfaceId() {} + +const viz::LocalSurfaceId& WindowPortForShutdown::GetLocalSurfaceId() { + return local_surface_id_; +} + +viz::FrameSinkId WindowPortForShutdown::GetFrameSinkId() const { + return frame_sink_id_; +} + void WindowPortForShutdown::OnWindowAddedToRootWindow() {} void WindowPortForShutdown::OnWillRemoveWindowFromRootWindow() {} +void WindowPortForShutdown::OnEventTargetingPolicyChanged() {} + } // namespace aura diff --git a/chromium/ui/aura/window_port_for_shutdown.h b/chromium/ui/aura/window_port_for_shutdown.h index 2d369f7a79f..0807e61aa57 100644 --- a/chromium/ui/aura/window_port_for_shutdown.h +++ b/chromium/ui/aura/window_port_for_shutdown.h @@ -7,6 +7,8 @@ #include "ui/aura/window_port.h" +#include "components/viz/common/surfaces/local_surface_id.h" + namespace aura { // When WindowTreeClient is destroyed any existing windows get a @@ -37,10 +39,16 @@ class WindowPortForShutdown : public WindowPort { std::unique_ptr<ui::PropertyData> data) override; std::unique_ptr<cc::LayerTreeFrameSink> CreateLayerTreeFrameSink() override; viz::SurfaceId GetSurfaceId() const override; + void AllocateLocalSurfaceId() override; + const viz::LocalSurfaceId& GetLocalSurfaceId() override; + viz::FrameSinkId GetFrameSinkId() const override; void OnWindowAddedToRootWindow() override; void OnWillRemoveWindowFromRootWindow() override; + void OnEventTargetingPolicyChanged() override; private: + viz::LocalSurfaceId local_surface_id_; + viz::FrameSinkId frame_sink_id_; DISALLOW_COPY_AND_ASSIGN(WindowPortForShutdown); }; diff --git a/chromium/ui/aura/window_targeter.cc b/chromium/ui/aura/window_targeter.cc index 5b3a9885cfe..8be8e145d7d 100644 --- a/chromium/ui/aura/window_targeter.cc +++ b/chromium/ui/aura/window_targeter.cc @@ -4,6 +4,7 @@ #include "ui/aura/window_targeter.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/event_client.h" #include "ui/aura/client/focus_client.h" @@ -26,6 +27,26 @@ bool WindowTargeter::SubtreeShouldBeExploredForEvent( EventLocationInsideBounds(window, event); } +bool WindowTargeter::GetHitTestRects(Window* window, + gfx::Rect* hit_test_rect_mouse, + gfx::Rect* hit_test_rect_touch) const { + DCHECK(hit_test_rect_mouse); + DCHECK(hit_test_rect_touch); + *hit_test_rect_mouse = *hit_test_rect_touch = window->bounds(); + + if (ShouldUseExtendedBounds(window)) { + hit_test_rect_mouse->Inset(mouse_extend_); + hit_test_rect_touch->Inset(touch_extend_); + } + + return true; +} + +std::unique_ptr<WindowTargeter::HitTestRects> +WindowTargeter::GetExtraHitTestShapeRects(Window* target) const { + return nullptr; +} + Window* WindowTargeter::FindTargetInRootWindow(Window* root_window, const ui::LocatedEvent& event) { DCHECK_EQ(root_window, root_window->GetRootWindow()); @@ -83,8 +104,10 @@ ui::EventTarget* WindowTargeter::FindTargetForEvent(ui::EventTarget* root, // applying the host's transform. ui::LocatedEvent* located_event = static_cast<ui::LocatedEvent*>(event); located_event->ConvertLocationToTarget(target, new_root); + WindowTreeHost* window_tree_host = new_root->GetHost(); located_event->UpdateForRootTransform( - new_root->GetHost()->GetRootTransform()); + window_tree_host->GetRootTransform(), + window_tree_host->GetRootTransformForLocalEventCoordinates()); } ignore_result(new_root->GetHost()->event_sink()->OnEventFromSource(event)); @@ -116,8 +139,12 @@ bool WindowTargeter::SubtreeCanAcceptEvent( const ui::LocatedEvent& event) const { if (!window->IsVisible()) return false; - if (window->ignore_events()) + if (window->event_targeting_policy() == + ui::mojom::EventTargetingPolicy::NONE || + window->event_targeting_policy() == + ui::mojom::EventTargetingPolicy::TARGET_ONLY) { return false; + } client::EventClient* client = client::GetEventClient(window->GetRootWindow()); if (client && !client->CanProcessEventsWithinSubtree(window)) return false; @@ -134,10 +161,48 @@ bool WindowTargeter::SubtreeCanAcceptEvent( bool WindowTargeter::EventLocationInsideBounds( Window* window, const ui::LocatedEvent& event) const { + gfx::Rect mouse_rect; + gfx::Rect touch_rect; + if (!GetHitTestRects(window, &mouse_rect, &touch_rect)) + return false; + + const gfx::Vector2d offset = -window->bounds().OffsetFromOrigin(); + mouse_rect.Offset(offset); + touch_rect.Offset(offset); gfx::Point point = event.location(); if (window->parent()) Window::ConvertPointToTarget(window->parent(), window, &point); - return gfx::Rect(window->bounds().size()).Contains(point); + + const bool point_in_rect = event.IsTouchEvent() || event.IsGestureEvent() + ? touch_rect.Contains(point) + : mouse_rect.Contains(point); + if (!point_in_rect) + return false; + + auto shape_rects = GetExtraHitTestShapeRects(window); + if (!shape_rects) + return true; + + for (const gfx::Rect& shape_rect : *shape_rects) { + if (shape_rect.Contains(point)) { + return true; + } + } + + return false; +} + +bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* window) const { + return true; +} + +void WindowTargeter::OnSetInsets() {} + +void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend, + const gfx::Insets& touch_extend) { + mouse_extend_ = mouse_extend; + touch_extend_ = touch_extend; + OnSetInsets(); } Window* WindowTargeter::FindTargetForKeyEvent(Window* window, diff --git a/chromium/ui/aura/window_targeter.h b/chromium/ui/aura/window_targeter.h index 83fdcde3b80..cb07c106c62 100644 --- a/chromium/ui/aura/window_targeter.h +++ b/chromium/ui/aura/window_targeter.h @@ -5,9 +5,17 @@ #ifndef UI_AURA_WINDOW_TARGETER_H_ #define UI_AURA_WINDOW_TARGETER_H_ +#include <memory> +#include <vector> + #include "base/macros.h" #include "ui/aura/aura_export.h" #include "ui/events/event_targeter.h" +#include "ui/gfx/geometry/insets.h" + +namespace gfx { +class Rect; +} namespace ui { class KeyEvent; @@ -23,6 +31,8 @@ class AURA_EXPORT WindowTargeter : public ui::EventTargeter { WindowTargeter(); ~WindowTargeter() override; + using HitTestRects = std::vector<gfx::Rect>; + // Returns true if |window| or one of its descendants can be a target of // |event|. This requires that |window| and its descendants are not // prohibited from accepting the event, and that the event is within an @@ -31,6 +41,25 @@ class AURA_EXPORT WindowTargeter : public ui::EventTargeter { virtual bool SubtreeShouldBeExploredForEvent(Window* window, const ui::LocatedEvent& event); + // Returns true if the |target| is accepting LocatedEvents, false otherwise. + // |hit_test_rect_mouse| and |hit_test_rect_touch| must be not null and return + // the bounds that can be used for hit testing. The default implementation + // extends the |target|'s |bounds()| by insets provided with SetInsets(). + // This can be used to extend the hit-test area for touch events and make + // targeting windows with imprecise input devices easier. + // Returned rectangles are in |target|'s parent's coordinates. + virtual bool GetHitTestRects(Window* target, + gfx::Rect* hit_test_rect_mouse, + gfx::Rect* hit_test_rect_touch) const; + + // Returns additional hit-test areas or nullptr when there are none. Used when + // a window needs a complex shape hit-test area. This additional area is + // clipped to |hit_test_rect_mouse| returned by GetHitTestRects or the window + // bounds when GetHitTestRects is not overridden. + // Returned rectangles are in |target|'s coordinates. + virtual std::unique_ptr<HitTestRects> GetExtraHitTestShapeRects( + Window* target) const; + Window* FindTargetInRootWindow(Window* root_window, const ui::LocatedEvent& event); @@ -60,15 +89,35 @@ class AURA_EXPORT WindowTargeter : public ui::EventTargeter { // Returns whether the location of the event is in an actionable region of the // target. Note that the location etc. of |event| is in the |window|'s // parent's coordinate system. + // Deprecated. As an alternative, override GetHitTestRects. + // TODO(varkha): Make this non-overridable. virtual bool EventLocationInsideBounds(Window* target, const ui::LocatedEvent& event) const; + // Returns true if the hit testing (GetHitTestRects()) should use the + // extended bounds. + virtual bool ShouldUseExtendedBounds(const aura::Window* window) const; + + // Called after the hit-test area has been extended with SetInsets(). + virtual void OnSetInsets(); + + // 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_; } + private: Window* FindTargetForKeyEvent(Window* root_window, const ui::KeyEvent& event); Window* FindTargetForNonKeyEvent(Window* root_window, ui::Event* event); Window* FindTargetForLocatedEventRecursively(Window* root_window, ui::LocatedEvent* event); + gfx::Insets mouse_extend_; + gfx::Insets touch_extend_; + DISALLOW_COPY_AND_ASSIGN(WindowTargeter); }; diff --git a/chromium/ui/aura/window_tree_host.cc b/chromium/ui/aura/window_tree_host.cc index b47bcc61b00..d73bbda0ec5 100644 --- a/chromium/ui/aura/window_tree_host.cc +++ b/chromium/ui/aura/window_tree_host.cc @@ -7,7 +7,7 @@ #include "base/command_line.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" -#include "cc/base/switches.h" +#include "components/viz/common/switches.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/env.h" @@ -20,6 +20,7 @@ #include "ui/base/ime/input_method_factory.h" #include "ui/base/layout.h" #include "ui/base/view_prop.h" +#include "ui/compositor/compositor_switches.h" #include "ui/compositor/dip_util.h" #include "ui/compositor/layer.h" #include "ui/display/display.h" @@ -96,6 +97,20 @@ gfx::Transform WindowTreeHost::GetInverseRootTransform() const { return invert; } +gfx::Transform WindowTreeHost::GetRootTransformForLocalEventCoordinates() + const { + return GetRootTransform(); +} + +gfx::Transform WindowTreeHost::GetInverseRootTransformForLocalEventCoordinates() + const { + gfx::Transform invert; + gfx::Transform transform = GetRootTransformForLocalEventCoordinates(); + if (!transform.GetInverse(&invert)) + return transform; + return invert; +} + void WindowTreeHost::SetOutputSurfacePaddingInPixels( const gfx::Insets& padding_in_pixels) { if (output_surface_padding_in_pixels_ == padding_in_pixels) @@ -255,7 +270,8 @@ void WindowTreeHost::DestroyDispatcher() { //window()->RemoveOrDestroyChildren(); } -void WindowTreeHost::CreateCompositor(const viz::FrameSinkId& frame_sink_id) { +void WindowTreeHost::CreateCompositor(const viz::FrameSinkId& frame_sink_id, + bool force_software_compositor) { DCHECK(Env::GetInstance()); ui::ContextFactory* context_factory = Env::GetInstance()->context_factory(); DCHECK(context_factory); @@ -264,13 +280,14 @@ void WindowTreeHost::CreateCompositor(const viz::FrameSinkId& frame_sink_id) { bool enable_surface_synchronization = aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS || base::CommandLine::ForCurrentProcess()->HasSwitch( - cc::switches::kEnableSurfaceSynchronization); + switches::kEnableSurfaceSynchronization); compositor_.reset(new ui::Compositor( (!context_factory_private || frame_sink_id.is_valid()) ? frame_sink_id : context_factory_private->AllocateFrameSinkId(), context_factory, context_factory_private, - base::ThreadTaskRunnerHandle::Get(), enable_surface_synchronization)); + base::ThreadTaskRunnerHandle::Get(), enable_surface_synchronization, + ui::IsPixelCanvasRecordingEnabled(), false, force_software_compositor)); if (!dispatcher()) { window()->Init(ui::LAYER_NOT_DRAWN); window()->set_host(this); @@ -280,10 +297,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()); + GetBoundsInPixels().size(), + window()->GetLocalSurfaceId()); compositor_->SetRootLayer(window()->layer()); compositor_->SetDisplayColorSpace(display.color_space()); } @@ -308,10 +327,11 @@ void WindowTreeHost::OnHostResizedInPixels( gfx::Size adjusted_size(new_size_in_pixels); adjusted_size.Enlarge(output_surface_padding_in_pixels_.width(), output_surface_padding_in_pixels_.height()); + // 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); + adjusted_size, window()->GetLocalSurfaceId()); gfx::Size layer_size = GetBoundsInPixels().size(); // The layer, and the observers should be notified of the @@ -326,6 +346,14 @@ void WindowTreeHost::OnHostWorkspaceChanged() { observer.OnHostWorkspaceChanged(this); } +void WindowTreeHost::OnHostDisplayChanged() { + if (!compositor_) + return; + display::Display display = + display::Screen::GetScreen()->GetDisplayNearestWindow(window()); + compositor_->SetDisplayColorSpace(display.color_space()); +} + void WindowTreeHost::OnHostCloseRequested() { for (WindowTreeHostObserver& observer : observers_) observer.OnHostCloseRequested(this); @@ -354,7 +382,7 @@ void WindowTreeHost::OnDisplayMetricsChanged(const display::Display& display, if (metrics & DisplayObserver::DISPLAY_METRIC_COLOR_SPACE) { display::Screen* screen = display::Screen::GetScreen(); if (compositor_ && - display.id() != screen->GetDisplayNearestView(window()).id()) { + display.id() == screen->GetDisplayNearestView(window()).id()) { compositor_->SetDisplayColorSpace(display.color_space()); } } diff --git a/chromium/ui/aura/window_tree_host.h b/chromium/ui/aura/window_tree_host.h index f0757f56ef4..a436fca5d35 100644 --- a/chromium/ui/aura/window_tree_host.h +++ b/chromium/ui/aura/window_tree_host.h @@ -83,6 +83,13 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, virtual void SetRootTransform(const gfx::Transform& transform); virtual gfx::Transform GetInverseRootTransform() const; + // These functions are used in event translation for translating the local + // coordinates of LocatedEvents. Default implementation calls to non-event + // ones (e.g. GetRootTransform()). + virtual gfx::Transform GetRootTransformForLocalEventCoordinates() const; + 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 @@ -195,7 +202,8 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, // If frame_sink_id is not passed in, one will be grabbed from // ContextFactoryPrivate. void CreateCompositor( - const viz::FrameSinkId& frame_sink_id = viz::FrameSinkId()); + const viz::FrameSinkId& frame_sink_id = viz::FrameSinkId(), + bool force_software_compositor = false); void InitCompositor(); void OnAcceleratedWidgetAvailable(); @@ -206,6 +214,7 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, void OnHostMovedInPixels(const gfx::Point& new_location_in_pixels); void OnHostResizedInPixels(const gfx::Size& new_size_in_pixels); void OnHostWorkspaceChanged(); + void OnHostDisplayChanged(); void OnHostCloseRequested(); void OnHostActivated(); void OnHostLostWindowCapture(); diff --git a/chromium/ui/aura/window_tree_host_mac.h b/chromium/ui/aura/window_tree_host_mac.h deleted file mode 100644 index 7ae5efd57c0..00000000000 --- a/chromium/ui/aura/window_tree_host_mac.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_AURA_WINDOW_TREE_HOST_MAC_H_ -#define UI_AURA_WINDOW_TREE_HOST_MAC_H_ - -#include <vector> - -#include "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#include "ui/aura/aura_export.h" -#include "ui/aura/window_tree_host.h" -#include "ui/gfx/geometry/insets.h" -#include "ui/gfx/geometry/rect.h" - -namespace aura { - -class AURA_EXPORT WindowTreeHostMac : public WindowTreeHost { - public: - explicit WindowTreeHostMac(const gfx::Rect& bounds); - ~WindowTreeHostMac() override; - - private: - // WindowTreeHost Overrides. - ui::EventSource* GetEventSource() override; - gfx::AcceleratedWidget GetAcceleratedWidget() override; - void Show() override; - void Hide() override; - void ToggleFullScreen() override; - gfx::Rect GetBounds() const override; - void SetBounds(const gfx::Rect& bounds) override; - gfx::Insets GetInsets() const override; - void SetInsets(const gfx::Insets& insets) override; - gfx::Point GetLocationOnNativeScreen() const override; - void SetCapture() override; - void ReleaseCapture() override; - bool ConfineCursorToRootWindow() override; - void UnConfineCursor() override; - void SetCursorNative(gfx::NativeCursor cursor_type) override; - void MoveCursorToNative(const gfx::Point& location) override; - void OnCursorVisibilityChangedNative(bool show) override; - void OnDeviceScaleFactorChanged(float device_scale_factor) override; - - private: - base::scoped_nsobject<NSWindow> window_; - - DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMac); -}; - -} // namespace aura - -#endif // UI_AURA_WINDOW_TREE_HOST_MAC_H_ diff --git a/chromium/ui/aura/window_tree_host_mac.mm b/chromium/ui/aura/window_tree_host_mac.mm deleted file mode 100644 index c6748a6076f..00000000000 --- a/chromium/ui/aura/window_tree_host_mac.mm +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <Cocoa/Cocoa.h> - -#include "ui/aura/window_tree_host.h" -#include "ui/aura/window_tree_host_mac.h" - -namespace aura { - -WindowTreeHostMac::WindowTreeHostMac(const gfx::Rect& bounds) { - window_.reset( - [[NSWindow alloc] - initWithContentRect:NSRectFromCGRect(bounds.ToCGRect()) - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]); - CreateCompositor(); - OnAcceleratedWidgetAvailable(); -} - -WindowTreeHostMac::~WindowTreeHostMac() { - DestroyDispatcher(); -} - -EventSource* WindowTreeHostMac::GetEventSource() { - NOTIMPLEMENTED(); - return nil; -} - -gfx::AcceleratedWidget WindowTreeHostMac::GetAcceleratedWidget() { - return [window_ contentView]; -} -void WindowTreeHostMac::ShowImpl() { - [window_ makeKeyAndOrderFront:nil]; -} - -void WindowTreeHostMac::HideImpl() { - [window_ orderOut:nil]; -} - -void WindowTreeHostMac::ToggleFullScreen() { -} - -gfx::Rect WindowTreeHostMac::GetBounds() const { - return gfx::Rect(NSRectToCGRect([window_ frame])); -} - -void WindowTreeHostMac::SetBounds(const gfx::Rect& bounds) { - [window_ setFrame:NSRectFromCGRect(bounds.ToCGRect()) display:YES animate:NO]; -} - -gfx::Insets WindowTreeHostMac::GetInsets() const { - NOTIMPLEMENTED(); - return gfx::Insets(); -} - -void WindowTreeHostMac::SetInsets(const gfx::Insets& insets) { - NOTIMPLEMENTED(); -} - -gfx::Point WindowTreeHostMac::GetLocationOnNativeScreen() const { - NOTIMPLEMENTED(); - return gfx::Point(0, 0); -} - -void WindowTreeHostMac::SetCapture() { - NOTIMPLEMENTED(); -} - -void WindowTreeHostMac::ReleaseCapture() { - NOTIMPLEMENTED(); -} - -bool WindowTreeHostMac::ConfineCursorToRootWindow() { - return false; -} - -void WindowTreeHostMac::UnConfineCursor() { - NOTIMPLEMENTED(); -} - -void WindowTreeHostMac::SetCursorNative(gfx::NativeCursor cursor_type) { - NOTIMPLEMENTED(); -} - -void WindowTreeHostMac::MoveCursorToNative(const gfx::Point& location) { - NOTIMPLEMENTED(); -} - -void WindowTreeHostMac::OnCursorVisibilityChangedNative(bool show) { - NOTIMPLEMENTED(); -} - -void WindowTreeHostMac::OnDeviceScaleFactorChanged(float device_scale_factor) { - NOTIMPLEMENTED(); -} - -// static -WindowTreeHost* WindowTreeHost::Create(const gfx::Rect& bounds) { - return new WindowTreeHostMac(bounds); -} - -} // namespace aura diff --git a/chromium/ui/aura/window_tree_host_observer.h b/chromium/ui/aura/window_tree_host_observer.h index f5c112ad8df..e71455dc743 100644 --- a/chromium/ui/aura/window_tree_host_observer.h +++ b/chromium/ui/aura/window_tree_host_observer.h @@ -17,17 +17,17 @@ class WindowTreeHost; class AURA_EXPORT WindowTreeHostObserver { public: // Called when the host's client size has changed. - virtual void OnHostResized(const WindowTreeHost* host) {} + virtual void OnHostResized(WindowTreeHost* host) {} // Called when the host is moved on screen. - virtual void OnHostMovedInPixels(const WindowTreeHost* host, + virtual void OnHostMovedInPixels(WindowTreeHost* host, const gfx::Point& new_origin_in_pixels) {} // Called when the host is moved to a different workspace. - virtual void OnHostWorkspaceChanged(const WindowTreeHost* host) {} + virtual void OnHostWorkspaceChanged(WindowTreeHost* host) {} // Called when the native window system sends the host request to close. - virtual void OnHostCloseRequested(const WindowTreeHost* host) {} + virtual void OnHostCloseRequested(WindowTreeHost* host) {} protected: virtual ~WindowTreeHostObserver() {} diff --git a/chromium/ui/aura/window_tree_host_platform.cc b/chromium/ui/aura/window_tree_host_platform.cc index 8cb7263bdaa..16027faaa71 100644 --- a/chromium/ui/aura/window_tree_host_platform.cc +++ b/chromium/ui/aura/window_tree_host_platform.cc @@ -6,6 +6,7 @@ #include <utility> +#include "base/run_loop.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "ui/aura/window_event_dispatcher.h" @@ -23,7 +24,6 @@ #endif #if defined(OS_WIN) -#include "base/message_loop/message_loop.h" #include "ui/base/cursor/cursor_loader_win.h" #include "ui/platform_window/win/win_window.h" #endif @@ -158,7 +158,7 @@ void WindowTreeHostPlatform::DispatchEvent(ui::Event* event) { void WindowTreeHostPlatform::OnCloseRequest() { #if defined(OS_WIN) // TODO: this obviously shouldn't be here. - base::MessageLoopForUI::current()->QuitWhenIdle(); + base::RunLoop::QuitCurrentWhenIdleDeprecated(); #else OnHostCloseRequested(); #endif diff --git a/chromium/ui/aura/window_tree_host_platform.h b/chromium/ui/aura/window_tree_host_platform.h index 07340787579..13ef6ca1e57 100644 --- a/chromium/ui/aura/window_tree_host_platform.h +++ b/chromium/ui/aura/window_tree_host_platform.h @@ -21,9 +21,8 @@ class WindowPort; // The unified WindowTreeHost implementation for platforms // that implement PlatformWindow. -class AURA_EXPORT WindowTreeHostPlatform - : public WindowTreeHost, - public NON_EXPORTED_BASE(ui::PlatformWindowDelegate) { +class AURA_EXPORT WindowTreeHostPlatform : public WindowTreeHost, + public ui::PlatformWindowDelegate { public: explicit WindowTreeHostPlatform(const gfx::Rect& bounds); ~WindowTreeHostPlatform() override; diff --git a/chromium/ui/aura/window_tree_host_unittest.cc b/chromium/ui/aura/window_tree_host_unittest.cc index 735d26debfb..3c4900bd758 100644 --- a/chromium/ui/aura/window_tree_host_unittest.cc +++ b/chromium/ui/aura/window_tree_host_unittest.cc @@ -86,4 +86,12 @@ TEST_F(WindowTreeHostTest, NoRewritesPostIME) { host()->RemoveEventRewriter(&event_rewriter); } +TEST_F(WindowTreeHostTest, ColorSpace) { + EXPECT_EQ(gfx::ColorSpace::CreateSRGB(), + host()->compositor()->output_color_space()); + test_screen()->SetColorSpace(gfx::ColorSpace::CreateSCRGBLinear()); + EXPECT_EQ(gfx::ColorSpace::CreateSCRGBLinear(), + host()->compositor()->output_color_space()); +} + } // namespace aura diff --git a/chromium/ui/aura/window_tree_host_x11.cc b/chromium/ui/aura/window_tree_host_x11.cc index d57358cf49a..cd4d4487e87 100644 --- a/chromium/ui/aura/window_tree_host_x11.cc +++ b/chromium/ui/aura/window_tree_host_x11.cc @@ -288,8 +288,16 @@ uint32_t WindowTreeHostX11::DispatchEvent(const ui::PlatformEvent& event) { // It's possible that the X window may be resized by some other means // than from within aura (e.g. the X window manager can change the // size). Make sure the root window size is maintained properly. - gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, - xev->xconfigure.width, xev->xconfigure.height); + int translated_x = xev->xconfigure.x; + int translated_y = xev->xconfigure.y; + if (!xev->xconfigure.send_event && !xev->xconfigure.override_redirect) { + ::Window unused; + XTranslateCoordinates(xdisplay_, xwindow_, x_root_window_, 0, 0, + &translated_x, &translated_y, &unused); + } + gfx::Rect bounds(translated_x, translated_y, xev->xconfigure.width, + xev->xconfigure.height); + bool size_changed = bounds_.size() != bounds.size(); bool origin_changed = bounds_.origin() != bounds.origin(); bounds_ = bounds; diff --git a/chromium/ui/aura/window_unittest.cc b/chromium/ui/aura/window_unittest.cc index afe9120cbef..68fb78b987f 100644 --- a/chromium/ui/aura/window_unittest.cc +++ b/chromium/ui/aura/window_unittest.cc @@ -16,6 +16,8 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" +#include "cc/output/layer_tree_frame_sink.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/focus_change_observer.h" @@ -1544,7 +1546,7 @@ TEST_P(WindowTest, Visibility) { EXPECT_EQ(1, d2.shown()); } -TEST_P(WindowTest, IgnoreEventsTest) { +TEST_P(WindowTest, EventTargetingPolicy) { TestWindowDelegate d11; TestWindowDelegate d12; TestWindowDelegate d111; @@ -1560,18 +1562,32 @@ TEST_P(WindowTest, IgnoreEventsTest) { std::unique_ptr<Window> w121(CreateTestWindowWithDelegate( &d121, 121, gfx::Rect(150, 150, 50, 50), w12.get())); + EXPECT_EQ(w121.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); + w12->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::TARGET_ONLY); + EXPECT_EQ(w12.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); + w12->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); + EXPECT_EQ(w12.get(), w1->GetEventHandlerForPoint(gfx::Point(10, 10))); - w12->set_ignore_events(true); + w12->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); EXPECT_EQ(w11.get(), w1->GetEventHandlerForPoint(gfx::Point(10, 10))); - w12->set_ignore_events(false); + w12->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); EXPECT_EQ(w121.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); - w121->set_ignore_events(true); + w121->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); EXPECT_EQ(w12.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); - w12->set_ignore_events(true); + w12->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); EXPECT_EQ(w111.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); - w111->set_ignore_events(true); + w111->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); EXPECT_EQ(w11.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); + + w11->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY); + EXPECT_EQ(nullptr, w1->GetEventHandlerForPoint(gfx::Point(160, 160))); + w111->SetEventTargetingPolicy( + ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); + EXPECT_EQ(w111.get(), w1->GetEventHandlerForPoint(gfx::Point(160, 160))); } // Tests transformation on the root window. @@ -2850,6 +2866,33 @@ TEST_P(WindowTest, WindowDestroyCompletesAnimations) { animator->RemoveObserver(&observer); } +TEST_P(WindowTest, LocalSurfaceIdChanges) { + Window window(nullptr); + window.Init(ui::LAYER_NOT_DRAWN); + std::unique_ptr<cc::LayerTreeFrameSink> frame_sink( + window.CreateLayerTreeFrameSink()); + viz::LocalSurfaceId local_surface_id1 = window.GetLocalSurfaceId(); + EXPECT_NE(nullptr, frame_sink.get()); + EXPECT_TRUE(local_surface_id1.is_valid()); + + window.SetBounds(gfx::Rect(300, 300)); + viz::LocalSurfaceId local_surface_id2 = window.GetLocalSurfaceId(); + EXPECT_TRUE(local_surface_id2.is_valid()); + EXPECT_NE(local_surface_id1, local_surface_id2); + + window.OnDeviceScaleFactorChanged(3.0f); + viz::LocalSurfaceId local_surface_id3 = window.GetLocalSurfaceId(); + EXPECT_TRUE(local_surface_id3.is_valid()); + EXPECT_NE(local_surface_id1, local_surface_id3); + EXPECT_NE(local_surface_id2, local_surface_id3); + + window.AllocateLocalSurfaceId(); + viz::LocalSurfaceId local_surface_id4 = window.GetLocalSurfaceId(); + EXPECT_NE(local_surface_id1, local_surface_id4); + EXPECT_NE(local_surface_id2, local_surface_id4); + EXPECT_NE(local_surface_id3, local_surface_id4); +} + INSTANTIATE_TEST_CASE_P(/* no prefix */, WindowTest, ::testing::Values(BackendType::CLASSIC, diff --git a/chromium/ui/base/BUILD.gn b/chromium/ui/base/BUILD.gn index 119d0fab47c..4210c32aa44 100644 --- a/chromium/ui/base/BUILD.gn +++ b/chromium/ui/base/BUILD.gn @@ -67,8 +67,6 @@ component("base") { "accelerators/platform_accelerator.h", "accelerators/platform_accelerator_cocoa.h", "accelerators/platform_accelerator_cocoa.mm", - "android/ui_base_jni_registrar.cc", - "android/ui_base_jni_registrar.h", "class_property.cc", "class_property.h", "clipboard/clipboard_android.cc", @@ -379,8 +377,7 @@ component("base") { if (is_fuchsia) { sources += [ - "clipboard/clipboard_fuchsia.cc", - "clipboard/clipboard_fuchsia.h", + "idle/idle_fuchsia.cc", "resource/resource_bundle_fuchsia.cc", ] } @@ -584,10 +581,13 @@ component("base") { ] deps += [ - "//ui/events/ozone:events_ozone_evdev", "//ui/events/ozone:events_ozone_layout", "//ui/ozone:ozone_base", ] + + if (!is_fuchsia) { + deps += [ "//ui/events/ozone:events_ozone_evdev" ] + } } if (!toolkit_views && !use_aura) { @@ -788,6 +788,7 @@ test("ui_base_unittests") { "material_design/material_design_controller_unittest.cc", "models/tree_node_iterator_unittest.cc", "resource/data_pack_literal.cc", + "resource/data_pack_literal.h", "resource/data_pack_unittest.cc", "resource/resource_bundle_unittest.cc", "resource/scale_factor_unittest.cc", @@ -834,6 +835,7 @@ test("ui_base_unittests") { "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", "cocoa/nsgraphics_context_additions_unittest.mm", @@ -868,7 +870,7 @@ test("ui_base_unittests") { if (use_x11) { sources += [ "ime/composition_text_util_pango_unittest.cc" ] } - if (!use_x11 && is_chromeos) { + if (is_chromeos) { # These were already removed in the non-chromeos case. sources -= [ "ime/input_method_chromeos_unittest.cc" ] } @@ -881,6 +883,7 @@ test("ui_base_unittests") { ":ui_base_unittests_bundle_data", "//base", "//base/test:test_support", + "//mojo/edk/system", "//net", "//skia", "//testing/gmock", @@ -971,7 +974,7 @@ test("ui_base_unittests") { ] } - if (use_x11 && !is_chromeos) { + if (use_x11) { sources += [ "dragdrop/os_exchange_data_provider_aurax11_unittest.cc", "x/selection_requestor_unittest.cc", diff --git a/chromium/ui/base/accelerators/accelerator_manager.cc b/chromium/ui/base/accelerators/accelerator_manager.cc index d5c8a5bec16..b604c019ccc 100644 --- a/chromium/ui/base/accelerators/accelerator_manager.cc +++ b/chromium/ui/base/accelerators/accelerator_manager.cc @@ -88,22 +88,22 @@ bool AcceleratorManager::IsRegistered(const Accelerator& accelerator) const { } bool AcceleratorManager::Process(const Accelerator& accelerator) { - bool result = false; AcceleratorMap::iterator map_iter = accelerators_.find(accelerator); - if (map_iter != accelerators_.end()) { - // We have to copy the target list here, because an AcceleratorPressed - // event handler may modify the list. - AcceleratorTargetList targets(map_iter->second.second); - for (AcceleratorTargetList::iterator iter = targets.begin(); - iter != targets.end(); ++iter) { - if ((*iter)->CanHandleAccelerators() && - (*iter)->AcceleratorPressed(accelerator)) { - result = true; - break; - } + if (map_iter == accelerators_.end()) + return false; + + // We have to copy the target list here, because an AcceleratorPressed + // event handler may modify the list. + AcceleratorTargetList targets(map_iter->second.second); + for (AcceleratorTargetList::iterator iter = targets.begin(); + iter != targets.end(); ++iter) { + if ((*iter)->CanHandleAccelerators() && + (*iter)->AcceleratorPressed(accelerator)) { + return true; } } - return result; + + return false; } bool AcceleratorManager::HasPriorityHandler( diff --git a/chromium/ui/base/accelerators/accelerator_manager.h b/chromium/ui/base/accelerators/accelerator_manager.h index c792b181f91..621454eb9ed 100644 --- a/chromium/ui/base/accelerators/accelerator_manager.h +++ b/chromium/ui/base/accelerators/accelerator_manager.h @@ -69,7 +69,7 @@ class UI_BASE_EXPORT AcceleratorManager { // Returns whether |accelerator| is already registered. bool IsRegistered(const Accelerator& accelerator) const; - // Activate the target associated with the specified accelerator. + // Activates the target associated with the specified accelerator. // First, AcceleratorPressed handler of the most recently registered target // is called, and if that handler processes the event (i.e. returns true), // this method immediately returns. If not, we do the same thing on the next diff --git a/chromium/ui/base/accelerators/accelerator_manager_unittest.cc b/chromium/ui/base/accelerators/accelerator_manager_unittest.cc index 3cca7da2723..93752b37d4d 100644 --- a/chromium/ui/base/accelerators/accelerator_manager_unittest.cc +++ b/chromium/ui/base/accelerators/accelerator_manager_unittest.cc @@ -20,8 +20,8 @@ namespace { class TestTarget : public AcceleratorTarget { public: - TestTarget() : accelerator_pressed_count_(0) {} - ~TestTarget() override {} + TestTarget() = default; + ~TestTarget() override = default; int accelerator_pressed_count() const { return accelerator_pressed_count_; @@ -36,7 +36,7 @@ class TestTarget : public AcceleratorTarget { bool CanHandleAccelerators() const override; private: - int accelerator_pressed_count_; + int accelerator_pressed_count_ = 0; DISALLOW_COPY_AND_ASSIGN(TestTarget); }; diff --git a/chromium/ui/base/class_property.h b/chromium/ui/base/class_property.h index 4df498944b0..38fdb7eb7fa 100644 --- a/chromium/ui/base/class_property.h +++ b/chromium/ui/base/class_property.h @@ -11,6 +11,7 @@ #include <memory> #include <set> +#include "base/time/time.h" #include "ui/base/property_data.h" #include "ui/base/ui_base_export.h" #include "ui/base/ui_base_types.h" @@ -45,8 +46,9 @@ // // outside all namespaces: // DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(FOO_EXPORT, MyType) // -// If a property type is not exported, use DECLARE_CLASS_PROPERTY_TYPE(MyType) -// which is a shorthand for DECLARE_EXPORTED_CLASS_PROPERTY_TYPE(, MyType). +// If a property type is not exported, use +// DECLARE_UI_CLASS_PROPERTY_TYPE(MyType) which is a shorthand for +// DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(, MyType). namespace ui { @@ -144,6 +146,14 @@ class ClassPropertyCaster<bool> { static int64_t ToInt64(bool x) { return static_cast<int64_t>(x); } static bool FromInt64(int64_t x) { return x != 0; } }; +template <> +class ClassPropertyCaster<base::TimeDelta> { + public: + static int64_t ToInt64(base::TimeDelta x) { return x.InMicroseconds(); } + static base::TimeDelta FromInt64(int64_t x) { + return base::TimeDelta::FromMicroseconds(x); + } +}; } // namespace diff --git a/chromium/ui/base/class_property_unittest.cc b/chromium/ui/base/class_property_unittest.cc index 31e85efb709..dcbacd021a9 100644 --- a/chromium/ui/base/class_property_unittest.cc +++ b/chromium/ui/base/class_property_unittest.cc @@ -86,29 +86,31 @@ TEST(PropertyTest, Property) { } TEST(PropertyTest, OwnedProperty) { - std::unique_ptr<PropertyHandler> h = base::MakeUnique<PropertyHandler>(); - - EXPECT_EQ(NULL, h->GetProperty(kOwnedKey)); - void* last_deleted = TestProperty::last_deleted(); - TestProperty* p1 = new TestProperty(); - h->SetProperty(kOwnedKey, p1); - EXPECT_EQ(p1, h->GetProperty(kOwnedKey)); - EXPECT_EQ(last_deleted, TestProperty::last_deleted()); - - TestProperty* p2 = new TestProperty(); - h->SetProperty(kOwnedKey, p2); - EXPECT_EQ(p2, h->GetProperty(kOwnedKey)); - EXPECT_EQ(p1, TestProperty::last_deleted()); - - h->ClearProperty(kOwnedKey); - EXPECT_EQ(NULL, h->GetProperty(kOwnedKey)); - EXPECT_EQ(p2, TestProperty::last_deleted()); - - TestProperty* p3 = new TestProperty(); - h->SetProperty(kOwnedKey, p3); - EXPECT_EQ(p3, h->GetProperty(kOwnedKey)); - EXPECT_EQ(p2, TestProperty::last_deleted()); - h.reset(); + TestProperty* p3; + { + PropertyHandler h; + + EXPECT_EQ(NULL, h.GetProperty(kOwnedKey)); + void* last_deleted = TestProperty::last_deleted(); + TestProperty* p1 = new TestProperty(); + h.SetProperty(kOwnedKey, p1); + EXPECT_EQ(p1, h.GetProperty(kOwnedKey)); + EXPECT_EQ(last_deleted, TestProperty::last_deleted()); + + TestProperty* p2 = new TestProperty(); + h.SetProperty(kOwnedKey, p2); + EXPECT_EQ(p2, h.GetProperty(kOwnedKey)); + EXPECT_EQ(p1, TestProperty::last_deleted()); + + h.ClearProperty(kOwnedKey); + EXPECT_EQ(NULL, h.GetProperty(kOwnedKey)); + EXPECT_EQ(p2, TestProperty::last_deleted()); + + p3 = new TestProperty(); + h.SetProperty(kOwnedKey, p3); + EXPECT_EQ(p3, h.GetProperty(kOwnedKey)); + EXPECT_EQ(p2, TestProperty::last_deleted()); + } EXPECT_EQ(p3, TestProperty::last_deleted()); } diff --git a/chromium/ui/base/clipboard/clipboard.h b/chromium/ui/base/clipboard/clipboard.h index 8d0cd38f4a2..94236adca28 100644 --- a/chromium/ui/base/clipboard/clipboard.h +++ b/chromium/ui/base/clipboard/clipboard.h @@ -8,12 +8,12 @@ #include <stddef.h> #include <stdint.h> -#include <map> #include <memory> #include <string> #include <vector> #include "base/compiler_specific.h" +#include "base/containers/flat_map.h" #include "base/lazy_instance.h" #include "base/macros.h" #include "base/process/process.h" @@ -42,7 +42,7 @@ namespace ui { class TestClipboard; class ScopedClipboardWriter; -class UI_BASE_EXPORT Clipboard : NON_EXPORTED_BASE(public base::ThreadChecker) { +class UI_BASE_EXPORT Clipboard : public base::ThreadChecker { public: // MIME type constants. static const char kMimeTypeText[]; @@ -292,9 +292,9 @@ class UI_BASE_EXPORT Clipboard : NON_EXPORTED_BASE(public base::ThreadChecker) { // CBF_WEBKIT none empty vector // CBF_DATA format char array // data byte array - typedef std::vector<char> ObjectMapParam; - typedef std::vector<ObjectMapParam> ObjectMapParams; - typedef std::map<int /* ObjectType */, ObjectMapParams> ObjectMap; + using ObjectMapParam = std::vector<char>; + using ObjectMapParams = std::vector<ObjectMapParam>; + using ObjectMap = base::flat_map<int /* ObjectType */, ObjectMapParams>; // Write a bunch of objects to the system clipboard. Copies are made of the // contents of |objects|. @@ -337,13 +337,13 @@ class UI_BASE_EXPORT Clipboard : NON_EXPORTED_BASE(public base::ThreadChecker) { // A list of allowed threads. By default, this is empty and no thread checking // is done (in the unit test case), but a user (like content) can set which // threads are allowed to call this method. - typedef std::vector<base::PlatformThreadId> AllowedThreadsVector; + using AllowedThreadsVector = std::vector<base::PlatformThreadId>; static base::LazyInstance<AllowedThreadsVector>::DestructorAtExit allowed_threads_; // Mapping from threads to clipboard objects. - typedef std::map<base::PlatformThreadId, std::unique_ptr<Clipboard>> - ClipboardMap; + using ClipboardMap = + base::flat_map<base::PlatformThreadId, std::unique_ptr<Clipboard>>; static base::LazyInstance<ClipboardMap>::DestructorAtExit clipboard_map_; // Mutex that controls access to |g_clipboard_map|. diff --git a/chromium/ui/base/clipboard/clipboard_android.cc b/chromium/ui/base/clipboard/clipboard_android.cc index 849493e10fe..a151b9930a0 100644 --- a/chromium/ui/base/clipboard/clipboard_android.cc +++ b/chromium/ui/base/clipboard/clipboard_android.cc @@ -550,10 +550,6 @@ void ClipboardAndroid::WriteData(const Clipboard::FormatType& format, g_map.Get().Set(format.ToString(), std::string(data_data, data_len)); } -bool RegisterClipboardAndroid(JNIEnv* env) { - return RegisterNativesImpl(env); -} - // Returns a pointer to the current ClipboardAndroid object. static jlong Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj) { diff --git a/chromium/ui/base/clipboard/clipboard_android.h b/chromium/ui/base/clipboard/clipboard_android.h index 716cc387f74..1ef1bee944b 100644 --- a/chromium/ui/base/clipboard/clipboard_android.h +++ b/chromium/ui/base/clipboard/clipboard_android.h @@ -87,9 +87,6 @@ class ClipboardAndroid : public Clipboard { DISALLOW_COPY_AND_ASSIGN(ClipboardAndroid); }; -// Registers the ClipboardAndroid native method. -bool RegisterClipboardAndroid(JNIEnv* env); - } // namespace ui #endif // UI_BASE_CLIPBOARD_CLIPBOARD_ANDROID_H_ diff --git a/chromium/ui/base/clipboard/clipboard_fuchsia.cc b/chromium/ui/base/clipboard/clipboard_fuchsia.cc deleted file mode 100644 index 72de3f4daf1..00000000000 --- a/chromium/ui/base/clipboard/clipboard_fuchsia.cc +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/base/clipboard/clipboard_fuchsia.h" - -#include <algorithm> -#include <utility> - -#include "base/callback.h" -#include "base/lazy_instance.h" -#include "base/stl_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/synchronization/lock.h" -#include "base/time/time.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/geometry/size.h" - -namespace ui { - -const char kMimeTypeBitmap[] = "image/bmp"; - -// Clipboard::FormatType implementation. -Clipboard::FormatType::FormatType() {} - -Clipboard::FormatType::FormatType(const std::string& native_format) - : data_(native_format) {} - -Clipboard::FormatType::~FormatType() {} - -std::string Clipboard::FormatType::Serialize() const { - return data_; -} - -// static -Clipboard::FormatType Clipboard::FormatType::Deserialize( - const std::string& serialization) { - return FormatType(serialization); -} - -bool Clipboard::FormatType::operator<(const FormatType& other) const { - return data_ < other.data_; -} - -bool Clipboard::FormatType::Equals(const FormatType& other) const { - return data_ == other.data_; -} - -// Various predefined FormatTypes. -// static -Clipboard::FormatType Clipboard::GetFormatType( - const std::string& format_string) { - return FormatType::Deserialize(format_string); -} - -// static -const Clipboard::FormatType& Clipboard::GetUrlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList)); - return type; -} - -// static -const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { - return GetUrlFormatType(); -} - -// static -const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); - return type; -} - -// static -const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { - return GetPlainTextFormatType(); -} - -// static -const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); - return type; -} - -// static -const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); - return type; -} - -// static -const Clipboard::FormatType& Clipboard::GetRtfFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); - return type; -} - -// static -const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap)); - return type; -} - -// static -const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); - return type; -} - -// static -const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); - return type; -} - -// Clipboard factory method. -// static -Clipboard* Clipboard::Create() { - return new ClipboardFuchsia; -} - -// ClipboardFuchsia implementation. - -ClipboardFuchsia::ClipboardFuchsia() { - DCHECK(CalledOnValidThread()); -} - -ClipboardFuchsia::~ClipboardFuchsia() { - DCHECK(CalledOnValidThread()); -} - -void ClipboardFuchsia::OnPreShutdown() {} - -uint64_t ClipboardFuchsia::GetSequenceNumber(ClipboardType type) const { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); - return 0; -} - -bool ClipboardFuchsia::IsFormatAvailable(const Clipboard::FormatType& format, - ClipboardType type) const { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTIMPLEMENTED(); - return false; -} - -void ClipboardFuchsia::Clear(ClipboardType type) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::ReadAvailableTypes(ClipboardType type, - std::vector<base::string16>* types, - bool* contains_filenames) const { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTIMPLEMENTED(); - types->clear(); - *contains_filenames = false; -} - -void ClipboardFuchsia::ReadText(ClipboardType type, - base::string16* result) const { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTREACHED(); -} - -void ClipboardFuchsia::ReadAsciiText(ClipboardType type, - std::string* result) const { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTREACHED(); -} - -// Note: |src_url| isn't really used. It is only implemented in Windows -void ClipboardFuchsia::ReadHTML(ClipboardType type, - base::string16* markup, - std::string* src_url, - uint32_t* fragment_start, - uint32_t* fragment_end) const { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTREACHED(); -} - -void ClipboardFuchsia::ReadRTF(ClipboardType type, std::string* result) const { - DCHECK(CalledOnValidThread()); - NOTREACHED(); -} - -SkBitmap ClipboardFuchsia::ReadImage(ClipboardType type) const { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTREACHED(); - return SkBitmap(); -} - -void ClipboardFuchsia::ReadCustomData(ClipboardType clipboard_type, - const base::string16& type, - base::string16* result) const { - DCHECK(CalledOnValidThread()); - NOTREACHED(); -} - -void ClipboardFuchsia::ReadBookmark(base::string16* title, - std::string* url) const { - DCHECK(CalledOnValidThread()); - NOTREACHED(); -} - -void ClipboardFuchsia::ReadData(const Clipboard::FormatType& format, - std::string* result) const { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); - result->clear(); -} - -base::Time ClipboardFuchsia::GetLastModifiedTime() const { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); - return base::Time(); -} - -void ClipboardFuchsia::ClearLastModifiedTime() { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -// Main entry point used to write several values in the clipboard. -void ClipboardFuchsia::WriteObjects(ClipboardType type, - const ObjectMap& objects) { - DCHECK(CalledOnValidThread()); - DCHECK_EQ(type, CLIPBOARD_TYPE_COPY_PASTE); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::WriteText(const char* text_data, size_t text_len) { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::WriteHTML(const char* markup_data, - size_t markup_len, - const char* url_data, - size_t url_len) { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::WriteRTF(const char* rtf_data, size_t data_len) { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::WriteBookmark(const char* title_data, - size_t title_len, - const char* url_data, - size_t url_len) { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::WriteWebSmartPaste() { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::WriteBitmap(const SkBitmap& bitmap) { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -void ClipboardFuchsia::WriteData(const Clipboard::FormatType& format, - const char* data_data, - size_t data_len) { - DCHECK(CalledOnValidThread()); - NOTIMPLEMENTED(); -} - -} // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_fuchsia.h b/chromium/ui/base/clipboard/clipboard_fuchsia.h deleted file mode 100644 index ca68893aac9..00000000000 --- a/chromium/ui/base/clipboard/clipboard_fuchsia.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_CLIPBOARD_CLIPBOARD_FUCHSIA_H_ -#define UI_BASE_CLIPBOARD_CLIPBOARD_FUCHSIA_H_ - -#include "ui/base/clipboard/clipboard.h" - -#include "base/macros.h" -#include "base/time/time.h" - -namespace ui { - -// This is a stub implementation of Clipboard for Fuchsia. -// TODO(fuchsia): Implement this class. -class ClipboardFuchsia : public Clipboard { - private: - friend class Clipboard; - - ClipboardFuchsia(); - ~ClipboardFuchsia() override; - - // Clipboard overrides: - void OnPreShutdown() override; - uint64_t GetSequenceNumber(ClipboardType type) const override; - bool IsFormatAvailable(const FormatType& format, - ClipboardType type) const override; - void Clear(ClipboardType type) override; - void ReadAvailableTypes(ClipboardType type, - std::vector<base::string16>* types, - bool* contains_filenames) const override; - void ReadText(ClipboardType type, base::string16* result) const override; - void ReadAsciiText(ClipboardType type, std::string* result) const override; - void ReadHTML(ClipboardType type, - base::string16* markup, - std::string* src_url, - uint32_t* fragment_start, - uint32_t* fragment_end) const override; - void ReadRTF(ClipboardType type, std::string* result) const override; - SkBitmap ReadImage(ClipboardType type) const override; - void ReadCustomData(ClipboardType clipboard_type, - const base::string16& type, - base::string16* result) const override; - void ReadBookmark(base::string16* title, std::string* url) const override; - void ReadData(const FormatType& format, std::string* result) const override; - base::Time GetLastModifiedTime() const override; - void ClearLastModifiedTime() override; - void WriteObjects(ClipboardType type, const ObjectMap& objects) override; - void WriteText(const char* text_data, size_t text_len) override; - void WriteHTML(const char* markup_data, - size_t markup_len, - const char* url_data, - size_t url_len) override; - void WriteRTF(const char* rtf_data, size_t data_len) override; - void WriteBookmark(const char* title_data, - size_t title_len, - const char* url_data, - size_t url_len) override; - void WriteWebSmartPaste() override; - void WriteBitmap(const SkBitmap& bitmap) override; - void WriteData(const FormatType& format, - const char* data_data, - size_t data_len) override; - - DISALLOW_COPY_AND_ASSIGN(ClipboardFuchsia); -}; - -} // namespace ui - -#endif // UI_BASE_CLIPBOARD_CLIPBOARD_FUCHSIA_H_ diff --git a/chromium/ui/base/clipboard/clipboard_monitor.h b/chromium/ui/base/clipboard/clipboard_monitor.h index cd6ed1e13c3..1330aaf0764 100644 --- a/chromium/ui/base/clipboard/clipboard_monitor.h +++ b/chromium/ui/base/clipboard/clipboard_monitor.h @@ -17,8 +17,7 @@ class ClipboardObserver; // A singleton instance to monitor and notify ClipboardObservers for clipboard // changes. -class UI_BASE_EXPORT ClipboardMonitor - : NON_EXPORTED_BASE(public base::ThreadChecker) { +class UI_BASE_EXPORT ClipboardMonitor : public base::ThreadChecker { public: static ClipboardMonitor* GetInstance(); diff --git a/chromium/ui/base/cocoa/hover_button.h b/chromium/ui/base/cocoa/hover_button.h index b25e29aa3e1..5354bf7258a 100644 --- a/chromium/ui/base/cocoa/hover_button.h +++ b/chromium/ui/base/cocoa/hover_button.h @@ -10,6 +10,22 @@ #import "ui/base/cocoa/tracking_area.h" #import "ui/base/ui_base_export.h" +@class HoverButton; + +// Assign an object which conforms to this protocol to a HoverButton's +// dragDelegate property to make the button draggable. +UI_BASE_EXPORT +@protocol HoverButtonDragDelegate + +// When the user performs a drag on the HoverButton, this method will be called +// with the button and the mouse down event. The delegate is expected to begin +// a drag by calling -[NSView beginDraggingSessionWithItems:event:source:] with +// the event or run a nested tracking loop. When it returns, the HoverButton +// returns to kHoverStateNone and stops tracking the mouse. +- (void)beginDragFromHoverButton:(HoverButton*)button event:(NSEvent*)event; + +@end + // A button that changes when you hover over it and click it. UI_BASE_EXPORT @interface HoverButton : NSButton { @@ -32,6 +48,19 @@ UI_BASE_EXPORT @property(nonatomic) HoverState hoverState; +// Enables or disables the tracking for the button. +@property(nonatomic) BOOL trackingEnabled; + +// Assign an object to make the button a drag source. +@property(nonatomic, assign) id<HoverButtonDragDelegate> dragDelegate; + +// An NSRect in the view's coordinate space which is used for hover and hit +// testing. Default value is NSZeroRect, which makes the hitbox equal to the +// view's bounds. May be overridden by subclasses. Example: A button in the +// corner of a fullscreen window might extend its hitbox to the edges of the +// window so that it can be clicked more easily (Fitts's law). +@property(readonly, nonatomic) NSRect hitbox; + // Common initialization called from initWithFrame: and awakeFromNib. // Subclassers should call [super commonInit]. - (void)commonInit; @@ -39,9 +68,6 @@ UI_BASE_EXPORT // Text that would be announced by screen readers. - (void)setAccessibilityTitle:(NSString*)accessibilityTitle; -// Enables or disables the tracking for the button. -- (void)setTrackingEnabled:(BOOL)enabled; - // Checks to see whether the mouse is in the button's bounds and update // the image in case it gets out of sync. This occurs to the close button // when you close a tab so the tab to the left of it takes its place, and diff --git a/chromium/ui/base/cocoa/hover_button.mm b/chromium/ui/base/cocoa/hover_button.mm index ebcd582ea64..b8e9b1547a7 100644 --- a/chromium/ui/base/cocoa/hover_button.mm +++ b/chromium/ui/base/cocoa/hover_button.mm @@ -4,9 +4,20 @@ #import "ui/base/cocoa/hover_button.h" +#include <cmath> + +namespace { + +// Distance to start a drag when a dragDelegate is assigned. +constexpr CGFloat kDragDistance = 5; + +} // namespace + @implementation HoverButton @synthesize hoverState = hoverState_; +@synthesize trackingEnabled = trackingEnabled_; +@synthesize dragDelegate = dragDelegate_; - (id)initWithFrame:(NSRect)frameRect { if ((self = [super initWithFrame:frameRect])) { @@ -20,16 +31,33 @@ } - (void)commonInit { - [self setTrackingEnabled:YES]; self.hoverState = kHoverStateNone; - [self updateTrackingAreas]; + self.trackingEnabled = YES; } - (void)dealloc { - [self setTrackingEnabled:NO]; + self.trackingEnabled = NO; [super dealloc]; } +- (NSRect)hitbox { + return NSZeroRect; +} + +- (void)setTrackingEnabled:(BOOL)trackingEnabled { + if (trackingEnabled == trackingEnabled_) + return; + trackingEnabled_ = trackingEnabled; + [self updateTrackingAreas]; +} + +- (void)setEnabled:(BOOL)enabled { + if (enabled == self.enabled) + return; + super.enabled = enabled; + [self updateTrackingAreas]; +} + - (void)mouseEntered:(NSEvent*)theEvent { if (trackingArea_.get()) self.hoverState = kHoverStateMouseOver; @@ -45,6 +73,8 @@ } - (void)mouseDown:(NSEvent*)theEvent { + if (!self.enabled) + return; mouseDown_ = YES; self.hoverState = kHoverStateMouseDown; @@ -64,12 +94,21 @@ NSKeyDownMask | NSKeyUpMask); while ((nextEvent = [window nextEventMatchingMask:eventMask])) { + if ([nextEvent type] == NSLeftMouseUp) + break; // Update the image state, which will change if the user moves the mouse // into or out of the button. [self checkImageState]; - - if ([nextEvent type] == NSLeftMouseUp) { - break; + if (dragDelegate_ && [nextEvent type] == NSLeftMouseDragged) { + const NSPoint startPos = [theEvent locationInWindow]; + const NSPoint pos = [nextEvent locationInWindow]; + if (std::abs(startPos.x - pos.x) > kDragDistance || + std::abs(startPos.y - pos.y) > kDragDistance) { + [dragDelegate_ beginDragFromHoverButton:self event:nextEvent]; + mouseDown_ = NO; + self.hoverState = kHoverStateNone; + return; + } } } } @@ -77,11 +116,12 @@ // If the mouse is still over the button, it means the user clicked the // button. if (self.hoverState == kHoverStateMouseDown) { - [self performClick:nil]; + [self sendAction:self.action to:self.target]; } // Clean up. mouseDown_ = NO; + [self checkImageState]; } - (void)setAccessibilityTitle:(NSString*)accessibilityTitle { @@ -90,16 +130,22 @@ forAttribute:NSAccessibilityTitleAttribute]; } -- (void)setTrackingEnabled:(BOOL)enabled { - if (enabled) { - trackingArea_.reset( - [[CrTrackingArea alloc] initWithRect:NSZeroRect - options:NSTrackingMouseEnteredAndExited | - NSTrackingMouseMoved | - NSTrackingActiveAlways | - NSTrackingInVisibleRect - owner:self - userInfo:nil]); +- (void)updateTrackingAreas { + if (trackingEnabled_ && self.enabled) { + NSRect hitbox = self.hitbox; + if (CrTrackingArea* trackingArea = trackingArea_.get()) { + if (NSEqualRects(trackingArea.rect, hitbox)) + return; + [self removeTrackingArea:trackingArea]; + } + trackingArea_.reset([[CrTrackingArea alloc] + initWithRect:hitbox + options:NSTrackingMouseEnteredAndExited | + NSTrackingMouseMoved | + NSTrackingActiveAlways | + (NSIsEmptyRect(hitbox) ? NSTrackingInVisibleRect : 0) + owner:self + userInfo:nil]); [self addTrackingArea:trackingArea_.get()]; // If you have a separate window that overlaps the close button, and you @@ -119,9 +165,6 @@ trackingArea_.reset(nil); } } -} - -- (void)updateTrackingAreas { [super updateTrackingAreas]; [self checkImageState]; } @@ -130,10 +173,14 @@ if (!trackingArea_.get()) return; + NSEvent* currentEvent = [NSApp currentEvent]; + if (!currentEvent || currentEvent.window != self.window) + return; + // Update the button's state if the button has moved. - NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; - mouseLoc = [self convertPoint:mouseLoc fromView:nil]; - BOOL mouseInBounds = NSPointInRect(mouseLoc, [self bounds]); + const NSPoint mouseLoc = + [self.superview convertPoint:currentEvent.locationInWindow fromView:nil]; + BOOL mouseInBounds = [self hitTest:mouseLoc] != nil; if (mouseDown_ && mouseInBounds) { self.hoverState = kHoverStateMouseDown; } else { @@ -141,10 +188,19 @@ } } -- (void)setHoverState:(HoverState)state { - BOOL stateChanged = (hoverState_ != state); - hoverState_ = state; - [self setNeedsDisplay:stateChanged]; +- (void)setHoverState:(HoverState)hoverState { + if (hoverState == hoverState_) + return; + hoverState_ = hoverState; + self.needsDisplay = YES; +} + +- (NSView*)hitTest:(NSPoint)point { + if (NSPointInRect([self.superview convertPoint:point toView:self], + self.hitbox)) { + return self; + } + return [super hitTest:point]; } @end diff --git a/chromium/ui/base/cocoa/hover_button_unittest.mm b/chromium/ui/base/cocoa/hover_button_unittest.mm new file mode 100644 index 00000000000..54f15c24b67 --- /dev/null +++ b/chromium/ui/base/cocoa/hover_button_unittest.mm @@ -0,0 +1,199 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ui/base/cocoa/hover_button.h" + +#import <Cocoa/Cocoa.h> + +#import "ui/events/test/cocoa_test_event_utils.h" +#import "ui/gfx/test/ui_cocoa_test_helper.h" + +@interface TestHoverButton : HoverButton +@property(readwrite, nonatomic) NSRect hitbox; +@end + +@implementation TestHoverButton +@synthesize hitbox = hitbox_; + +- (void)setHitbox:(NSRect)hitbox { + hitbox_ = hitbox; + [self updateTrackingAreas]; +} +@end + +@interface HoverButtonTestTarget : NSObject +@property(nonatomic, copy) void (^actionHandler)(id); +@end + +@implementation HoverButtonTestTarget +@synthesize actionHandler = actionHandler_; + +- (void)dealloc { + [actionHandler_ release]; + [super dealloc]; +} + +- (IBAction)action:(id)sender { + actionHandler_(sender); +} +@end + +@interface HoverButtonTestDragDelegate : NSObject<HoverButtonDragDelegate> +@property(nonatomic, copy) void (^dragHandler)(HoverButton*, NSEvent*); +@end + +@implementation HoverButtonTestDragDelegate +@synthesize dragHandler = dragHandler_; + +- (void)dealloc { + [dragHandler_ release]; + [super dealloc]; +} + +- (void)beginDragFromHoverButton:(HoverButton*)button event:(NSEvent*)event { + dragHandler_(button, event); +} +@end + +namespace { + +class HoverButtonTest : public ui::CocoaTest { + public: + HoverButtonTest() { + NSRect frame = NSMakeRect(0, 0, 20, 20); + base::scoped_nsobject<TestHoverButton> button( + [[TestHoverButton alloc] initWithFrame:frame]); + button_ = button; + target_.reset([[HoverButtonTestTarget alloc] init]); + button_.target = target_; + button_.action = @selector(action:); + [[test_window() contentView] addSubview:button_]; + } + + protected: + void HoverAndExpect(HoverState hoverState) { + EXPECT_EQ(kHoverStateNone, button_.hoverState); + [button_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; + EXPECT_EQ(hoverState, button_.hoverState); + [button_ mouseExited:cocoa_test_event_utils::ExitEvent()]; + EXPECT_EQ(kHoverStateNone, button_.hoverState); + } + + bool HandleMouseDown(NSEvent* mouseDownEvent) { + __block bool action_sent = false; + target_.get().actionHandler = ^(id sender) { + action_sent = true; + EXPECT_EQ(kHoverStateMouseDown, button_.hoverState); + }; + [NSApp sendEvent:mouseDownEvent]; + target_.get().actionHandler = nil; + return action_sent; + } + + TestHoverButton* button_; // Weak, owned by test_window(). + base::scoped_nsobject<HoverButtonTestTarget> target_; +}; + +TEST_VIEW(HoverButtonTest, button_) + +TEST_F(HoverButtonTest, Hover) { + EXPECT_EQ(kHoverStateNone, button_.hoverState); + + // Default + HoverAndExpect(kHoverStateMouseOver); + + // Tracking disabled + button_.trackingEnabled = NO; + HoverAndExpect(kHoverStateNone); + button_.trackingEnabled = YES; + + // Button disabled + button_.enabled = NO; + HoverAndExpect(kHoverStateNone); + button_.enabled = YES; + + // Back to normal + HoverAndExpect(kHoverStateMouseOver); +} + +TEST_F(HoverButtonTest, Click) { + EXPECT_EQ(kHoverStateNone, button_.hoverState); + const auto click = cocoa_test_event_utils::MouseClickInView(button_, 1); + + [NSApp postEvent:click.second atStart:YES]; + EXPECT_TRUE(HandleMouseDown(click.first)); + + button_.enabled = NO; + EXPECT_FALSE(HandleMouseDown(click.first)); + + EXPECT_EQ(kHoverStateNone, button_.hoverState); +} + +TEST_F(HoverButtonTest, CustomHitbox) { + NSRect hitbox = button_.frame; + hitbox.size.width += 10; + + NSPoint inside_hit_point = + NSMakePoint(NSMaxX(button_.frame) + 5, NSMidY(button_.frame)); + NSPoint outside_hit_point = + NSMakePoint(inside_hit_point.x + 10, inside_hit_point.y); + + { + NSRect trackingRect = button_.trackingAreas[0].rect; + EXPECT_FALSE(NSPointInRect(inside_hit_point, trackingRect)); + EXPECT_FALSE(NSPointInRect(outside_hit_point, trackingRect)); + EXPECT_NE(button_, [button_ hitTest:inside_hit_point]); + EXPECT_EQ(nil, [button_ hitTest:outside_hit_point]); + } + + button_.hitbox = hitbox; + { + NSRect trackingRect = button_.trackingAreas[0].rect; + EXPECT_TRUE(NSPointInRect(inside_hit_point, trackingRect)); + EXPECT_FALSE(NSPointInRect(outside_hit_point, trackingRect)); + EXPECT_EQ(button_, [button_ hitTest:inside_hit_point]); + EXPECT_EQ(nil, [button_ hitTest:outside_hit_point]); + } + + button_.hitbox = NSZeroRect; + { + NSRect trackingRect = button_.trackingAreas[0].rect; + EXPECT_FALSE(NSPointInRect(inside_hit_point, trackingRect)); + EXPECT_FALSE(NSPointInRect(outside_hit_point, trackingRect)); + EXPECT_NE(button_, [button_ hitTest:inside_hit_point]); + EXPECT_EQ(nil, [button_ hitTest:outside_hit_point]); + } +} + +TEST_F(HoverButtonTest, DragDelegate) { + base::scoped_nsobject<HoverButtonTestDragDelegate> dragDelegate( + [[HoverButtonTestDragDelegate alloc] init]); + + __block bool dragged = false; + dragDelegate.get().dragHandler = ^(HoverButton* button, NSEvent* event) { + dragged = true; + }; + button_.dragDelegate = dragDelegate; + + const auto click = cocoa_test_event_utils::MouseClickInView(button_, 1); + NSPoint targetPoint = click.first.locationInWindow; + targetPoint.x += 5; // *Not* enough to trigger a drag. + [NSApp postEvent:cocoa_test_event_utils::MouseEventAtPointInWindow( + targetPoint, NSEventTypeLeftMouseDragged, + [button_ window], 1) + atStart:NO]; + [NSApp postEvent:click.second atStart:NO]; + EXPECT_TRUE(HandleMouseDown(click.first)); + EXPECT_FALSE(dragged); + + targetPoint.x += 1; // Now it's enough to trigger a drag. + [NSApp postEvent:cocoa_test_event_utils::MouseEventAtPointInWindow( + targetPoint, NSEventTypeLeftMouseDragged, + [button_ window], 1) + atStart:NO]; + [NSApp postEvent:click.second atStart:NO]; + EXPECT_FALSE(HandleMouseDown(click.first)); + EXPECT_TRUE(dragged); +} +} // namespace diff --git a/chromium/ui/base/cocoa/menu_controller.mm b/chromium/ui/base/cocoa/menu_controller.mm index 7e38840d22b..7f547003ba5 100644 --- a/chromium/ui/base/cocoa/menu_controller.mm +++ b/chromium/ui/base/cocoa/menu_controller.mm @@ -18,6 +18,33 @@ #include "ui/gfx/font_list.h" #include "ui/gfx/image/image.h" #include "ui/gfx/text_elider.h" +#include "ui/strings/grit/ui_strings.h" + +namespace { + +// Called when an empty submenu is created. This inserts a menu item labeled +// "(empty)" into the submenu. Matches Windows behavior. +NSMenu* MakeEmptySubmenu() { + base::scoped_nsobject<NSMenu> submenu([[NSMenu alloc] initWithTitle:@""]); + NSString* empty_menu_title = + l10n_util::GetNSString(IDS_APP_MENU_EMPTY_SUBMENU); + [submenu addItemWithTitle:empty_menu_title action:NULL keyEquivalent:@""]; + [[submenu itemAtIndex:0] setEnabled:NO]; + return submenu.autorelease(); +} + +// Called when adding a submenu to the menu and checks if the submenu, via its +// |model|, has visible child items. +bool MenuHasVisibleItems(const ui::MenuModel* model) { + int count = model->GetItemCount(); + for (int index = 0; index < count; index++) { + if (model->IsVisibleAt(index)) + return true; + } + return false; +} + +} // namespace NSString* const kMenuControllerMenuWillOpenNotification = @"MenuControllerMenuWillOpen"; @@ -144,13 +171,16 @@ NSString* const kMenuControllerMenuDidCloseNotification = [item setImage:icon.ToNSImage()]; ui::MenuModel::ItemType type = model->GetTypeAt(index); - if (type == ui::MenuModel::TYPE_SUBMENU) { - // Recursively build a submenu from the sub-model at this index. + if (type == ui::MenuModel::TYPE_SUBMENU && model->IsVisibleAt(index)) { + ui::MenuModel* submenuModel = model->GetSubmenuModelAt(index); + + // If there are visible items, recursively build the submenu. + NSMenu* submenu = MenuHasVisibleItems(submenuModel) + ? [self menuFromModel:submenuModel] + : MakeEmptySubmenu(); + [item setTarget:nil]; [item setAction:nil]; - ui::MenuModel* submenuModel = model->GetSubmenuModelAt(index); - NSMenu* submenu = - [self menuFromModel:(ui::SimpleMenuModel*)submenuModel]; [item setSubmenu:submenu]; } else { // The MenuModel works on indexes so we can't just set the command id as the diff --git a/chromium/ui/base/cocoa/menu_controller_unittest.mm b/chromium/ui/base/cocoa/menu_controller_unittest.mm index 9dffd0fce4b..f282e24b068 100644 --- a/chromium/ui/base/cocoa/menu_controller_unittest.mm +++ b/chromium/ui/base/cocoa/menu_controller_unittest.mm @@ -9,8 +9,10 @@ #include "base/run_loop.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" +#import "testing/gtest_mac.h" #include "third_party/skia/include/core/SkBitmap.h" #import "ui/base/cocoa/menu_controller.h" +#include "ui/base/l10n/l10n_util_mac.h" #include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/test/cocoa_test_event_utils.h" @@ -74,6 +76,50 @@ const int kTestLabelResourceId = IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE; class MenuControllerTest : public CocoaTest { }; +class TestSimpleMenuModelVisibility : public SimpleMenuModel { + public: + explicit TestSimpleMenuModelVisibility(SimpleMenuModel::Delegate* delegate) + : SimpleMenuModel(delegate) {} + + // SimpleMenuModel: + bool IsVisibleAt(int index) const override { + return items_[ValidateItemIndex(index)].visible; + } + + void SetVisibility(int command_id, bool visible) { + int index = SimpleMenuModel::GetIndexOfCommandId(command_id); + items_[ValidateItemIndex(index)].visible = visible; + } + + void AddItem(int command_id, const base::string16& label) { + SimpleMenuModel::AddItem(command_id, label); + items_.push_back({true, command_id}); + } + + void AddSubMenuWithStringId(int command_id, int string_id, MenuModel* model) { + SimpleMenuModel::AddSubMenuWithStringId(command_id, string_id, model); + items_.push_back({true, command_id}); + } + + private: + struct Item { + bool visible; + int command_id; + }; + + typedef std::vector<Item> ItemVector; + + int ValidateItemIndex(int index) const { + CHECK_GE(index, 0); + CHECK_LT(static_cast<size_t>(index), items_.size()); + return index; + } + + ItemVector items_; + + DISALLOW_COPY_AND_ASSIGN(TestSimpleMenuModelVisibility); +}; + // A menu delegate that counts the number of times certain things are called // to make sure things are hooked up properly. class Delegate : public SimpleMenuModel::Delegate { @@ -214,7 +260,7 @@ TEST_F(MenuControllerTest, EmptyMenu) { SimpleMenuModel model(&delegate); base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 0); + EXPECT_EQ(0, [[menu menu] numberOfItems]); } TEST_F(MenuControllerTest, BasicCreation) { @@ -229,14 +275,14 @@ TEST_F(MenuControllerTest, BasicCreation) { base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 6); + EXPECT_EQ(6, [[menu menu] numberOfItems]); // Check the title, tag, and represented object are correct for a random // element. NSMenuItem* itemTwo = [[menu menu] itemAtIndex:2]; NSString* title = [itemTwo title]; EXPECT_EQ(ASCIIToUTF16("three"), base::SysNSStringToUTF16(title)); - EXPECT_EQ([itemTwo tag], 2); + EXPECT_EQ(2, [itemTwo tag]); EXPECT_EQ([[itemTwo representedObject] pointerValue], &model); EXPECT_TRUE([[[menu menu] itemAtIndex:3] isSeparatorItem]); @@ -255,19 +301,19 @@ TEST_F(MenuControllerTest, Submenus) { base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 3); + EXPECT_EQ(3, [[menu menu] numberOfItems]); // Inspect the submenu to ensure it has correct properties. NSMenu* submenu = [[[menu menu] itemAtIndex:1] submenu]; EXPECT_TRUE(submenu); - EXPECT_EQ([submenu numberOfItems], 3); + EXPECT_EQ(3, [submenu numberOfItems]); // Inspect one of the items to make sure it has the correct model as its // represented object and the proper tag. NSMenuItem* submenuItem = [submenu itemAtIndex:1]; NSString* title = [submenuItem title]; EXPECT_EQ(ASCIIToUTF16("sub-two"), base::SysNSStringToUTF16(title)); - EXPECT_EQ([submenuItem tag], 1); + EXPECT_EQ(1, [submenuItem tag]); EXPECT_EQ([[submenuItem representedObject] pointerValue], &submodel); // Make sure the item after the submenu is correct and its represented @@ -275,7 +321,7 @@ TEST_F(MenuControllerTest, Submenus) { NSMenuItem* item = [[menu menu] itemAtIndex:2]; title = [item title]; EXPECT_EQ(ASCIIToUTF16("three"), base::SysNSStringToUTF16(title)); - EXPECT_EQ([item tag], 2); + EXPECT_EQ(2, [item tag]); EXPECT_EQ([[item representedObject] pointerValue], &model); } @@ -288,7 +334,88 @@ TEST_F(MenuControllerTest, EmptySubmenu) { base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 2); + EXPECT_EQ(2, [[menu menu] numberOfItems]); + + // Inspect the submenu to ensure it has one item labeled "(empty)". + NSMenu* submenu = [[[menu menu] itemAtIndex:1] submenu]; + EXPECT_TRUE(submenu); + EXPECT_EQ(1, [submenu numberOfItems]); + + EXPECT_NSEQ(@"(empty)", [[submenu itemAtIndex:0] title]); +} + +// Tests that an empty menu item, "(empty)", is added to a submenu that contains +// hidden child items. +TEST_F(MenuControllerTest, EmptySubmenuWhenAllChildItemsAreHidden) { + Delegate delegate; + TestSimpleMenuModelVisibility model(&delegate); + model.AddItem(1, ASCIIToUTF16("one")); + TestSimpleMenuModelVisibility submodel(&delegate); + // Hide the two child menu items. + submodel.AddItem(2, ASCIIToUTF16("sub-one")); + submodel.SetVisibility(2, false); + submodel.AddItem(3, ASCIIToUTF16("sub-two")); + submodel.SetVisibility(3, false); + model.AddSubMenuWithStringId(4, kTestLabelResourceId, &submodel); + + base::scoped_nsobject<MenuController> menu( + [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); + EXPECT_EQ(2, [[menu menu] numberOfItems]); + + // Inspect the submenu to ensure it has one item labeled "(empty)". + NSMenu* submenu = [[[menu menu] itemAtIndex:1] submenu]; + EXPECT_TRUE(submenu); + EXPECT_EQ(1, [submenu numberOfItems]); + + EXPECT_NSEQ(@"(empty)", [[submenu itemAtIndex:0] title]); +} + +// Tests hiding a submenu item. If a submenu item with children is set to +// hidden, then the submenu should hide. +TEST_F(MenuControllerTest, HiddenSubmenu) { + // SimpleMenuModel posts a task that calls Delegate::MenuClosed. Create + // a MessageLoop for that purpose. + base::MessageLoopForUI message_loop; + + // Create the model. + Delegate delegate; + TestSimpleMenuModelVisibility model(&delegate); + model.AddItem(1, ASCIIToUTF16("one")); + TestSimpleMenuModelVisibility submodel(&delegate); + submodel.AddItem(2, ASCIIToUTF16("sub-one")); + submodel.AddItem(3, ASCIIToUTF16("sub-two")); + // Set the submenu to be hidden. + model.AddSubMenuWithStringId(4, kTestLabelResourceId, &submodel); + + model.SetVisibility(4, false); + + // Create the controller. + base::scoped_nsobject<MenuController> menu_controller( + [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); + EXPECT_EQ(2, [[menu_controller menu] numberOfItems]); + delegate.menu_to_close_ = [menu_controller menu]; + + // Show the menu. + CFRunLoopPerformBlock(CFRunLoopGetCurrent(), NSEventTrackingRunLoopMode, ^{ + EXPECT_TRUE([menu_controller isMenuOpen]); + // Ensure that the submenu is hidden. + NSMenuItem* item = [[menu_controller menu] itemAtIndex:1]; + EXPECT_TRUE([item isHidden]); + }); + + // Pop open the menu, which will spin an event-tracking run loop. + [NSMenu popUpContextMenu:[menu_controller menu] + withEvent:cocoa_test_event_utils::RightMouseDownAtPoint( + NSZeroPoint) + forView:[test_window() contentView]]; + + EXPECT_FALSE([menu_controller isMenuOpen]); + + // Pump the task that notifies the delegate. + base::RunLoop().RunUntilIdle(); + + // Expect that the delegate got notified properly. + EXPECT_TRUE(delegate.did_close_); } TEST_F(MenuControllerTest, PopUpButton) { @@ -302,13 +429,13 @@ TEST_F(MenuControllerTest, PopUpButton) { // title. base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:YES]); - EXPECT_EQ([[menu menu] numberOfItems], 4); - EXPECT_EQ(base::SysNSStringToUTF16([[[menu menu] itemAtIndex:0] title]), - base::string16()); + EXPECT_EQ(4, [[menu menu] numberOfItems]); + EXPECT_EQ(base::string16(), + base::SysNSStringToUTF16([[[menu menu] itemAtIndex:0] title])); // Make sure the tags are still correct (the index no longer matches the tag). NSMenuItem* itemTwo = [[menu menu] itemAtIndex:2]; - EXPECT_EQ([itemTwo tag], 1); + EXPECT_EQ(1, [itemTwo tag]); } TEST_F(MenuControllerTest, Execute) { @@ -317,13 +444,13 @@ TEST_F(MenuControllerTest, Execute) { model.AddItem(1, ASCIIToUTF16("one")); base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 1); + EXPECT_EQ(1, [[menu menu] numberOfItems]); // Fake selecting the menu item, we expect the delegate to be told to execute // a command. NSMenuItem* item = [[menu menu] itemAtIndex:0]; [[item target] performSelector:[item action] withObject:item]; - EXPECT_EQ(delegate.execute_count_, 1); + EXPECT_EQ(1, delegate.execute_count_); } void Validate(MenuController* controller, NSMenu* menu) { @@ -346,7 +473,7 @@ TEST_F(MenuControllerTest, Validate) { base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 3); + EXPECT_EQ(3, [[menu menu] numberOfItems]); Validate(menu.get(), [menu menu]); } @@ -363,7 +490,7 @@ TEST_F(MenuControllerTest, LabelFontList) { base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 2); + EXPECT_EQ(2, [[menu menu] numberOfItems]); Validate(menu.get(), [menu menu]); @@ -403,7 +530,7 @@ TEST_F(MenuControllerTest, Dynamic) { model.AddItem(1, ASCIIToUTF16("foo")); base::scoped_nsobject<MenuController> menu( [[MenuController alloc] initWithModel:&model useWithPopUpButtonCell:NO]); - EXPECT_EQ([[menu menu] numberOfItems], 1); + EXPECT_EQ(1, [[menu menu] numberOfItems]); // Validate() simulates opening the menu - the item label/icon should be // initialized after this so we can validate the menu contents. Validate(menu.get(), [menu menu]); diff --git a/chromium/ui/base/cocoa/touch_bar_util.h b/chromium/ui/base/cocoa/touch_bar_util.h index 5d6d7f7705d..c97f726a2d9 100644 --- a/chromium/ui/base/cocoa/touch_bar_util.h +++ b/chromium/ui/base/cocoa/touch_bar_util.h @@ -41,15 +41,7 @@ UI_BASE_EXPORT Class NSGroupTouchBarItem(); // Returns a stylized blue button for the touch bar. The button performs // |action| from the |target|. -// The __attribute__ visibility annotation is necessary to work around a clang -// bug: https://bugs.llvm.org/show_bug.cgi?id=33796. -#if defined(UI_BASE_IMPLEMENTATION) && defined(COMPONENT_BUILD) -// UI_BASE_EXPORT specifies "default" visibility. API_AVAILABLE(macosx(10.12.2)) -#else -API_AVAILABLE(macosx(10.12.2)) -__attribute__((visibility("hidden"))) -#endif UI_BASE_EXPORT NSButton* GetBlueTouchBarButton(NSString* title, id target, SEL action); diff --git a/chromium/ui/base/cursor/cursor_loader.h b/chromium/ui/base/cursor/cursor_loader.h index fca5e7dce44..527b1472018 100644 --- a/chromium/ui/base/cursor/cursor_loader.h +++ b/chromium/ui/base/cursor/cursor_loader.h @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/strings/string16.h" +#include "ui/base/cursor/cursor_type.h" #include "ui/base/ui_base_export.h" #include "ui/display/display.h" #include "ui/gfx/geometry/point.h" diff --git a/chromium/ui/base/cursor/cursor_loader_ozone.cc b/chromium/ui/base/cursor/cursor_loader_ozone.cc index b8498ef9b66..ef23518d1e4 100644 --- a/chromium/ui/base/cursor/cursor_loader_ozone.cc +++ b/chromium/ui/base/cursor/cursor_loader_ozone.cc @@ -16,7 +16,9 @@ CursorLoaderOzone::CursorLoaderOzone() { factory_ = CursorFactoryOzone::GetInstance(); } -CursorLoaderOzone::~CursorLoaderOzone() {} +CursorLoaderOzone::~CursorLoaderOzone() { + UnloadAll(); +} void CursorLoaderOzone::LoadImageCursor(CursorType id, int resource_id, diff --git a/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.cc b/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.cc index e0a68d956ad..3bcaf00b7b6 100644 --- a/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.cc +++ b/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.cc @@ -18,7 +18,7 @@ const uint32_t kBadCookie = 0xBADBADCC; CursorDataOzone* ToCursorDataOzone(PlatformCursor cursor) { CursorDataOzone* ozone = static_cast<CursorDataOzone*>(cursor); #if DCHECK_IS_ON() - ozone->AssertIsACusrorDataOzone(); + ozone->AssertIsACursorDataOzone(); #endif return ozone; } @@ -32,7 +32,7 @@ PlatformCursor ToPlatformCursor(CursorDataOzone* cursor) { CursorDataOzone::CursorDataOzone(const ui::CursorData& data) : magic_cookie_(kCookie), data_(data) {} -void CursorDataOzone::AssertIsACusrorDataOzone() { +void CursorDataOzone::AssertIsACursorDataOzone() { CHECK_EQ(magic_cookie_, kCookie); } 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 d3ef8a137e4..c9859f23306 100644 --- a/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.h +++ b/chromium/ui/base/cursor/ozone/cursor_data_factory_ozone.h @@ -35,7 +35,7 @@ class UI_BASE_EXPORT CursorDataOzone // level CursorFactoryOzone interface. Even worse, there can be multiple // subclasses that map to this void* type. This asserts that a magic cookie // that we put at the start of valid CursorDataOzone objects is correct. - void AssertIsACusrorDataOzone(); + void AssertIsACursorDataOzone(); private: friend class base::RefCounted<CursorDataOzone>; diff --git a/chromium/ui/base/dragdrop/os_exchange_data.h b/chromium/ui/base/dragdrop/os_exchange_data.h index 282587d23df..3bb6feffc1b 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data.h +++ b/chromium/ui/base/dragdrop/os_exchange_data.h @@ -113,7 +113,7 @@ class UI_BASE_EXPORT OSExchangeData { virtual bool HasFile() const = 0; virtual bool HasCustomFormat(const Clipboard::FormatType& format) const = 0; -#if (!defined(OS_CHROMEOS) && defined(USE_X11)) || defined(OS_WIN) +#if defined(USE_X11) || defined(OS_WIN) virtual void SetFileContents(const base::FilePath& filename, const std::string& file_contents) = 0; #endif 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 962fd2c5748..bc3ecda3a1a 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc @@ -5,8 +5,9 @@ #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) && !defined(OS_CHROMEOS) +#if defined(USE_X11) #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" #elif defined(OS_LINUX) #include "ui/base/dragdrop/os_exchange_data_provider_aura.h" @@ -40,7 +41,7 @@ OSExchangeDataProviderFactory::CreateProvider() { if (factory_) return factory_->BuildProvider(); -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) return base::MakeUnique<OSExchangeDataProviderAuraX11>(); #elif defined(OS_LINUX) return base::MakeUnique<OSExchangeDataProviderAura>(); @@ -48,6 +49,10 @@ OSExchangeDataProviderFactory::CreateProvider() { return ui::BuildOSExchangeDataProviderMac(); #elif defined(OS_WIN) return base::MakeUnique<OSExchangeDataProviderWin>(); +#elif defined(OS_FUCHSIA) + // TODO(fuchsia): Implement this when UI support is added. (crbug.com/750934) + NOTIMPLEMENTED(); + return nullptr; #else #error "Unknown operating system" #endif 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 9d0338b03c5..5741375f506 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc @@ -108,9 +108,9 @@ class FormatEtcEnumerator final : public IEnumFORMATETC { private: // We are _forced_ to use a vector as our internal data model as Windows' - // retarded IEnumFORMATETC API assumes a deterministic ordering of elements - // through methods like Next and Skip. This exposes the underlying data - // structure to the user. Bah. + // IEnumFORMATETC API assumes a deterministic ordering of elements through + // methods like Next and Skip. This exposes the underlying data structure to + // the user. Bah. std::vector<std::unique_ptr<FORMATETC>> contents_; // The cursor of the active enumeration - an index into |contents_|. diff --git a/chromium/ui/base/idle/idle_fuchsia.cc b/chromium/ui/base/idle/idle_fuchsia.cc new file mode 100644 index 00000000000..ed21be8b78a --- /dev/null +++ b/chromium/ui/base/idle/idle_fuchsia.cc @@ -0,0 +1,23 @@ +// 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/base/idle/idle.h" + +#include "base/logging.h" + +namespace ui { + +void CalculateIdleTime(IdleTimeCallback notify) { + // TODO(fuchsia): https://crbug.com/743296. + NOTIMPLEMENTED(); + notify.Run(0); +} + +bool CheckIdleStateIsLocked() { + // TODO(fuchsia): https://crbug.com/743296. + NOTIMPLEMENTED(); + return false; +} + +} // namespace ui diff --git a/chromium/ui/base/ime/BUILD.gn b/chromium/ui/base/ime/BUILD.gn index 2940cb9b6eb..f53139c8da0 100644 --- a/chromium/ui/base/ime/BUILD.gn +++ b/chromium/ui/base/ime/BUILD.gn @@ -31,8 +31,6 @@ component("ime") { "chromeos/ime_candidate_window_handler_interface.h", "chromeos/ime_keyboard.cc", "chromeos/ime_keyboard.h", - "chromeos/ime_keyboard_x11.cc", - "chromeos/ime_keyboard_x11.h", "chromeos/ime_keymap.cc", "chromeos/ime_keymap.h", "chromeos/input_method_delegate.h", @@ -54,11 +52,12 @@ component("ime") { "composition_text.h", "composition_text_util_pango.cc", "composition_text_util_pango.h", - "composition_underline.h", "ime_bridge.cc", "ime_bridge.h", "ime_engine_handler_interface.h", "ime_input_context_handler_interface.h", + "ime_text_span.cc", + "ime_text_span.h", "infolist_entry.cc", "infolist_entry.h", "input_method.h", @@ -161,22 +160,14 @@ component("ime") { if (is_chromeos) { deps += [ "//chromeos", + "//services/ui/public/cpp/input_devices", "//ui/chromeos/strings", "//ui/events:dom_keycode_converter", ] - if (use_ozone) { - deps += [ "//services/ui/public/cpp/input_devices" ] - sources += [ - "chromeos/ime_keyboard_mus.cc", - "chromeos/ime_keyboard_mus.h", - ] - } - if (!use_x11) { - sources -= [ - "chromeos/ime_keyboard_x11.cc", - "chromeos/ime_keyboard_x11.h", - ] - } + sources += [ + "chromeos/ime_keyboard_mus.cc", + "chromeos/ime_keyboard_mus.h", + ] } if (use_pango) { @@ -209,4 +200,11 @@ component("ime") { "//ui/ozone", ] } + + if (is_fuchsia) { + sources -= [ + "input_method_auralinux.cc", + "input_method_auralinux.h", + ] + } } diff --git a/chromium/ui/base/ime/composition_text.cc b/chromium/ui/base/ime/composition_text.cc index a2e583f6221..ab2136e9d1e 100644 --- a/chromium/ui/base/ime/composition_text.cc +++ b/chromium/ui/base/ime/composition_text.cc @@ -14,19 +14,4 @@ CompositionText::CompositionText(const CompositionText& other) = default; CompositionText::~CompositionText() { } -void CompositionText::Clear() { - text.clear(); - underlines.clear(); - selection = gfx::Range(); -} - -void CompositionText::CopyFrom(const CompositionText& obj) { - Clear(); - text = obj.text; - for (size_t i = 0; i < obj.underlines.size(); i++) { - underlines.push_back(obj.underlines[i]); - } - selection = obj.selection; -} - } // namespace ui diff --git a/chromium/ui/base/ime/composition_text.h b/chromium/ui/base/ime/composition_text.h index ba56c627106..272535ba3e6 100644 --- a/chromium/ui/base/ime/composition_text.h +++ b/chromium/ui/base/ime/composition_text.h @@ -8,7 +8,7 @@ #include <stddef.h> #include "base/strings/string16.h" -#include "ui/base/ime/composition_underline.h" +#include "ui/base/ime/ime_text_span.h" #include "ui/base/ime/ui_base_ime_export.h" #include "ui/gfx/range/range.h" @@ -21,12 +21,11 @@ struct UI_BASE_IME_EXPORT CompositionText { ~CompositionText(); bool operator==(const CompositionText& rhs) const { - if ((this->text != rhs.text) || - (this->selection != rhs.selection) || - (this->underlines.size() != rhs.underlines.size())) + if ((this->text != rhs.text) || (this->selection != rhs.selection) || + (this->ime_text_spans.size() != rhs.ime_text_spans.size())) return false; - for (size_t i = 0; i < this->underlines.size(); ++i) { - if (this->underlines[i] != rhs.underlines[i]) + for (size_t i = 0; i < this->ime_text_spans.size(); ++i) { + if (this->ime_text_spans[i] != rhs.ime_text_spans[i]) return false; } return true; @@ -36,17 +35,11 @@ struct UI_BASE_IME_EXPORT CompositionText { return !(*this == rhs); } - void Clear(); - - void CopyFrom(const CompositionText& obj); - // Content of the composition text. base::string16 text; - // Underline information of the composition text. - // They must be sorted in ascending order by their start_offset and cannot be - // overlapped with each other. - CompositionUnderlines underlines; + // ImeTextSpan information for the composition text. + ImeTextSpans ime_text_spans; // Selection range in the composition text. It represents the caret position // if the range length is zero. Usually it's used for representing the target diff --git a/chromium/ui/base/ime/composition_text_unittest.cc b/chromium/ui/base/ime/composition_text_unittest.cc index c03090a5623..8431e2eb6d5 100644 --- a/chromium/ui/base/ime/composition_text_unittest.cc +++ b/chromium/ui/base/ime/composition_text_unittest.cc @@ -13,37 +13,39 @@ namespace ui { TEST(CompositionTextTest, CopyTest) { const base::string16 kSampleText = base::UTF8ToUTF16("Sample Text"); - const CompositionUnderline kSampleUnderline1(10, 20, SK_ColorBLACK, false, - SK_ColorTRANSPARENT); + const ImeTextSpan kSampleUnderline1(ImeTextSpan::Type::kComposition, 10, 20, + SK_ColorBLACK, false, + SK_ColorTRANSPARENT); - const CompositionUnderline kSampleUnderline2(11, 21, SK_ColorBLACK, true, - SK_ColorTRANSPARENT); + const ImeTextSpan kSampleUnderline2(ImeTextSpan::Type::kComposition, 11, 21, + SK_ColorBLACK, true, SK_ColorTRANSPARENT); - const CompositionUnderline kSampleUnderline3(12, 22, SK_ColorRED, false, - SK_ColorTRANSPARENT); + const ImeTextSpan kSampleUnderline3(ImeTextSpan::Type::kComposition, 12, 22, + SK_ColorRED, false, SK_ColorTRANSPARENT); // Make CompositionText CompositionText text; text.text = kSampleText; - text.underlines.push_back(kSampleUnderline1); - text.underlines.push_back(kSampleUnderline2); - text.underlines.push_back(kSampleUnderline3); + text.ime_text_spans.push_back(kSampleUnderline1); + text.ime_text_spans.push_back(kSampleUnderline2); + text.ime_text_spans.push_back(kSampleUnderline3); text.selection.set_start(30); text.selection.set_end(40); - CompositionText text2; - text2.CopyFrom(text); + CompositionText text2 = text; EXPECT_EQ(text.text, text2.text); - EXPECT_EQ(text.underlines.size(), text2.underlines.size()); - for (size_t i = 0; i < text.underlines.size(); ++i) { - EXPECT_EQ(text.underlines[i].start_offset, - text2.underlines[i].start_offset); - EXPECT_EQ(text.underlines[i].end_offset, text2.underlines[i].end_offset); - EXPECT_EQ(text.underlines[i].color, text2.underlines[i].color); - EXPECT_EQ(text.underlines[i].thick, text2.underlines[i].thick); - EXPECT_EQ(text.underlines[i].background_color, - text2.underlines[i].background_color); + EXPECT_EQ(text.ime_text_spans.size(), text2.ime_text_spans.size()); + for (size_t i = 0; i < text.ime_text_spans.size(); ++i) { + EXPECT_EQ(text.ime_text_spans[i].start_offset, + text2.ime_text_spans[i].start_offset); + EXPECT_EQ(text.ime_text_spans[i].end_offset, + 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].background_color, + text2.ime_text_spans[i].background_color); } EXPECT_EQ(text.selection.start(), text2.selection.start()); diff --git a/chromium/ui/base/ime/composition_text_util_pango.cc b/chromium/ui/base/ime/composition_text_util_pango.cc index b8a92973992..9ef58037f7b 100644 --- a/chromium/ui/base/ime/composition_text_util_pango.cc +++ b/chromium/ui/base/ime/composition_text_util_pango.cc @@ -18,7 +18,7 @@ void ExtractCompositionTextFromGtkPreedit(const gchar* utf8_text, PangoAttrList* attrs, int cursor_position, CompositionText* composition) { - composition->Clear(); + *composition = CompositionText(); composition->text = base::UTF8ToUTF16(utf8_text); if (composition->text.empty()) @@ -75,44 +75,43 @@ void ExtractCompositionTextFromGtkPreedit(const gchar* utf8_text, if (background_attr || underline_attr) { // Use a black thin underline by default. - CompositionUnderline underline(char16_offsets[start], - char16_offsets[end], - SK_ColorBLACK, - false, - SK_ColorTRANSPARENT); + ImeTextSpan ime_text_span(ImeTextSpan::Type::kComposition, + char16_offsets[start], char16_offsets[end], + SK_ColorBLACK, false, SK_ColorTRANSPARENT); // Always use thick underline for a range with background color, which // is usually the selection range. if (background_attr) { - underline.thick = true; + ime_text_span.thick = true; // 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. - if (underline.start_offset == cursor_offset) { - composition->selection.set_start(underline.end_offset); + if (ime_text_span.start_offset == cursor_offset) { + composition->selection.set_start(ime_text_span.end_offset); composition->selection.set_end(cursor_offset); - } else if (underline.end_offset == cursor_offset) { - composition->selection.set_start(underline.start_offset); + } else if (ime_text_span.end_offset == cursor_offset) { + composition->selection.set_start(ime_text_span.start_offset); composition->selection.set_end(cursor_offset); } } if (underline_attr) { int type = reinterpret_cast<PangoAttrInt*>(underline_attr)->value; if (type == PANGO_UNDERLINE_DOUBLE) - underline.thick = true; + ime_text_span.thick = true; else if (type == PANGO_UNDERLINE_ERROR) - underline.color = SK_ColorRED; + ime_text_span.underline_color = SK_ColorRED; } - composition->underlines.push_back(underline); + composition->ime_text_spans.push_back(ime_text_span); } } while (pango_attr_iterator_next(iter)); pango_attr_iterator_destroy(iter); } // Use a black thin underline by default. - if (composition->underlines.empty()) { - composition->underlines.push_back(CompositionUnderline( - 0, length, SK_ColorBLACK, false, SK_ColorTRANSPARENT)); + if (composition->ime_text_spans.empty()) { + composition->ime_text_spans.push_back( + ImeTextSpan(ImeTextSpan::Type::kComposition, 0, length, SK_ColorBLACK, + false, 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 c645e300da8..be674433b65 100644 --- a/chromium/ui/base/ime/composition_text_util_pango_unittest.cc +++ b/chromium/ui/base/ime/composition_text_util_pango_unittest.cc @@ -25,10 +25,10 @@ struct AttributeInfo { int end_offset; }; -struct Underline { +struct ImeTextSpan { unsigned start_offset; unsigned end_offset; - uint32_t color; + uint32_t underline_color; bool thick; uint32_t background_color; }; @@ -36,7 +36,7 @@ struct Underline { struct TestData { const char* text; const AttributeInfo attrs[10]; - const Underline underlines[10]; + const ImeTextSpan ime_text_spans[10]; }; const TestData kTestData[] = { @@ -95,11 +95,10 @@ const TestData kTestData[] = { {0, 0, 0, false, SK_ColorTRANSPARENT}}}, }; -void CompareUnderline(const Underline& a, - const ui::CompositionUnderline& b) { +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.color, b.color); + EXPECT_EQ(a.underline_color, b.underline_color); EXPECT_EQ(a.thick, b.thick); EXPECT_EQ(a.background_color, b.background_color); } @@ -135,11 +134,12 @@ TEST(CompositionTextUtilPangoTest, ExtractCompositionText) { ui::CompositionText result; ui::ExtractCompositionTextFromGtkPreedit(text, pango_attrs, 0, &result); - const Underline* underlines = kTestData[i].underlines; - for (size_t u = 0; underlines[u].color && - u < result.underlines.size(); ++u) { - SCOPED_TRACE(testing::Message() << "Underline:" << u); - CompareUnderline(underlines[u], result.underlines[u]); + const ImeTextSpan* ime_text_spans = kTestData[i].ime_text_spans; + for (size_t u = 0; + ime_text_spans[u].underline_color && u < result.ime_text_spans.size(); + ++u) { + SCOPED_TRACE(testing::Message() << "ImeTextSpan:" << u); + CompareImeTextSpan(ime_text_spans[u], result.ime_text_spans[u]); } pango_attr_list_unref(pango_attrs); diff --git a/chromium/ui/base/ime/composition_underline.h b/chromium/ui/base/ime/composition_underline.h deleted file mode 100644 index 9eb7763aa80..00000000000 --- a/chromium/ui/base/ime/composition_underline.h +++ /dev/null @@ -1,68 +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_IME_COMPOSITION_UNDERLINE_H_ -#define UI_BASE_IME_COMPOSITION_UNDERLINE_H_ - -#include <stdint.h> - -#include <vector> - -#include "third_party/skia/include/core/SkColor.h" - -namespace ui { - -// Intentionally keep sync with blink::WebCompositionUnderline defined in: -// third_party/WebKit/public/web/WebCompositionUnderline.h -struct CompositionUnderline { - CompositionUnderline() - : start_offset(0), - end_offset(0), - color(SK_ColorTRANSPARENT), - thick(false), - background_color(SK_ColorTRANSPARENT) {} - - // TODO(huangs): remove this constructor. - CompositionUnderline(uint32_t s, uint32_t e, SkColor c, bool t) - : start_offset(s), - end_offset(e), - color(c), - thick(t), - background_color(SK_ColorTRANSPARENT) {} - - CompositionUnderline(uint32_t s, uint32_t e, SkColor c, bool t, SkColor bc) - : start_offset(s), - end_offset(e), - color(c), - thick(t), - background_color(bc) {} - - bool operator<(const CompositionUnderline& rhs) const { - return std::tie(start_offset, end_offset) < - std::tie(rhs.start_offset, rhs.end_offset); - } - - bool operator==(const CompositionUnderline& rhs) const { - return (this->start_offset == rhs.start_offset) && - (this->end_offset == rhs.end_offset) && (this->color == rhs.color) && - (this->thick == rhs.thick) && - (this->background_color == rhs.background_color); - } - - bool operator!=(const CompositionUnderline& rhs) const { - return !(*this == rhs); - } - - uint32_t start_offset; - uint32_t end_offset; - SkColor color; - bool thick; - SkColor background_color; -}; - -typedef std::vector<CompositionUnderline> CompositionUnderlines; - -} // namespace ui - -#endif // UI_BASE_IME_COMPOSITION_UNDERLINE_H_ diff --git a/chromium/ui/base/ime/ime_text_span.cc b/chromium/ui/base/ime/ime_text_span.cc new file mode 100644 index 00000000000..1ee2876be35 --- /dev/null +++ b/chromium/ui/base/ime/ime_text_span.cc @@ -0,0 +1,46 @@ +// 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/base/ime/ime_text_span.h" + +#include <string> +#include <vector> + +namespace ui { + +ImeTextSpan::ImeTextSpan() : ImeTextSpan(0, 0, SK_ColorTRANSPARENT, false) {} + +ImeTextSpan::ImeTextSpan(uint32_t start_offset, + uint32_t end_offset, + SkColor underline_color, + bool thick) + : ImeTextSpan(Type::kComposition, + start_offset, + end_offset, + underline_color, + thick, + SK_ColorTRANSPARENT) {} + +ImeTextSpan::ImeTextSpan(Type type, + uint32_t start_offset, + uint32_t end_offset, + SkColor underline_color, + bool thick, + 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), + background_color(background_color), + suggestion_highlight_color(suggestion_highlight_color), + suggestions(suggestions) {} + +ImeTextSpan::ImeTextSpan(const ImeTextSpan& rhs) = default; + +ImeTextSpan::~ImeTextSpan() = default; + +} // namespace ui diff --git a/chromium/ui/base/ime/ime_text_span.h b/chromium/ui/base/ime/ime_text_span.h new file mode 100644 index 00000000000..36b77f755ee --- /dev/null +++ b/chromium/ui/base/ime/ime_text_span.h @@ -0,0 +1,79 @@ +// 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_IME_IME_TEXT_SPAN_H_ +#define UI_BASE_IME_IME_TEXT_SPAN_H_ + +#include <stdint.h> + +#include <string> +#include <vector> + +#include "third_party/skia/include/core/SkColor.h" +#include "ui/base/ime/ui_base_ime_export.h" + +namespace ui { + +// Intentionally keep sync with blink::WebImeTextSpan defined in: +// third_party/WebKit/public/web/WebImeTextSpan.h + +struct UI_BASE_IME_EXPORT ImeTextSpan { + enum class Type { + // Creates a composition marker. + kComposition, + // Creates a suggestion marker that isn't cleared after the user picks a + // replacement. + kSuggestion, + }; + + // 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); + ImeTextSpan( + Type type, + uint32_t start_offset, + uint32_t end_offset, + SkColor underline_color, + bool thick, + SkColor background_color, + SkColor suggestion_highlight_color = SK_ColorTRANSPARENT, + const std::vector<std::string>& suggestions = std::vector<std::string>()); + + ImeTextSpan(const ImeTextSpan& rhs); + + ~ImeTextSpan(); + + bool operator==(const ImeTextSpan& rhs) const { + return (this->type == rhs.type) && + (this->start_offset == rhs.start_offset) && + (this->end_offset == rhs.end_offset) && + (this->underline_color == rhs.underline_color) && + (this->thick == rhs.thick) && + (this->background_color == rhs.background_color) && + (this->suggestion_highlight_color == + rhs.suggestion_highlight_color) && + (this->suggestions == rhs.suggestions); + } + + bool operator!=(const ImeTextSpan& rhs) const { return !(*this == rhs); } + + Type type; + uint32_t start_offset; + uint32_t end_offset; + SkColor underline_color; + bool thick; + SkColor background_color; + SkColor suggestion_highlight_color; + std::vector<std::string> suggestions; +}; + +typedef std::vector<ImeTextSpan> ImeTextSpans; + +} // namespace ui + +#endif // UI_BASE_IME_IME_TEXT_SPAN_H_ diff --git a/chromium/ui/base/ime/input_method_auralinux.cc b/chromium/ui/base/ime/input_method_auralinux.cc index 03833c120ac..01893d5739b 100644 --- a/chromium/ui/base/ime/input_method_auralinux.cc +++ b/chromium/ui/base/ime/input_method_auralinux.cc @@ -125,7 +125,7 @@ void InputMethodAuraLinux::ProcessKeyEventByEngineDone( base::string16* result_text, bool is_handled) { composition_changed_ = composition_changed; - composition_.CopyFrom(*composition); + composition_ = *composition; result_text_ = *result_text; ignore_result(ProcessKeyEventDone(event, filtered, is_handled)); } @@ -201,7 +201,7 @@ ui::EventDispatchDetails InputMethodAuraLinux::ProcessKeyEventDone( // Makes sure the cached composition is cleared after committing any text or // cleared composition. if (client && !client->HasCompositionText()) - composition_.Clear(); + composition_ = CompositionText(); if (!filtered) { details = DispatchKeyEventPostIME(event); @@ -317,7 +317,7 @@ void InputMethodAuraLinux::ResetContext() { context_->Focus(); } - composition_.Clear(); + composition_ = CompositionText(); result_text_.clear(); is_sync_mode_ = false; composition_changed_ = false; @@ -349,7 +349,7 @@ void InputMethodAuraLinux::OnCommit(const base::string16& text) { return; if (!event.stopped_propagation() && !details.target_destroyed) GetTextInputClient()->InsertText(text); - composition_.Clear(); + composition_ = CompositionText(); } } @@ -379,7 +379,7 @@ void InputMethodAuraLinux::OnPreeditEnd() { if (is_sync_mode_) { if (!composition_.text.empty()) { - composition_.Clear(); + composition_ = CompositionText(); composition_changed_ = true; } } else { @@ -392,7 +392,7 @@ void InputMethodAuraLinux::OnPreeditEnd() { if (!event.stopped_propagation() && !details.target_destroyed) client->ClearCompositionText(); } - composition_.Clear(); + composition_ = CompositionText(); } } diff --git a/chromium/ui/base/ime/input_method_base.h b/chromium/ui/base/ime/input_method_base.h index dbf9df4ecbd..77c95ef3183 100644 --- a/chromium/ui/base/ime/input_method_base.h +++ b/chromium/ui/base/ime/input_method_base.h @@ -29,7 +29,7 @@ class TextInputClient; // A helper class providing functionalities shared among ui::InputMethod // implementations. class UI_BASE_IME_EXPORT InputMethodBase - : NON_EXPORTED_BASE(public InputMethod), + : public InputMethod, public base::SupportsWeakPtr<InputMethodBase>, public IMEInputContextHandlerInterface { public: diff --git a/chromium/ui/base/ime/input_method_chromeos.cc b/chromium/ui/base/ime/input_method_chromeos.cc index 1312ba47d71..d5c7b01804e 100644 --- a/chromium/ui/base/ime/input_method_chromeos.cc +++ b/chromium/ui/base/ime/input_method_chromeos.cc @@ -289,7 +289,7 @@ void InputMethodChromeOS::ResetContext() { if (!IsNonPasswordInputFieldFocused() || !GetTextInputClient()) return; - composition_.Clear(); + composition_ = CompositionText(); result_text_.clear(); composing_text_ = false; composition_changed_ = false; @@ -534,7 +534,7 @@ void InputMethodChromeOS::UpdateCompositionText(const CompositionText& text, GetTextInputClient()->SetCompositionText(composition_); SendFakeProcessKeyEvent(false); composition_changed_ = false; - composition_.Clear(); + composition_ = CompositionText(); } } @@ -544,7 +544,7 @@ void InputMethodChromeOS::HidePreeditText() { // Intentionally leaves |composing_text_| unchanged. composition_changed_ = true; - composition_.Clear(); + composition_ = CompositionText(); if (!handling_key_event_) { TextInputClient* client = GetTextInputClient(); @@ -588,7 +588,7 @@ void InputMethodChromeOS::ExtractCompositionText( const CompositionText& text, uint32_t cursor_position, CompositionText* out_composition) const { - out_composition->Clear(); + *out_composition = CompositionText(); out_composition->text = text.text; if (out_composition->text.empty()) @@ -613,17 +613,19 @@ void InputMethodChromeOS::ExtractCompositionText( out_composition->selection = gfx::Range(cursor_offset); - const CompositionUnderlines text_underlines = text.underlines; - if (!text_underlines.empty()) { - for (size_t i = 0; i < text_underlines.size(); ++i) { - const uint32_t start = text_underlines[i].start_offset; - const uint32_t end = text_underlines[i].end_offset; + const ImeTextSpans text_ime_text_spans = text.ime_text_spans; + if (!text_ime_text_spans.empty()) { + for (size_t i = 0; i < text_ime_text_spans.size(); ++i) { + const uint32_t start = text_ime_text_spans[i].start_offset; + const uint32_t end = text_ime_text_spans[i].end_offset; if (start >= end) continue; - CompositionUnderline underline( - char16_offsets[start], char16_offsets[end], text_underlines[i].color, - text_underlines[i].thick, text_underlines[i].background_color); - out_composition->underlines.push_back(underline); + 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].background_color); + out_composition->ime_text_spans.push_back(ime_text_span); } } @@ -631,29 +633,29 @@ void InputMethodChromeOS::ExtractCompositionText( if (text.selection.start() < text.selection.end()) { const uint32_t start = text.selection.start(); const uint32_t end = text.selection.end(); - CompositionUnderline underline(char16_offsets[start], - char16_offsets[end], - SK_ColorBLACK, - true /* thick */, - SK_ColorTRANSPARENT); - out_composition->underlines.push_back(underline); - - // If the cursor is at start or end of this underline, then we treat + ImeTextSpan ime_text_span(ui::ImeTextSpan::Type::kComposition, + char16_offsets[start], char16_offsets[end], + SK_ColorBLACK, true /* thick */, + SK_ColorTRANSPARENT); + out_composition->ime_text_spans.push_back(ime_text_span); + + // If the cursor is at start or end of this ime_text_span, then we treat // it as the selection range as well, but make sure to set the cursor // position to the selection end. - if (underline.start_offset == cursor_offset) { - out_composition->selection.set_start(underline.end_offset); + if (ime_text_span.start_offset == cursor_offset) { + out_composition->selection.set_start(ime_text_span.end_offset); out_composition->selection.set_end(cursor_offset); - } else if (underline.end_offset == cursor_offset) { - out_composition->selection.set_start(underline.start_offset); + } else if (ime_text_span.end_offset == cursor_offset) { + out_composition->selection.set_start(ime_text_span.start_offset); out_composition->selection.set_end(cursor_offset); } } // Use a black thin underline by default. - if (out_composition->underlines.empty()) { - out_composition->underlines.push_back(CompositionUnderline( - 0, length, SK_ColorBLACK, false /* thick */, SK_ColorTRANSPARENT)); + 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)); } } diff --git a/chromium/ui/base/ime/mock_ime_input_context_handler.cc b/chromium/ui/base/ime/mock_ime_input_context_handler.cc index 4c6045df821..b0ef49d0ec8 100644 --- a/chromium/ui/base/ime/mock_ime_input_context_handler.cc +++ b/chromium/ui/base/ime/mock_ime_input_context_handler.cc @@ -26,7 +26,7 @@ void MockIMEInputContextHandler::UpdateCompositionText( uint32_t cursor_pos, bool visible) { ++update_preedit_text_call_count_; - last_update_composition_arg_.composition_text.CopyFrom(text); + last_update_composition_arg_.composition_text = text; last_update_composition_arg_.cursor_pos = cursor_pos; last_update_composition_arg_.is_visible = visible; } diff --git a/chromium/ui/base/ime/mock_input_method.h b/chromium/ui/base/ime/mock_input_method.h index f703e4a00a1..b841a7a1d26 100644 --- a/chromium/ui/base/ime/mock_input_method.h +++ b/chromium/ui/base/ime/mock_input_method.h @@ -23,8 +23,7 @@ class TextInputClient; // of this class as the global input method with calling // SetUpInputMethodFactoryForTesting() which is declared in // ui/base/ime/input_method_factory.h -class UI_BASE_IME_EXPORT MockInputMethod - : NON_EXPORTED_BASE(public InputMethod) { +class UI_BASE_IME_EXPORT MockInputMethod : public InputMethod { public: explicit MockInputMethod(internal::InputMethodDelegate* delegate); ~MockInputMethod() override; diff --git a/chromium/ui/base/ime/win/imm32_manager.cc b/chromium/ui/base/ime/win/imm32_manager.cc index 8b868ce51ca..b4af3c966cd 100644 --- a/chromium/ui/base/ime/win/imm32_manager.cc +++ b/chromium/ui/base/ime/win/imm32_manager.cc @@ -62,10 +62,10 @@ void GetCompositionTargetRange(HIMC imm_context, int* target_start, // Helper function for IMM32Manager::GetCompositionInfo() method, to get // underlines information of the current composition string. -void GetCompositionUnderlines(HIMC imm_context, - int target_start, - int target_end, - ui::CompositionUnderlines* underlines) { +void GetImeTextSpans(HIMC imm_context, + int target_start, + int target_end, + ui::ImeTextSpans* ime_text_spans) { int clause_size = ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, NULL, 0); int clause_length = clause_size / sizeof(uint32_t); @@ -75,19 +75,19 @@ void GetCompositionUnderlines(HIMC imm_context, ::ImmGetCompositionString(imm_context, GCS_COMPCLAUSE, clause_data.get(), clause_size); for (int i = 0; i < clause_length - 1; ++i) { - ui::CompositionUnderline underline; - underline.start_offset = clause_data[i]; - underline.end_offset = clause_data[i+1]; - underline.color = SK_ColorBLACK; - underline.thick = false; - underline.background_color = SK_ColorTRANSPARENT; + ui::ImeTextSpan ime_text_span; + 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.background_color = SK_ColorTRANSPARENT; // Use thick underline for the target clause. - if (underline.start_offset >= static_cast<uint32_t>(target_start) && - underline.end_offset <= static_cast<uint32_t>(target_end)) { - underline.thick = true; + 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; } - underlines->push_back(underline); + ime_text_spans->push_back(ime_text_span); } } } @@ -294,8 +294,8 @@ void IMM32Manager::GetCompositionInfo(HIMC imm_context, LPARAM lparam, CompositionText* composition) { // We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and - // convert them into underlines and selection range respectively. - composition->underlines.clear(); + // convert them into composition underlines and selection range respectively. + composition->ime_text_spans.clear(); int length = static_cast<int>(composition->text.length()); @@ -320,36 +320,36 @@ void IMM32Manager::GetCompositionInfo(HIMC imm_context, composition->selection = gfx::Range(0); } - // Retrieve the clause segmentations and convert them to underlines. + // Retrieve the clause segmentations and convert them to ime_text_spans. if (lparam & GCS_COMPCLAUSE) { - GetCompositionUnderlines(imm_context, target_start, target_end, - &composition->underlines); + GetImeTextSpans(imm_context, target_start, target_end, + &composition->ime_text_spans); } - // Set default underlines in case there is no clause information. - if (!composition->underlines.empty()) + // Set default composition underlines in case there is no clause information. + if (!composition->ime_text_spans.empty()) return; - CompositionUnderline underline; - underline.color = SK_ColorBLACK; - underline.background_color = SK_ColorTRANSPARENT; + ImeTextSpan ime_text_span; + ime_text_span.underline_color = SK_ColorBLACK; + ime_text_span.background_color = SK_ColorTRANSPARENT; if (target_start > 0) { - underline.start_offset = 0U; - underline.end_offset = static_cast<uint32_t>(target_start); - underline.thick = false; - composition->underlines.push_back(underline); + ime_text_span.start_offset = 0U; + ime_text_span.end_offset = static_cast<uint32_t>(target_start); + ime_text_span.thick = false; + composition->ime_text_spans.push_back(ime_text_span); } if (target_end > target_start) { - underline.start_offset = static_cast<uint32_t>(target_start); - underline.end_offset = static_cast<uint32_t>(target_end); - underline.thick = true; - composition->underlines.push_back(underline); + 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; + composition->ime_text_spans.push_back(ime_text_span); } if (target_end < length) { - underline.start_offset = static_cast<uint32_t>(target_end); - underline.end_offset = static_cast<uint32_t>(length); - underline.thick = false; - composition->underlines.push_back(underline); + 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; + composition->ime_text_spans.push_back(ime_text_span); } } @@ -403,7 +403,7 @@ bool IMM32Manager::GetComposition(HWND window_handle, LPARAM lparam, composition->text[0] = 0xFF3F; } - // Retrieve the composition underlines and selection range information. + // Retrieve the IME text spans and selection range information. GetCompositionInfo(imm_context, lparam, composition); // Mark that there is an ongoing composition. diff --git a/chromium/ui/base/l10n/l10n_util.cc b/chromium/ui/base/l10n/l10n_util.cc index 5100c0ced98..c43fd7cc09e 100644 --- a/chromium/ui/base/l10n/l10n_util.cc +++ b/chromium/ui/base/l10n/l10n_util.cc @@ -334,7 +334,7 @@ std::string GetLanguage(const std::string& locale) { return std::string(locale, 0, hyphen_pos); } -// TOOD(jshin): revamp this function completely to use a more sytematic +// TODO(jshin): revamp this function completely to use a more sytematic // and generic locale fallback based on ICU/CLDR. bool CheckAndResolveLocale(const std::string& locale, std::string* resolved_locale) { diff --git a/chromium/ui/base/l10n/l10n_util_android.cc b/chromium/ui/base/l10n/l10n_util_android.cc index 5c7c12d57b1..6fa1f4685b6 100644 --- a/chromium/ui/base/l10n/l10n_util_android.cc +++ b/chromium/ui/base/l10n/l10n_util_android.cc @@ -108,8 +108,4 @@ ScopedJavaLocalRef<jstring> GetDurationString(JNIEnv* env, return jtime_remaining; } -bool RegisterLocalizationUtil(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace l10n_util diff --git a/chromium/ui/base/l10n/l10n_util_android.h b/chromium/ui/base/l10n/l10n_util_android.h index 3745ed6cb2c..38592e9c58e 100644 --- a/chromium/ui/base/l10n/l10n_util_android.h +++ b/chromium/ui/base/l10n/l10n_util_android.h @@ -20,8 +20,6 @@ UI_BASE_EXPORT base::string16 GetDisplayNameForLocale( UI_BASE_EXPORT bool IsLayoutRtl(); -UI_BASE_EXPORT bool RegisterLocalizationUtil(JNIEnv* env); - } // namespace l10n_util #endif // UI_BASE_L10N_L10N_UTIL_ANDROID_H_ diff --git a/chromium/ui/base/material_design/material_design_controller.cc b/chromium/ui/base/material_design/material_design_controller.cc index ee9a7d6e8b2..530b6896e0c 100644 --- a/chromium/ui/base/material_design/material_design_controller.cc +++ b/chromium/ui/base/material_design/material_design_controller.cc @@ -20,7 +20,7 @@ #include "base/files/file_enumerator.h" #include "base/threading/thread_restrictions.h" -#include "ui/events/ozone/evdev/event_device_info.h" +#include "ui/events/ozone/evdev/event_device_info.h" // nogncheck #endif // defined(USE_OZONE) #endif // defined(OS_CHROMEOS) diff --git a/chromium/ui/base/models/button_menu_item_model.h b/chromium/ui/base/models/button_menu_item_model.h index 0b15d9c26ae..22595e64ee1 100644 --- a/chromium/ui/base/models/button_menu_item_model.h +++ b/chromium/ui/base/models/button_menu_item_model.h @@ -26,8 +26,7 @@ class UI_BASE_EXPORT ButtonMenuItemModel { TYPE_BUTTON_LABEL }; - class UI_BASE_EXPORT Delegate - : NON_EXPORTED_BASE(public AcceleratorProvider) { + class UI_BASE_EXPORT Delegate : public AcceleratorProvider { public: // Some command ids have labels that change over time. virtual bool IsItemForCommandIdDynamic(int command_id) const; diff --git a/chromium/ui/base/models/simple_menu_model.h b/chromium/ui/base/models/simple_menu_model.h index 9a9f3505095..7c54a24e12d 100644 --- a/chromium/ui/base/models/simple_menu_model.h +++ b/chromium/ui/base/models/simple_menu_model.h @@ -28,8 +28,7 @@ class ButtonMenuItemModel; // The breadth of MenuModel is not exposed through this API. class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { public: - class UI_BASE_EXPORT Delegate - : NON_EXPORTED_BASE(public AcceleratorProvider) { + class UI_BASE_EXPORT Delegate : public AcceleratorProvider { public: ~Delegate() override {} diff --git a/chromium/ui/base/page_transition_types.h b/chromium/ui/base/page_transition_types.h index 1d2d420834c..b77f25acc48 100644 --- a/chromium/ui/base/page_transition_types.h +++ b/chromium/ui/base/page_transition_types.h @@ -25,8 +25,10 @@ namespace ui { // A Java counterpart will be generated for this enum. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.ui.base enum PageTransition { + PAGE_TRANSITION_FIRST = 0, + // User got to this page by clicking a link on another page. - PAGE_TRANSITION_LINK = 0, + PAGE_TRANSITION_LINK = PAGE_TRANSITION_FIRST, // User got this page by typing the URL in the URL bar. This should not be // used for cases where the user selected a choice that didn't look at all diff --git a/chromium/ui/base/resource/data_pack.cc b/chromium/ui/base/resource/data_pack.cc index 3d07d985a2a..511149d4d9f 100644 --- a/chromium/ui/base/resource/data_pack.cc +++ b/chromium/ui/base/resource/data_pack.cc @@ -24,31 +24,14 @@ namespace { -static const uint32_t kFileFormatVersion = 4; -// Length of file header: version, entry count and text encoding type. -static const size_t kHeaderLength = 2 * sizeof(uint32_t) + sizeof(uint8_t); - -#pragma pack(push, 2) -struct DataPackEntry { - uint16_t resource_id; - uint32_t file_offset; - - static int CompareById(const void* void_key, const void* void_entry) { - uint16_t key = *reinterpret_cast<const uint16_t*>(void_key); - const DataPackEntry* entry = - reinterpret_cast<const DataPackEntry*>(void_entry); - if (key < entry->resource_id) { - return -1; - } else if (key > entry->resource_id) { - return 1; - } else { - return 0; - } - } -}; -#pragma pack(pop) - -static_assert(sizeof(DataPackEntry) == 6, "size of entry must be six"); +static const uint32_t kFileFormatV4 = 4; +static const uint32_t kFileFormatV5 = 5; +// int32(version), int32(resource_count), int8(encoding) +static const size_t kHeaderLengthV4 = 2 * sizeof(uint32_t) + sizeof(uint8_t); +// int32(version), int8(encoding), 3 bytes padding, +// int16(resource_count), int16(alias_count) +static const size_t kHeaderLengthV5 = + sizeof(uint32_t) + sizeof(uint8_t) * 4 + sizeof(uint16_t) * 2; // We're crashing when trying to load a pak file on Windows. Add some error // codes for logging. @@ -102,6 +85,30 @@ void MaybePrintResourceId(uint16_t resource_id) { namespace ui { +#pragma pack(push, 2) +struct DataPack::Entry { + uint16_t resource_id; + uint32_t file_offset; + + static int CompareById(const void* void_key, const void* void_entry) { + uint16_t key = *reinterpret_cast<const uint16_t*>(void_key); + const Entry* entry = reinterpret_cast<const Entry*>(void_entry); + return key - entry->resource_id; + } +}; + +struct DataPack::Alias { + uint16_t resource_id; + uint16_t entry_index; + + static int CompareById(const void* void_key, const void* void_entry) { + uint16_t key = *reinterpret_cast<const uint16_t*>(void_key); + const Alias* entry = reinterpret_cast<const Alias*>(void_entry); + return key - entry->resource_id; + } +}; +#pragma pack(pop) + // Abstraction of a data source (memory mapped file or in-memory buffer). class DataPack::DataSource { public: @@ -149,9 +156,15 @@ class DataPack::BufferDataSource : public DataPack::DataSource { }; DataPack::DataPack(ui::ScaleFactor scale_factor) - : resource_count_(0), + : resource_table_(nullptr), + resource_count_(0), + alias_table_(nullptr), + alias_count_(0), text_encoding_type_(BINARY), scale_factor_(scale_factor) { + // Static assert must be within a DataPack member to appease visiblity rules. + static_assert(sizeof(Entry) == 6, "size of Entry must be 6"); + static_assert(sizeof(Alias) == 4, "size of Alias must be 4"); } DataPack::~DataPack() { @@ -193,29 +206,37 @@ bool DataPack::LoadFromBuffer(base::StringPiece buffer) { } bool DataPack::LoadImpl(std::unique_ptr<DataPack::DataSource> data_source) { - // Sanity check the header of the file. - if (kHeaderLength > data_source->GetLength()) { + const uint8_t* data = data_source->GetData(); + size_t data_length = data_source->GetLength(); + // Parse the version and check for truncated header. + uint32_t version = 0; + if (data_length > sizeof(version)) + version = reinterpret_cast<const uint32_t*>(data)[0]; + size_t header_length = + version == kFileFormatV4 ? kHeaderLengthV4 : kHeaderLengthV5; + if (version == 0 || data_length < header_length) { DLOG(ERROR) << "Data pack file corruption: incomplete file header."; LogDataPackError(HEADER_TRUNCATED); return false; } // Parse the header of the file. - // First uint32_t: version; second: resource count; - const uint32_t* ptr = - reinterpret_cast<const uint32_t*>(data_source->GetData()); - uint32_t version = ptr[0]; - if (version != kFileFormatVersion) { + if (version == kFileFormatV4) { + resource_count_ = reinterpret_cast<const uint32_t*>(data)[1]; + alias_count_ = 0; + text_encoding_type_ = static_cast<TextEncodingType>(data[8]); + } else if (version == kFileFormatV5) { + // Version 5 added the alias table and changed the header format. + text_encoding_type_ = static_cast<TextEncodingType>(data[4]); + resource_count_ = reinterpret_cast<const uint16_t*>(data)[4]; + alias_count_ = reinterpret_cast<const uint16_t*>(data)[5]; + } else { LOG(ERROR) << "Bad data pack version: got " << version << ", expected " - << kFileFormatVersion; + << kFileFormatV4 << " or " << kFileFormatV5; LogDataPackError(BAD_VERSION); return false; } - resource_count_ = ptr[1]; - // third: text encoding. - const uint8_t* ptr_encoding = reinterpret_cast<const uint8_t*>(ptr + 2); - text_encoding_type_ = static_cast<TextEncodingType>(*ptr_encoding); if (text_encoding_type_ != UTF8 && text_encoding_type_ != UTF16 && text_encoding_type_ != BINARY) { LOG(ERROR) << "Bad data pack text encoding: got " << text_encoding_type_ @@ -227,35 +248,63 @@ bool DataPack::LoadImpl(std::unique_ptr<DataPack::DataSource> data_source) { // Sanity check the file. // 1) Check we have enough entries. There's an extra entry after the last item // which gives the length of the last item. - if (kHeaderLength + (resource_count_ + 1) * sizeof(DataPackEntry) > - data_source->GetLength()) { - LOG(ERROR) << "Data pack file corruption: too short for number of " - "entries specified."; + size_t resource_table_size = (resource_count_ + 1) * sizeof(Entry); + size_t alias_table_size = alias_count_ * sizeof(Alias); + if (header_length + resource_table_size + alias_table_size > data_length) { + LOG(ERROR) << "Data pack file corruption: " + << "too short for number of entries."; LogDataPackError(INDEX_TRUNCATED); return false; } + + resource_table_ = reinterpret_cast<const Entry*>(&data[header_length]); + alias_table_ = reinterpret_cast<const Alias*>( + &data[header_length + resource_table_size]); + // 2) Verify the entries are within the appropriate bounds. There's an extra // entry after the last item which gives us the length of the last item. for (size_t i = 0; i < resource_count_ + 1; ++i) { - const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>( - data_source->GetData() + kHeaderLength + (i * sizeof(DataPackEntry))); - if (entry->file_offset > data_source->GetLength()) { - LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. " - << "Was the file corrupted?"; + if (resource_table_[i].file_offset > data_length) { + LOG(ERROR) << "Data pack file corruption: " + << "Entry #" << i << " past end."; LogDataPackError(ENTRY_NOT_FOUND); return false; } } - data_source_ = std::move(data_source); + // 3) Verify the aliases are within the appropriate bounds. + for (size_t i = 0; i < alias_count_; ++i) { + if (alias_table_[i].entry_index >= resource_count_) { + LOG(ERROR) << "Data pack file corruption: " + << "Alias #" << i << " past end."; + LogDataPackError(ENTRY_NOT_FOUND); + return false; + } + } + data_source_ = std::move(data_source); return true; } +const DataPack::Entry* DataPack::LookupEntryById(uint16_t resource_id) const { + // Search the resource table first as most resources will be in there. + const Entry* ret = reinterpret_cast<const Entry*>( + bsearch(&resource_id, resource_table_, resource_count_, sizeof(Entry), + Entry::CompareById)); + if (ret == nullptr) { + // Search the alias table for the ~10% of entries which are aliases. + const Alias* alias = reinterpret_cast<const Alias*>( + bsearch(&resource_id, alias_table_, alias_count_, sizeof(Alias), + Alias::CompareById)); + if (alias != nullptr) { + ret = &resource_table_[alias->entry_index]; + } + } + return ret; +} + bool DataPack::HasResource(uint16_t resource_id) const { - return !!bsearch(&resource_id, data_source_->GetData() + kHeaderLength, - resource_count_, sizeof(DataPackEntry), - DataPackEntry::CompareById); + return !!LookupEntryById(resource_id); } bool DataPack::GetStringPiece(uint16_t resource_id, @@ -271,20 +320,19 @@ bool DataPack::GetStringPiece(uint16_t resource_id, #error DataPack assumes little endian #endif - const DataPackEntry* target = reinterpret_cast<const DataPackEntry*>(bsearch( - &resource_id, data_source_->GetData() + kHeaderLength, resource_count_, - sizeof(DataPackEntry), DataPackEntry::CompareById)); - if (!target) { + const Entry* target = LookupEntryById(resource_id); + if (!target) return false; - } - const DataPackEntry* next_entry = target + 1; + const Entry* next_entry = target + 1; // If the next entry points beyond the end of the file this data pack's entry // table is corrupt. Log an error and return false. See // http://crbug.com/371301. - if (next_entry->file_offset > data_source_->GetLength()) { - size_t entry_index = target - reinterpret_cast<const DataPackEntry*>( - data_source_->GetData() + kHeaderLength); + size_t entry_offset = + reinterpret_cast<const uint8_t*>(next_entry) - data_source_->GetData(); + size_t pak_size = data_source_->GetLength(); + if (entry_offset > pak_size || next_entry->file_offset > pak_size) { + size_t entry_index = target - resource_table_; LOG(ERROR) << "Entry #" << entry_index << " in data pack points off end " << "of file. This should have been caught when loading. Was the " << "file modified?"; @@ -320,9 +368,7 @@ ui::ScaleFactor DataPack::GetScaleFactor() const { void DataPack::CheckForDuplicateResources( const std::vector<std::unique_ptr<ResourceHandle>>& packs) { for (size_t i = 0; i < resource_count_ + 1; ++i) { - const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>( - data_source_->GetData() + kHeaderLength + (i * sizeof(DataPackEntry))); - const uint16_t resource_id = entry->resource_id; + const uint16_t resource_id = resource_table_[i].resource_id; const float resource_scale = GetScaleForScaleFactor(scale_factor_); for (const auto& handle : packs) { if (GetScaleForScaleFactor(handle->GetScaleFactor()) != resource_scale) @@ -339,56 +385,44 @@ void DataPack::CheckForDuplicateResources( bool DataPack::WritePack(const base::FilePath& path, const std::map<uint16_t, base::StringPiece>& resources, TextEncodingType textEncodingType) { - FILE* file = base::OpenFile(path, "wb"); - if (!file) - return false; - - if (fwrite(&kFileFormatVersion, sizeof(kFileFormatVersion), 1, file) != 1) { - LOG(ERROR) << "Failed to write file version"; - base::CloseFile(file); - return false; - } - - // Note: the python version of this function explicitly sorted keys, but - // std::map is a sorted associative container, we shouldn't have to do that. - uint32_t entry_count = resources.size(); - if (fwrite(&entry_count, sizeof(entry_count), 1, file) != 1) { - LOG(ERROR) << "Failed to write entry count"; - base::CloseFile(file); - return false; - } - if (textEncodingType != UTF8 && textEncodingType != UTF16 && textEncodingType != BINARY) { LOG(ERROR) << "Invalid text encoding type, got " << textEncodingType << ", expected between " << BINARY << " and " << UTF16; - base::CloseFile(file); return false; } - uint8_t write_buffer = static_cast<uint8_t>(textEncodingType); - if (fwrite(&write_buffer, sizeof(uint8_t), 1, file) != 1) { - LOG(ERROR) << "Failed to write file text resources encoding"; + FILE* file = base::OpenFile(path, "wb"); + if (!file) + return false; + + uint32_t encoding = static_cast<uint32_t>(textEncodingType); + // Note: the python version of this function explicitly sorted keys, but + // std::map is a sorted associative container, we shouldn't have to do that. + uint16_t entry_count = resources.size(); + // Don't bother computing aliases (revisit if it becomes worth it). + uint16_t alias_count = 0; + + if (fwrite(&kFileFormatV5, sizeof(kFileFormatV5), 1, file) != 1 || + fwrite(&encoding, sizeof(uint32_t), 1, file) != 1 || + fwrite(&entry_count, sizeof(entry_count), 1, file) != 1 || + fwrite(&alias_count, sizeof(alias_count), 1, file) != 1) { + LOG(ERROR) << "Failed to write header"; base::CloseFile(file); return false; } // Each entry is a uint16_t + a uint32_t. We have an extra entry after the // last item so we can compute the size of the list item. - uint32_t index_length = (entry_count + 1) * sizeof(DataPackEntry); - uint32_t data_offset = kHeaderLength + index_length; + uint32_t index_length = (entry_count + 1) * sizeof(Entry); + uint32_t data_offset = kHeaderLengthV5 + index_length; for (std::map<uint16_t, base::StringPiece>::const_iterator it = resources.begin(); it != resources.end(); ++it) { uint16_t resource_id = it->first; - if (fwrite(&resource_id, sizeof(resource_id), 1, file) != 1) { - LOG(ERROR) << "Failed to write id for " << resource_id; - base::CloseFile(file); - return false; - } - - if (fwrite(&data_offset, sizeof(data_offset), 1, file) != 1) { - LOG(ERROR) << "Failed to write offset for " << resource_id; + if (fwrite(&resource_id, sizeof(resource_id), 1, file) != 1 || + fwrite(&data_offset, sizeof(data_offset), 1, file) != 1) { + LOG(ERROR) << "Failed to write entry for " << resource_id; base::CloseFile(file); return false; } diff --git a/chromium/ui/base/resource/data_pack.h b/chromium/ui/base/resource/data_pack.h index 56d30d28e39..ffd47a4ad5e 100644 --- a/chromium/ui/base/resource/data_pack.h +++ b/chromium/ui/base/resource/data_pack.h @@ -75,6 +75,8 @@ class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { #endif private: + struct Entry; + struct Alias; class DataSource; class BufferDataSource; class MemoryMappedDataSource; @@ -82,11 +84,14 @@ class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { // Does the actual loading of a pack file. // Called by Load and LoadFromFile and LoadFromBuffer. bool LoadImpl(std::unique_ptr<DataSource> data_source); + const Entry* LookupEntryById(uint16_t resource_id) const; std::unique_ptr<DataSource> data_source_; - // Number of resources in the data. + const Entry* resource_table_; size_t resource_count_; + const Alias* alias_table_; + size_t alias_count_; // Type of encoding for text resources. TextEncodingType text_encoding_type_; diff --git a/chromium/ui/base/resource/data_pack_literal.cc b/chromium/ui/base/resource/data_pack_literal.cc index cf490868cca..3218f848e94 100644 --- a/chromium/ui/base/resource/data_pack_literal.cc +++ b/chromium/ui/base/resource/data_pack_literal.cc @@ -4,57 +4,71 @@ #include <stddef.h> +#include "ui/base/resource/data_pack_literal.h" + namespace ui { -extern const char kSamplePakContents[] = { - 0x04, 0x00, 0x00, 0x00, // header(version - 0x04, 0x00, 0x00, 0x00, // no. entries - 0x01, // encoding) - 0x01, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 1 - 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 4 - 0x06, 0x00, 0x33, 0x00, 0x00, 0x00, // index entry 6 - 0x0a, 0x00, 0x3f, 0x00, 0x00, 0x00, // index entry 10 - 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, // extra entry for the size of last - 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', - 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '6' -}; +const char kSamplePakContentsV4[] = { + 0x04, 0x00, 0x00, 0x00, // header(version + 0x04, 0x00, 0x00, 0x00, // no. entries + 0x01, // encoding) + 0x01, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 1 + 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 4 + 0x06, 0x00, 0x33, 0x00, 0x00, 0x00, // index entry 6 + 0x0a, 0x00, 0x3f, 0x00, 0x00, 0x00, // index entry 10 + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, // extra entry for the size of last + 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', + 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '6'}; -extern const size_t kSamplePakSize = sizeof(kSamplePakContents); - -extern const char kSampleCorruptPakContents[] = { - 0x04, 0x00, 0x00, 0x00, // header(version - 0x04, 0x00, 0x00, 0x00, // no. entries - 0x01, // encoding) - 0x01, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 1 - 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 4 - 0x06, 0x00, 0x33, 0x00, 0x00, 0x00, // index entry 6 - 0x0a, 0x00, 0x3f, 0x00, 0x00, 0x00, // index entry 10 - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // extra entry for the size of last, - // extends past END OF FILE. - 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', - 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '6' -}; +const size_t kSamplePakSizeV4 = sizeof(kSamplePakContentsV4); -extern const size_t kSampleCorruptPakSize = sizeof(kSampleCorruptPakContents); +const char kSamplePakContentsV5[] = { + 0x05, 0x00, 0x00, 0x00, // version + 0x01, 0x00, 0x00, 0x00, // encoding + padding + 0x03, 0x00, 0x01, 0x00, // num_resources, num_aliases + 0x01, 0x00, 0x28, 0x00, 0x00, 0x00, // index entry 1 + 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, // index entry 4 + 0x06, 0x00, 0x34, 0x00, 0x00, 0x00, // index entry 6 + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // extra entry for the size of last + 0x0a, 0x00, 0x01, 0x00, // alias table + 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', + 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '6'}; -extern const char kSamplePakContents2x[] = { - 0x04, 0x00, 0x00, 0x00, // header(version - 0x01, 0x00, 0x00, 0x00, // no. entries - 0x01, // encoding) - 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, // index entry 4 - 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, // extra entry for the size of last - 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', ' ', '2', 'x' -}; +const size_t kSamplePakSizeV5 = sizeof(kSamplePakContentsV5); + +const char kSampleCorruptPakContents[] = { + 0x04, 0x00, 0x00, 0x00, // header(version + 0x04, 0x00, 0x00, 0x00, // no. entries + 0x01, // encoding) + 0x01, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 1 + 0x04, 0x00, 0x27, 0x00, 0x00, 0x00, // index entry 4 + 0x06, 0x00, 0x33, 0x00, 0x00, 0x00, // index entry 6 + 0x0a, 0x00, 0x3f, 0x00, 0x00, 0x00, // index entry 10 + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // extra entry for the size of last, + // extends past END OF FILE. + 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', 't', 'h', 'i', + 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '6'}; + +const size_t kSampleCorruptPakSize = sizeof(kSampleCorruptPakContents); + +const char kSamplePakContents2x[] = { + 0x04, 0x00, 0x00, 0x00, // header(version + 0x01, 0x00, 0x00, 0x00, // no. entries + 0x01, // encoding) + 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, // index entry 4 + 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, // extra entry for the size of last + 't', 'h', 'i', 's', ' ', 'i', 's', ' ', + 'i', 'd', ' ', '4', ' ', '2', 'x'}; -extern const size_t kSamplePakSize2x = sizeof(kSamplePakContents2x); +const size_t kSamplePakSize2x = sizeof(kSamplePakContents2x); -extern const char kEmptyPakContents[] = { - 0x04, 0x00, 0x00, 0x00, // header(version - 0x00, 0x00, 0x00, 0x00, // no. entries - 0x01, // encoding) - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00 // extra entry for the size of last +const char kEmptyPakContents[] = { + 0x04, 0x00, 0x00, 0x00, // header(version + 0x00, 0x00, 0x00, 0x00, // no. entries + 0x01, // encoding) + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00 // extra entry for the size of last }; -extern const size_t kEmptyPakSize = sizeof(kEmptyPakContents); +const size_t kEmptyPakSize = sizeof(kEmptyPakContents); } // namespace ui diff --git a/chromium/ui/base/resource/data_pack_literal.h b/chromium/ui/base/resource/data_pack_literal.h new file mode 100644 index 00000000000..0e74647771d --- /dev/null +++ b/chromium/ui/base/resource/data_pack_literal.h @@ -0,0 +1,23 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_RESOURCE_DATA_PACK_LITERAL_H_ +#define UI_BASE_RESOURCE_DATA_PACK_LITERAL_H_ + +namespace ui { + +extern const char kSamplePakContentsV4[]; +extern const size_t kSamplePakSizeV4; +extern const char kSamplePakContentsV5[]; +extern const size_t kSamplePakSizeV5; +extern const char kSamplePakContents2x[]; +extern const size_t kSamplePakSize2x; +extern const char kEmptyPakContents[]; +extern const size_t kEmptyPakSize; +extern const char kSampleCorruptPakContents[]; +extern const size_t kSampleCorruptPakSize; + +} // namespace ui + +#endif // UI_BASE_RESOURCE_DATA_PACK_LITERAL_H_ diff --git a/chromium/ui/base/resource/data_pack_unittest.cc b/chromium/ui/base/resource/data_pack_unittest.cc index 4d5319637bc..d2615b08954 100644 --- a/chromium/ui/base/resource/data_pack_unittest.cc +++ b/chromium/ui/base/resource/data_pack_unittest.cc @@ -17,6 +17,7 @@ #include "base/strings/string_piece.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/resource/data_pack_literal.h" #include "ui/base/ui_base_paths.h" namespace ui { @@ -27,11 +28,6 @@ class DataPackTest DataPackTest() {} }; -extern const char kSamplePakContents[]; -extern const char kSampleCorruptPakContents[]; -extern const size_t kSamplePakSize; -extern const size_t kSampleCorruptPakSize; - TEST(DataPackTest, LoadFromPath) { base::ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); @@ -39,8 +35,8 @@ TEST(DataPackTest, LoadFromPath) { dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak file. - ASSERT_EQ(base::WriteFile(data_path, kSamplePakContents, kSamplePakSize), - static_cast<int>(kSamplePakSize)); + ASSERT_EQ(base::WriteFile(data_path, kSamplePakContentsV4, kSamplePakSizeV4), + static_cast<int>(kSamplePakSizeV4)); // Load the file through the data pack API. DataPack pack(SCALE_FACTOR_100P); @@ -72,8 +68,8 @@ TEST(DataPackTest, LoadFromFile) { dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak file. - ASSERT_EQ(base::WriteFile(data_path, kSamplePakContents, kSamplePakSize), - static_cast<int>(kSamplePakSize)); + ASSERT_EQ(base::WriteFile(data_path, kSamplePakContentsV4, kSamplePakSizeV4), + static_cast<int>(kSamplePakSizeV4)); base::File file(data_path, base::File::FLAG_OPEN | base::File::FLAG_READ); ASSERT_TRUE(file.IsValid()); @@ -112,15 +108,15 @@ TEST(DataPackTest, LoadFromFileRegion) { const char kPadding[5678] = {0}; ASSERT_EQ(static_cast<int>(sizeof(kPadding)), base::WriteFile(data_path, kPadding, sizeof(kPadding))); - ASSERT_TRUE(base::AppendToFile( - data_path, kSamplePakContents, kSamplePakSize)); + ASSERT_TRUE( + base::AppendToFile(data_path, kSamplePakContentsV4, kSamplePakSizeV4)); base::File file(data_path, base::File::FLAG_OPEN | base::File::FLAG_READ); ASSERT_TRUE(file.IsValid()); // Load the file through the data pack API. DataPack pack(SCALE_FACTOR_100P); - base::MemoryMappedFile::Region region = {sizeof(kPadding), kSamplePakSize}; + base::MemoryMappedFile::Region region = {sizeof(kPadding), kSamplePakSizeV4}; ASSERT_TRUE(pack.LoadFromFileRegion(std::move(file), region)); base::StringPiece data; @@ -142,11 +138,11 @@ TEST(DataPackTest, LoadFromFileRegion) { ASSERT_FALSE(pack.GetStringPiece(140, &data)); } -TEST(DataPackTest, LoadFromBuffer) { +TEST(DataPackTest, LoadFromBufferV4) { DataPack pack(SCALE_FACTOR_100P); ASSERT_TRUE(pack.LoadFromBuffer( - base::StringPiece(kSamplePakContents, kSamplePakSize))); + base::StringPiece(kSamplePakContentsV4, kSamplePakSizeV4))); base::StringPiece data; ASSERT_TRUE(pack.HasResource(4)); @@ -167,6 +163,31 @@ TEST(DataPackTest, LoadFromBuffer) { ASSERT_FALSE(pack.GetStringPiece(140, &data)); } +TEST(DataPackTest, LoadFromBufferV5) { + DataPack pack(SCALE_FACTOR_100P); + + ASSERT_TRUE(pack.LoadFromBuffer( + base::StringPiece(kSamplePakContentsV5, kSamplePakSizeV5))); + + base::StringPiece data; + ASSERT_TRUE(pack.HasResource(4)); + ASSERT_TRUE(pack.GetStringPiece(4, &data)); + EXPECT_EQ("this is id 4", data); + ASSERT_TRUE(pack.HasResource(6)); + ASSERT_TRUE(pack.GetStringPiece(6, &data)); + EXPECT_EQ("this is id 6", data); + + // Try reading zero-length data blobs, just in case. + ASSERT_TRUE(pack.GetStringPiece(1, &data)); + EXPECT_EQ(0U, data.length()); + ASSERT_TRUE(pack.GetStringPiece(10, &data)); + EXPECT_EQ("this is id 4", data); + + // Try looking up an invalid key. + ASSERT_FALSE(pack.HasResource(140)); + ASSERT_FALSE(pack.GetStringPiece(140, &data)); +} + INSTANTIATE_TEST_CASE_P(WriteBINARY, DataPackTest, ::testing::Values( DataPack::BINARY)); INSTANTIATE_TEST_CASE_P(WriteUTF8, DataPackTest, ::testing::Values( @@ -228,8 +249,8 @@ TEST(DataPackTest, ModifiedWhileUsed) { dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); // Dump contents into the pak file. - ASSERT_EQ(base::WriteFile(data_path, kSamplePakContents, kSamplePakSize), - static_cast<int>(kSamplePakSize)); + ASSERT_EQ(base::WriteFile(data_path, kSamplePakContentsV4, kSamplePakSizeV4), + static_cast<int>(kSamplePakSizeV4)); base::File file(data_path, base::File::FLAG_OPEN | base::File::FLAG_READ); ASSERT_TRUE(file.IsValid()); diff --git a/chromium/ui/base/resource/resource_bundle.cc b/chromium/ui/base/resource/resource_bundle.cc index 372d3302e13..4f823477883 100644 --- a/chromium/ui/base/resource/resource_bundle.cc +++ b/chromium/ui/base/resource/resource_bundle.cc @@ -707,7 +707,7 @@ void ResourceBundle::ReloadFonts() { } ScaleFactor ResourceBundle::GetMaxScaleFactor() const { -#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_LINUX) +#if defined(OS_WIN) || defined(OS_LINUX) return max_scale_factor_; #else return GetSupportedScaleFactors().back(); @@ -762,8 +762,7 @@ void ResourceBundle::InitSharedInstance(Delegate* delegate) { } else { supported_scale_factors.push_back(SCALE_FACTOR_100P); } -#elif defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_LINUX) || \ - defined(OS_WIN) +#elif defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_WIN) supported_scale_factors.push_back(SCALE_FACTOR_200P); #endif ui::SetSupportedScaleFactors(supported_scale_factors); diff --git a/chromium/ui/base/resource/resource_bundle_unittest.cc b/chromium/ui/base/resource/resource_bundle_unittest.cc index df11f1afddb..363a0e9d03d 100644 --- a/chromium/ui/base/resource/resource_bundle_unittest.cc +++ b/chromium/ui/base/resource/resource_bundle_unittest.cc @@ -23,6 +23,7 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/layout.h" #include "ui/base/resource/data_pack.h" +#include "ui/base/resource/data_pack_literal.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/font_list.h" #include "ui/gfx/image/image_skia.h" @@ -38,14 +39,6 @@ using ::testing::Return; using ::testing::ReturnArg; namespace ui { - -extern const char kSamplePakContents[]; -extern const size_t kSamplePakSize; -extern const char kSamplePakContents2x[]; -extern const size_t kSamplePakSize2x; -extern const char kEmptyPakContents[]; -extern const size_t kEmptyPakSize; - namespace { const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 }; @@ -416,8 +409,8 @@ TEST_F(ResourceBundleImageTest, GetRawDataResource) { dir_path().Append(FILE_PATH_LITERAL("sample_2x.pak")); // Dump contents into the pak files. - ASSERT_EQ(base::WriteFile(data_path, kSamplePakContents, - kSamplePakSize), static_cast<int>(kSamplePakSize)); + ASSERT_EQ(base::WriteFile(data_path, kSamplePakContentsV4, kSamplePakSizeV4), + static_cast<int>(kSamplePakSizeV4)); ASSERT_EQ(base::WriteFile(data_2x_path, kSamplePakContents2x, kSamplePakSize2x), static_cast<int>(kSamplePakSize2x)); diff --git a/chromium/ui/base/ui_base_switches.cc b/chromium/ui/base/ui_base_switches.cc index ed2ef37c03f..6681787be91 100644 --- a/chromium/ui/base/ui_base_switches.cc +++ b/chromium/ui/base/ui_base_switches.cc @@ -5,6 +5,16 @@ #include "build/build_config.h" #include "ui/base/ui_base_switches.h" +namespace features { + +#if defined(OS_WIN) +// Enables stylus appearing as touch when in contact with digitizer. +const base::Feature kDirectManipulationStylus = { + "DirectManipulationStylus", base::FEATURE_ENABLED_BY_DEFAULT}; +#endif // defined(OS_WIN) + +} // namespace features + namespace switches { #if defined(OS_MACOSX) && !defined(OS_IOS) @@ -31,6 +41,9 @@ const char kDisableMergeKeyCharEvents[] = "disable-merge-key-char-events"; const char kEnableMergeKeyCharEvents[] = "enable-merge-key-char-events"; #endif +// Disables layer-edge anti-aliasing in the compositor. +const char kDisableCompositedAntialiasing[] = "disable-composited-antialiasing"; + // Disables use of DWM composition for top level windows. const char kDisableDwmComposition[] = "disable-dwm-composition"; @@ -43,6 +56,13 @@ const char kDisableTouchDragDrop[] = "disable-touch-drag-drop"; // Enables touch event based drag and drop. const char kEnableTouchDragDrop[] = "enable-touch-drag-drop"; +// TODO(dcastagna): Draw debug quad borders only when it is actually +// an overlay candidate. +// Renders a border around GL composited overlay candidate quads to +// help debug and study overlay support. +const char kGlCompositedOverlayCandidateQuadBorder[] = + "gl-composited-overlay-candidate-quad-border"; + // The language file that we want to try to open. Of the form // language[-country] where language is the 2 letter code from ISO-639. const char kLang[] = "lang"; @@ -79,4 +99,33 @@ const char kDisableNewVirtualKeyboardBehavior[] = // throughout Chrome (not just top Chrome). const char kExtendMdToSecondaryUi[] = "secondary-ui-md"; +// Disable partial swap which is needed for some OpenGL drivers / emulators. +const char kUIDisablePartialSwap[] = "ui-disable-partial-swap"; + +// Visualize overdraw by color-coding elements based on if they have other +// elements drawn underneath. This is good for showing where the UI might be +// doing more rendering work than necessary. The colors are hinting at the +// amount of overdraw on your screen for each pixel, as follows: +// +// True color: No overdraw. +// Blue: Overdrawn once. +// Green: Overdrawn twice. +// Pink: Overdrawn three times. +// Red: Overdrawn four or more times. +const char kShowOverdrawFeedback[] = "show-overdraw-feedback"; + +// Use SkiaRenderer instead of GLRenderer for direct rendering. +const char kUseSkiaRenderer[] = "use-skia-renderer"; + +// Disable re-use of non-exact resources to fulfill ResourcePool requests. +// Intended only for use in layout or pixel tests to reduce noise. +const char kDisallowNonExactResourceReuse[] = + "disallow-non-exact-resource-reuse"; + +// Re-draw everything multiple times to simulate a much slower machine. +// Give a slow down factor to cause renderer to take that many times longer to +// complete, such as --slow-down-compositing-scale-factor=2. +const char kSlowDownCompositingScaleFactor[] = + "slow-down-compositing-scale-factor"; + } // namespace switches diff --git a/chromium/ui/base/ui_base_switches.h b/chromium/ui/base/ui_base_switches.h index 69e8cc1ee17..7a7d35865d0 100644 --- a/chromium/ui/base/ui_base_switches.h +++ b/chromium/ui/base/ui_base_switches.h @@ -8,9 +8,18 @@ #define UI_BASE_UI_BASE_SWITCHES_H_ #include "base/compiler_specific.h" +#include "base/feature_list.h" #include "build/build_config.h" #include "ui/base/ui_base_export.h" +namespace features { + +#if defined(OS_WIN) +UI_BASE_EXPORT extern const base::Feature kDirectManipulationStylus; +#endif // defined(OS_WIN) + +} // namespace features + namespace switches { #if defined(OS_MACOSX) && !defined(OS_IOS) @@ -25,21 +34,29 @@ UI_BASE_EXPORT extern const char kDisableMergeKeyCharEvents[]; UI_BASE_EXPORT extern const char kEnableMergeKeyCharEvents[]; #endif +UI_BASE_EXPORT extern const char kDisableCompositedAntialiasing[]; UI_BASE_EXPORT extern const char kDisableDwmComposition[]; UI_BASE_EXPORT extern const char kDisableNewVirtualKeyboardBehavior[]; UI_BASE_EXPORT extern const char kDisableTouchAdjustment[]; UI_BASE_EXPORT extern const char kDisableTouchDragDrop[]; UI_BASE_EXPORT extern const char kEnableTouchDragDrop[]; +UI_BASE_EXPORT extern const char kExtendMdToSecondaryUi[]; +UI_BASE_EXPORT extern const char kGlCompositedOverlayCandidateQuadBorder[]; UI_BASE_EXPORT extern const char kLang[]; UI_BASE_EXPORT extern const char kMaterialDesignInkDropAnimationSpeed[]; UI_BASE_EXPORT extern const char kMaterialDesignInkDropAnimationSpeedFast[]; UI_BASE_EXPORT extern const char kMaterialDesignInkDropAnimationSpeedSlow[]; - +UI_BASE_EXPORT extern const char kShowOverdrawFeedback[]; +UI_BASE_EXPORT extern const char kSlowDownCompositingScaleFactor[]; UI_BASE_EXPORT extern const char kTopChromeMD[]; UI_BASE_EXPORT extern const char kTopChromeMDMaterial[]; UI_BASE_EXPORT extern const char kTopChromeMDMaterialHybrid[]; UI_BASE_EXPORT extern const char kTopChromeMDNonMaterial[]; -UI_BASE_EXPORT extern const char kExtendMdToSecondaryUi[]; +UI_BASE_EXPORT extern const char kUIDisablePartialSwap[]; +UI_BASE_EXPORT extern const char kUseSkiaRenderer[]; + +// Test related. +UI_BASE_EXPORT extern const char kDisallowNonExactResourceReuse[]; } // namespace switches diff --git a/chromium/ui/base/ui_features.gni b/chromium/ui/base/ui_features.gni index 2dca6a502ca..186dc04160d 100644 --- a/chromium/ui/base/ui_features.gni +++ b/chromium/ui/base/ui_features.gni @@ -13,6 +13,9 @@ declare_args() { # Whether the platform provides a native accessibility toolkit. has_native_accessibility = use_atk || is_win || is_mac + + # Whether the message center should be included for displaying notifications. + enable_message_center = is_win || is_mac || is_linux || is_chromeos } enable_hidpi = is_mac || is_win || is_linux 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 0b9b7608d6a..b2decd61f41 100644 --- a/chromium/ui/base/user_activity/user_activity_detector_unittest.cc +++ b/chromium/ui/base/user_activity/user_activity_detector_unittest.cc @@ -92,8 +92,7 @@ TEST_F(UserActivityDetectorTest, Basic) { ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_NONE); OnEvent(&key_event); EXPECT_FALSE(key_event.handled()); - EXPECT_EQ(now_.ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(now_, detector_->last_activity_time()); EXPECT_EQ(1, observer_->num_invocations()); observer_->reset_stats(); @@ -104,8 +103,7 @@ TEST_F(UserActivityDetectorTest, Basic) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); OnEvent(&mouse_event); EXPECT_FALSE(mouse_event.handled()); - EXPECT_EQ(now_.ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(now_, detector_->last_activity_time()); EXPECT_EQ(1, observer_->num_invocations()); observer_->reset_stats(); @@ -115,8 +113,7 @@ TEST_F(UserActivityDetectorTest, Basic) { detector_->OnDisplayPowerChanging(); OnEvent(&mouse_event); EXPECT_FALSE(mouse_event.handled()); - EXPECT_EQ(time_before_ignore.ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(time_before_ignore, detector_->last_activity_time()); EXPECT_EQ(0, observer_->num_invocations()); observer_->reset_stats(); @@ -126,8 +123,7 @@ TEST_F(UserActivityDetectorTest, Basic) { AdvanceTime(kIgnoreMouseTime / 2); OnEvent(&mouse_event); EXPECT_FALSE(mouse_event.handled()); - EXPECT_EQ(time_before_ignore.ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(time_before_ignore, detector_->last_activity_time()); EXPECT_EQ(0, observer_->num_invocations()); observer_->reset_stats(); @@ -135,8 +131,7 @@ TEST_F(UserActivityDetectorTest, Basic) { AdvanceTime(std::max(kIgnoreMouseTime, advance_delta)); OnEvent(&mouse_event); EXPECT_FALSE(mouse_event.handled()); - EXPECT_EQ(now_.ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(now_, detector_->last_activity_time()); EXPECT_EQ(1, observer_->num_invocations()); observer_->reset_stats(); @@ -146,8 +141,7 @@ TEST_F(UserActivityDetectorTest, Basic) { ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); OnEvent(&touch_event); EXPECT_FALSE(touch_event.handled()); - EXPECT_EQ(now_.ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(now_, detector_->last_activity_time()); EXPECT_EQ(1, observer_->num_invocations()); observer_->reset_stats(); @@ -156,8 +150,7 @@ TEST_F(UserActivityDetectorTest, Basic) { ui::GestureEventDetails(ui::ET_GESTURE_TAP)); OnEvent(&gesture_event); EXPECT_FALSE(gesture_event.handled()); - EXPECT_EQ(now_.ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(now_, detector_->last_activity_time()); EXPECT_EQ(1, observer_->num_invocations()); observer_->reset_stats(); } @@ -204,8 +197,7 @@ TEST_F(UserActivityDetectorTest, IgnoreSyntheticMouseEvents) { ui::EF_NONE); OnEvent(&mouse_event); EXPECT_FALSE(mouse_event.handled()); - EXPECT_EQ(base::TimeTicks().ToInternalValue(), - detector_->last_activity_time().ToInternalValue()); + EXPECT_EQ(base::TimeTicks(), detector_->last_activity_time()); EXPECT_EQ(0, observer_->num_invocations()); } diff --git a/chromium/ui/base/webui/OWNERS b/chromium/ui/base/webui/OWNERS index 89558c26a0f..918baa1330f 100644 --- a/chromium/ui/base/webui/OWNERS +++ b/chromium/ui/base/webui/OWNERS @@ -1,5 +1,3 @@ file://ui/webui/PLATFORM_OWNERS -dschuyler@chromium.org - # COMPONENT: UI>Browser>WebUI diff --git a/chromium/ui/base/win/accessibility_misc_utils.h b/chromium/ui/base/win/accessibility_misc_utils.h index bb5befce2b3..23c9ca97302 100644 --- a/chromium/ui/base/win/accessibility_misc_utils.h +++ b/chromium/ui/base/win/accessibility_misc_utils.h @@ -17,7 +17,7 @@ namespace win { // UIA Text provider implementation for edit controls. class UI_BASE_EXPORT UIATextProvider - : public NON_EXPORTED_BASE(CComObjectRootEx<CComMultiThreadModel>), + : public CComObjectRootEx<CComMultiThreadModel>, public IValueProvider, public ITextProvider { public: diff --git a/chromium/ui/base/win/shell.cc b/chromium/ui/base/win/shell.cc index dd8459e9b2e..cd82678bd9e 100644 --- a/chromium/ui/base/win/shell.cc +++ b/chromium/ui/base/win/shell.cc @@ -6,8 +6,10 @@ #include <dwmapi.h> #include <shlobj.h> // Must be before propkey. + #include <propkey.h> #include <shellapi.h> +#include <wrl/client.h> #include "base/command_line.h" #include "base/debug/alias.h" @@ -17,7 +19,6 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" -#include "base/win/scoped_comptr.h" #include "base/win/win_util.h" #include "ui/base/ui_base_switches.h" @@ -98,7 +99,7 @@ bool OpenFolderViaShell(const base::FilePath& full_path) { bool PreventWindowFromPinning(HWND hwnd) { DCHECK(hwnd); - base::win::ScopedComPtr<IPropertyStore> pps; + Microsoft::WRL::ComPtr<IPropertyStore> pps; if (FAILED( SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(pps.GetAddressOf())))) return false; @@ -117,7 +118,7 @@ void SetAppDetailsForWindow(const base::string16& app_id, HWND hwnd) { DCHECK(hwnd); - base::win::ScopedComPtr<IPropertyStore> pps; + Microsoft::WRL::ComPtr<IPropertyStore> pps; if (FAILED( SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(pps.GetAddressOf())))) return; @@ -167,7 +168,7 @@ void SetRelaunchDetailsForWindow(const base::string16& relaunch_command, void ClearWindowPropertyStore(HWND hwnd) { DCHECK(hwnd); - base::win::ScopedComPtr<IPropertyStore> pps; + Microsoft::WRL::ComPtr<IPropertyStore> pps; if (FAILED( SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(pps.GetAddressOf())))) return; @@ -177,13 +178,18 @@ void ClearWindowPropertyStore(HWND hwnd) { return; PROPVARIANT empty_property_variant = {}; - for (DWORD i = 0; i < property_count; i++) { + for (DWORD i = property_count; i > 0; i--) { PROPERTYKEY key; - if (SUCCEEDED(pps->GetAt(i, &key))) + if (SUCCEEDED(pps->GetAt(i - 1, &key))) { + // Removes the value from |pps|'s array. pps->SetValue(key, empty_property_variant); + } } + if (FAILED(pps->Commit())) + return; - pps->Commit(); + // Verify none of the keys are leaking. + DCHECK(FAILED(pps->GetCount(&property_count)) || property_count == 0); } bool IsAeroGlassEnabled() { diff --git a/chromium/ui/base/x/x11_util.cc b/chromium/ui/base/x/x11_util.cc index f97eca3f7e9..dbb8cc534e3 100644 --- a/chromium/ui/base/x/x11_util.cc +++ b/chromium/ui/base/x/x11_util.cc @@ -391,12 +391,8 @@ int CoalescePendingMotionEvents(const XEvent* xev, XEvent* last_event) { break; } - if (event_type == XI_Motion && num_coalesced > 0) { - base::TimeDelta delta = ui::EventTimeFromNative(last_event) - - ui::EventTimeFromNative(const_cast<XEvent*>(xev)); + if (event_type == XI_Motion && num_coalesced > 0) UMA_HISTOGRAM_COUNTS_10000("Event.CoalescedCount.Mouse", num_coalesced); - UMA_HISTOGRAM_TIMES("Event.CoalescedLatency.Mouse", delta); - } return num_coalesced; } diff --git a/chromium/ui/chromeos/BUILD.gn b/chromium/ui/chromeos/BUILD.gn index e83d1d043a7..0b7d6c0cc4e 100644 --- a/chromium/ui/chromeos/BUILD.gn +++ b/chromium/ui/chromeos/BUILD.gn @@ -11,6 +11,8 @@ component("chromeos") { sources = [ "accelerometer/accelerometer_util.cc", "accelerometer/accelerometer_util.h", + "devicetype_utils.cc", + "devicetype_utils.h", "ime/candidate_view.cc", "ime/candidate_view.h", "ime/candidate_window_constants.h", diff --git a/chromium/ui/compositor/BUILD.gn b/chromium/ui/compositor/BUILD.gn index a992c2722fe..82793dca686 100644 --- a/chromium/ui/compositor/BUILD.gn +++ b/chromium/ui/compositor/BUILD.gn @@ -20,21 +20,19 @@ component("compositor") { "compositor.cc", "compositor.h", "compositor_animation_observer.h", - "compositor_constants.h", "compositor_export.h", "compositor_lock.cc", "compositor_lock.h", "compositor_observer.h", "compositor_switches.cc", "compositor_switches.h", - "compositor_util.cc", - "compositor_util.h", "compositor_vsync_manager.cc", "compositor_vsync_manager.h", "debug_utils.cc", "debug_utils.h", "dip_util.cc", "dip_util.h", + "external_begin_frame_client.h", "float_animation_curve_adapter.cc", "float_animation_curve_adapter.h", "layer.cc", @@ -86,13 +84,12 @@ component("compositor") { "//cc", "//cc/animation", "//cc/paint", - "//cc/surfaces", - "//cc/surfaces:surfaces", "//components/viz/common", "//components/viz/host", "//components/viz/service", "//gpu/command_buffer/common", "//skia", + "//ui/base", "//ui/display", "//ui/gfx", "//ui/gfx/animation", @@ -156,9 +153,9 @@ static_library("test_support") { "//base/test:test_support", "//cc", "//cc:test_support", - "//cc/surfaces", "//components/viz/host", "//components/viz/service", + "//components/viz/test:test_support", "//gpu/command_buffer/client:gles2_c_lib", "//gpu/command_buffer/client:gles2_implementation", "//gpu/command_buffer/common:gles2_utils", @@ -211,8 +208,9 @@ test("compositor_unittests") { "//base/test:test_support", "//cc", "//cc:test_support", - "//cc/surfaces", "//components/viz/common", + "//components/viz/service", + "//components/viz/test:test_support", "//mojo/edk/system", "//skia", "//testing/gmock", diff --git a/chromium/ui/compositor/DEPS b/chromium/ui/compositor/DEPS index 75b24eca257..69f116388f5 100644 --- a/chromium/ui/compositor/DEPS +++ b/chromium/ui/compositor/DEPS @@ -18,5 +18,8 @@ include_rules = [ specific_include_rules = { "run_all_unittests\.cc": [ "+mojo/edk/embedder", - ] + ], + ".*_(unit|pixel|perf)test.*\.cc": [ + "+components/viz/test", + ], } diff --git a/chromium/ui/compositor/canvas_painter.cc b/chromium/ui/compositor/canvas_painter.cc index a12e85ab06e..864d8b03e84 100644 --- a/chromium/ui/compositor/canvas_painter.cc +++ b/chromium/ui/compositor/canvas_painter.cc @@ -11,13 +11,17 @@ namespace ui { CanvasPainter::CanvasPainter(SkBitmap* output, const gfx::Size& paint_size, float raster_scale, - SkColor clear_color) + SkColor clear_color, + bool is_pixel_canvas) : output_(output), paint_size_(paint_size), raster_scale_(raster_scale), clear_color_(clear_color), list_(new cc::DisplayItemList), - context_(list_.get(), raster_scale, gfx::Rect(paint_size_)) {} + context_(list_.get(), + raster_scale, + gfx::Rect(paint_size_), + is_pixel_canvas) {} CanvasPainter::~CanvasPainter() { gfx::Size pixel_size = gfx::ScaleToCeiledSize(paint_size_, raster_scale_); diff --git a/chromium/ui/compositor/canvas_painter.h b/chromium/ui/compositor/canvas_painter.h index 1db206f36b0..da44d7bb166 100644 --- a/chromium/ui/compositor/canvas_painter.h +++ b/chromium/ui/compositor/canvas_painter.h @@ -29,7 +29,8 @@ class COMPOSITOR_EXPORT CanvasPainter { CanvasPainter(SkBitmap* output, const gfx::Size& paint_size, float raster_scale, - SkColor clear_color); + SkColor clear_color, + bool is_pixel_canvas); ~CanvasPainter(); const PaintContext& context() const { return context_; } diff --git a/chromium/ui/compositor/clip_recorder.cc b/chromium/ui/compositor/clip_recorder.cc index 5f8133f2abf..77acf0f7cfc 100644 --- a/chromium/ui/compositor/clip_recorder.cc +++ b/chromium/ui/compositor/clip_recorder.cc @@ -21,8 +21,8 @@ ClipRecorder::~ClipRecorder() { for (int i = 0; i < num_closers_; ++i) { // Each restore is part of a separate visual rect, so gets its own // StartPaint/EndPaintOfPairedEnd. - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::RestoreOp>(); + context_.list_->StartPaint(); + context_.list_->push<cc::RestoreOp>(); context_.list_->EndPaintOfPairedEnd(); } } @@ -30,10 +30,10 @@ ClipRecorder::~ClipRecorder() { void ClipRecorder::ClipRect(const gfx::Rect& clip_rect) { bool antialias = false; - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::SaveOp>(); - buffer->push<cc::ClipRectOp>(gfx::RectToSkRect(clip_rect), - SkClipOp::kIntersect, antialias); + context_.list_->StartPaint(); + context_.list_->push<cc::SaveOp>(); + context_.list_->push<cc::ClipRectOp>(gfx::RectToSkRect(clip_rect), + SkClipOp::kIntersect, antialias); context_.list_->EndPaintOfPairedBegin(); ++num_closers_; } @@ -41,9 +41,10 @@ void ClipRecorder::ClipRect(const gfx::Rect& clip_rect) { void ClipRecorder::ClipPath(const gfx::Path& clip_path) { bool antialias = false; - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::SaveOp>(); - buffer->push<cc::ClipPathOp>(clip_path, SkClipOp::kIntersect, antialias); + context_.list_->StartPaint(); + context_.list_->push<cc::SaveOp>(); + context_.list_->push<cc::ClipPathOp>(clip_path, SkClipOp::kIntersect, + antialias); context_.list_->EndPaintOfPairedBegin(); ++num_closers_; } @@ -51,9 +52,10 @@ void ClipRecorder::ClipPath(const gfx::Path& clip_path) { void ClipRecorder::ClipPathWithAntiAliasing(const gfx::Path& clip_path) { bool antialias = true; - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::SaveOp>(); - buffer->push<cc::ClipPathOp>(clip_path, SkClipOp::kIntersect, antialias); + context_.list_->StartPaint(); + context_.list_->push<cc::SaveOp>(); + context_.list_->push<cc::ClipPathOp>(clip_path, SkClipOp::kIntersect, + antialias); context_.list_->EndPaintOfPairedBegin(); ++num_closers_; } diff --git a/chromium/ui/compositor/compositing_recorder.cc b/chromium/ui/compositor/compositing_recorder.cc index 4270342a13a..ab4bf8826ab 100644 --- a/chromium/ui/compositor/compositing_recorder.cc +++ b/chromium/ui/compositor/compositing_recorder.cc @@ -19,9 +19,9 @@ CompositingRecorder::CompositingRecorder(const PaintContext& context, if (!saved_) return; - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::SaveLayerAlphaOp>(nullptr, alpha, - !lcd_text_requires_opaque_layer); + context_.list_->StartPaint(); + context_.list_->push<cc::SaveLayerAlphaOp>(nullptr, alpha, + !lcd_text_requires_opaque_layer); context_.list_->EndPaintOfPairedBegin(); } @@ -29,8 +29,8 @@ CompositingRecorder::~CompositingRecorder() { if (!saved_) return; - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::RestoreOp>(); + context_.list_->StartPaint(); + context_.list_->push<cc::RestoreOp>(); context_.list_->EndPaintOfPairedEnd(); } diff --git a/chromium/ui/compositor/compositor.cc b/chromium/ui/compositor/compositor.cc index 70eab90261e..68e7df216cd 100644 --- a/chromium/ui/compositor/compositor.cc +++ b/chromium/ui/compositor/compositor.cc @@ -27,21 +27,24 @@ #include "cc/base/switches.h" #include "cc/input/input_handler.h" #include "cc/layers/layer.h" -#include "cc/output/begin_frame_args.h" #include "cc/output/latency_info_swap_promise.h" -#include "cc/scheduler/begin_frame_source.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_settings.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/frame_sinks/begin_frame_source.h" #include "components/viz/common/gpu/context_provider.h" -#include "components/viz/common/quads/resource_format.h" +#include "components/viz/common/resources/resource_format.h" #include "components/viz/common/resources/resource_settings.h" #include "components/viz/common/surfaces/local_surface_id_allocator.h" +#include "components/viz/host/host_frame_sink_manager.h" #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/base/ui_base_switches.h" #include "ui/compositor/compositor_observer.h" #include "ui/compositor/compositor_switches.h" #include "ui/compositor/compositor_vsync_manager.h" #include "ui/compositor/dip_util.h" +#include "ui/compositor/external_begin_frame_client.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator_collection.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" @@ -57,21 +60,27 @@ Compositor::Compositor(const viz::FrameSinkId& frame_sink_id, ui::ContextFactory* context_factory, ui::ContextFactoryPrivate* context_factory_private, scoped_refptr<base::SingleThreadTaskRunner> task_runner, - bool enable_surface_synchronization) + bool enable_surface_synchronization, + bool enable_pixel_canvas, + bool external_begin_frames_enabled, + bool force_software_compositor) : context_factory_(context_factory), context_factory_private_(context_factory_private), frame_sink_id_(frame_sink_id), task_runner_(task_runner), vsync_manager_(new CompositorVSyncManager()), + external_begin_frames_enabled_(external_begin_frames_enabled), + force_software_compositor_(force_software_compositor), layer_animator_collection_(this), scheduled_timeout_(base::TimeTicks()), allow_locks_to_extend_timeout_(false), + is_pixel_canvas_(enable_pixel_canvas), weak_ptr_factory_(this), lock_timeout_weak_ptr_factory_(this) { if (context_factory_private) { - context_factory_private->GetFrameSinkManager() - ->surface_manager() - ->RegisterFrameSinkId(frame_sink_id_); + auto* host_frame_sink_manager = + context_factory_private_->GetHostFrameSinkManager(); + host_frame_sink_manager->RegisterFrameSinkId(frame_sink_id_, this); } root_web_layer_ = cc::Layer::Create(); @@ -162,7 +171,10 @@ Compositor::Compositor(const viz::FrameSinkId& frame_sink_id, gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE; settings.disallow_non_exact_resource_reuse = - command_line->HasSwitch(cc::switches::kDisallowNonExactResourceReuse); + command_line->HasSwitch(switches::kDisallowNonExactResourceReuse); + + settings.wait_for_all_pipeline_stages_before_draw = + command_line->HasSwitch(cc::switches::kRunAllCompositorStagesBeforeDraw); base::TimeTicks before_create = base::TimeTicks::Now(); @@ -213,12 +225,14 @@ Compositor::~Compositor() { context_factory_->RemoveCompositor(this); if (context_factory_private_) { - auto* manager = context_factory_private_->GetFrameSinkManager(); + auto* host_frame_sink_manager = + context_factory_private_->GetHostFrameSinkManager(); for (auto& client : child_frame_sinks_) { DCHECK(client.is_valid()); - manager->UnregisterFrameSinkHierarchy(frame_sink_id_, client); + host_frame_sink_manager->UnregisterFrameSinkHierarchy(frame_sink_id_, + client); } - manager->surface_manager()->InvalidateFrameSinkId(frame_sink_id_); + host_frame_sink_manager->InvalidateFrameSinkId(frame_sink_id_); } } @@ -229,8 +243,8 @@ bool Compositor::IsForSubframe() { void Compositor::AddFrameSink(const viz::FrameSinkId& frame_sink_id) { if (!context_factory_private_) return; - context_factory_private_->GetFrameSinkManager()->RegisterFrameSinkHierarchy( - frame_sink_id_, frame_sink_id); + context_factory_private_->GetHostFrameSinkManager() + ->RegisterFrameSinkHierarchy(frame_sink_id_, frame_sink_id); child_frame_sinks_.insert(frame_sink_id); } @@ -240,8 +254,8 @@ void Compositor::RemoveFrameSink(const viz::FrameSinkId& frame_sink_id) { auto it = child_frame_sinks_.find(frame_sink_id); DCHECK(it != child_frame_sinks_.end()); DCHECK(it->is_valid()); - context_factory_private_->GetFrameSinkManager()->UnregisterFrameSinkHierarchy( - frame_sink_id_, *it); + context_factory_private_->GetHostFrameSinkManager() + ->UnregisterFrameSinkHierarchy(frame_sink_id_, *it); child_frame_sinks_.erase(it); } @@ -312,11 +326,13 @@ void Compositor::SetLatencyInfo(const ui::LatencyInfo& latency_info) { host_->QueueSwapPromise(std::move(swap_promise)); } -void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) { +void Compositor::SetScaleAndSize(float scale, + const gfx::Size& size_in_pixel, + const viz::LocalSurfaceId& local_surface_id) { DCHECK_GT(scale, 0); if (!size_in_pixel.IsEmpty()) { size_ = size_in_pixel; - host_->SetViewportSize(size_in_pixel); + host_->SetViewportSize(size_in_pixel, local_surface_id); root_web_layer_->SetBounds(size_in_pixel); // TODO(fsamuel): Get rid of ContextFactoryPrivate. if (context_factory_private_) @@ -325,19 +341,17 @@ void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) { if (device_scale_factor_ != scale) { device_scale_factor_ = scale; host_->SetDeviceScaleFactor(scale); + if (is_pixel_canvas()) + host_->SetRecordingScaleFactor(scale); if (root_layer_) root_layer_->OnDeviceScaleFactorChanged(scale); } } void Compositor::SetDisplayColorSpace(const gfx::ColorSpace& color_space) { - blending_color_space_ = color_space; - output_color_space_ = blending_color_space_; - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableHDR)) { - blending_color_space_ = gfx::ColorSpace::CreateExtendedSRGB(); - output_color_space_ = gfx::ColorSpace::CreateSCRGBLinear(); - } - host_->SetRasterColorSpace(color_space.GetParametricApproximation()); + output_color_space_ = color_space; + blending_color_space_ = output_color_space_.GetBlendingColorSpace(); + host_->SetRasterColorSpace(output_color_space_.GetRasterColorSpace()); // Color space is reset when the output surface is lost, so this must also be // updated then. // TODO(fsamuel): Get rid of this. @@ -392,7 +406,7 @@ void Compositor::SetDisplayVSyncParameters(base::TimeTicks timebase, } if (interval.is_zero()) { // TODO(brianderson): We should not be receiving 0 intervals. - interval = cc::BeginFrameArgs::DefaultInterval(); + interval = viz::BeginFrameArgs::DefaultInterval(); } DCHECK_GT(interval.InMillisecondsF(), 0); refresh_rate_ = @@ -433,6 +447,34 @@ scoped_refptr<CompositorVSyncManager> Compositor::vsync_manager() const { return vsync_manager_; } +void Compositor::IssueExternalBeginFrame(const viz::BeginFrameArgs& args) { + DCHECK(external_begin_frames_enabled_); + if (context_factory_private_) + context_factory_private_->IssueExternalBeginFrame(this, args); +} + +void Compositor::SetExternalBeginFrameClient(ExternalBeginFrameClient* client) { + DCHECK(external_begin_frames_enabled_); + external_begin_frame_client_ = client; + if (needs_external_begin_frames_) + external_begin_frame_client_->OnNeedsExternalBeginFrames(true); +} + +void Compositor::OnDisplayDidFinishFrame(const viz::BeginFrameAck& ack) { + DCHECK(external_begin_frames_enabled_); + if (external_begin_frame_client_) + external_begin_frame_client_->OnDisplayDidFinishFrame(ack); +} + +void Compositor::OnNeedsExternalBeginFrames(bool needs_begin_frames) { + DCHECK(external_begin_frames_enabled_); + if (external_begin_frame_client_) { + external_begin_frame_client_->OnNeedsExternalBeginFrames( + needs_begin_frames); + } + needs_external_begin_frames_ = needs_begin_frames; +} + void Compositor::AddObserver(CompositorObserver* observer) { observer_list_.AddObserver(observer); } @@ -460,7 +502,7 @@ bool Compositor::HasAnimationObserver( return animation_observer_list_.HasObserver(observer); } -void Compositor::BeginMainFrame(const cc::BeginFrameArgs& args) { +void Compositor::BeginMainFrame(const viz::BeginFrameArgs& args) { DCHECK(!IsLocked()); for (auto& observer : animation_observer_list_) observer.OnAnimationStep(args.frame_time); @@ -517,6 +559,12 @@ void Compositor::DidSubmitCompositorFrame() { observer.OnCompositingStarted(this, start_time); } +void Compositor::OnFirstSurfaceActivation( + const viz::SurfaceInfo& surface_info) { + // TODO(fsamuel): Once surface synchronization is turned on, the fallback + // surface should be set here. +} + void Compositor::SetOutputIsSecure(bool output_is_secure) { if (context_factory_private_) context_factory_private_->SetOutputIsSecure(this, output_is_secure); diff --git a/chromium/ui/compositor/compositor.h b/chromium/ui/compositor/compositor.h index 163fbb01646..a3bc8cafc57 100644 --- a/chromium/ui/compositor/compositor.h +++ b/chromium/ui/compositor/compositor.h @@ -17,10 +17,12 @@ #include "base/single_thread_task_runner.h" #include "base/time/time.h" #include "build/build_config.h" -#include "cc/output/begin_frame_args.h" #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_single_thread_client.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "components/viz/common/surfaces/local_surface_id.h" #include "components/viz/common/surfaces/surface_sequence.h" +#include "components/viz/host/host_frame_sink_client.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/compositor/compositor_animation_observer.h" #include "ui/compositor/compositor_export.h" @@ -69,6 +71,7 @@ namespace ui { class Compositor; class CompositorVSyncManager; +class ExternalBeginFrameClient; class LatencyInfo; class Layer; class Reflector; @@ -133,6 +136,8 @@ class COMPOSITOR_EXPORT ContextFactoryPrivate { virtual void SetDisplayVSyncParameters(ui::Compositor* compositor, base::TimeTicks timebase, base::TimeDelta interval) = 0; + virtual void IssueExternalBeginFrame(ui::Compositor* compositor, + const viz::BeginFrameArgs& args) = 0; virtual void SetOutputIsSecure(Compositor* compositor, bool secure) = 0; }; @@ -179,16 +184,19 @@ class COMPOSITOR_EXPORT ContextFactory { // displayable form of pixels comprising a single widget's contents. It draws an // appropriately transformed texture for each transformed view in the widget's // view hierarchy. -class COMPOSITOR_EXPORT Compositor - : NON_EXPORTED_BASE(public cc::LayerTreeHostClient), - NON_EXPORTED_BASE(public cc::LayerTreeHostSingleThreadClient), - NON_EXPORTED_BASE(public CompositorLockDelegate) { +class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, + public cc::LayerTreeHostSingleThreadClient, + public CompositorLockDelegate, + public viz::HostFrameSinkClient { public: Compositor(const viz::FrameSinkId& frame_sink_id, ui::ContextFactory* context_factory, ui::ContextFactoryPrivate* context_factory_private, scoped_refptr<base::SingleThreadTaskRunner> task_runner, - bool enable_surface_synchronization); + bool enable_surface_synchronization, + bool enable_pixel_canvas, + bool external_begin_frames_enabled = false, + bool force_software_compositor = false); ~Compositor() override; ui::ContextFactory* context_factory() { return context_factory_; } @@ -227,6 +235,11 @@ class COMPOSITOR_EXPORT Compositor // compositing layers on. float device_scale_factor() const { return device_scale_factor_; } + // The color space of the device that this compositor is being displayed on. + const gfx::ColorSpace& output_color_space() const { + return output_color_space_; + } + // Where possible, draws are scissored to a damage region calculated from // changes to layer properties. This bypasses that and indicates that // the whole frame needs to be drawn. @@ -243,7 +256,10 @@ class COMPOSITOR_EXPORT Compositor void SetLatencyInfo(const LatencyInfo& latency_info); // Sets the compositor's device scale factor and size. - void SetScaleAndSize(float scale, const gfx::Size& size_in_pixel); + void SetScaleAndSize( + float scale, + const gfx::Size& size_in_pixel, + const viz::LocalSurfaceId& local_surface_id = viz::LocalSurfaceId()); // Set the output color profile into which this compositor should render. void SetDisplayColorSpace(const gfx::ColorSpace& color_space); @@ -291,6 +307,26 @@ class COMPOSITOR_EXPORT Compositor // Returns the vsync manager for this compositor. scoped_refptr<CompositorVSyncManager> vsync_manager() const; + bool external_begin_frames_enabled() { + return external_begin_frames_enabled_; + } + + void SetExternalBeginFrameClient(ExternalBeginFrameClient* client); + + // The ExternalBeginFrameClient calls this to issue a BeginFrame with the + // given |args|. + void IssueExternalBeginFrame(const viz::BeginFrameArgs& args); + + // Called by the ContextFactory when a BeginFrame was completed by the Display + // if the enable_external_begin_frames setting is true. + void OnDisplayDidFinishFrame(const viz::BeginFrameAck& ack); + + // Called by the ContextFactory to signal whether BeginFrames are needed by + // the compositor if the enable_external_begin_frames setting is true. + void OnNeedsExternalBeginFrames(bool needs_begin_frames); + + bool force_software_compositor() { return force_software_compositor_; } + // Returns the main thread task runner this compositor uses. Users of the // compositor generally shouldn't use this. scoped_refptr<base::SingleThreadTaskRunner> task_runner() const { @@ -330,7 +366,7 @@ class COMPOSITOR_EXPORT Compositor // LayerTreeHostClient implementation. void WillBeginMainFrame() override {} void DidBeginMainFrame() override {} - void BeginMainFrame(const cc::BeginFrameArgs& args) override; + void BeginMainFrame(const viz::BeginFrameArgs& args) override; void BeginMainFrameNotExpectedSoon() override; void BeginMainFrameNotExpectedUntil(base::TimeTicks time) override; void UpdateLayerTreeHost() override; @@ -356,6 +392,9 @@ class COMPOSITOR_EXPORT Compositor void DidSubmitCompositorFrame() override; void DidLoseLayerTreeFrameSink() override {} + // viz::HostFrameSinkClient implementation. + void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override; + bool IsLocked() { return !active_locks_.empty(); } void SetOutputIsSecure(bool output_is_secure); @@ -375,6 +414,9 @@ class COMPOSITOR_EXPORT Compositor allow_locks_to_extend_timeout_ = allowed; } + // If true, all paint commands are recorded at pixel size instead of DIP. + bool is_pixel_canvas() const { return is_pixel_canvas_; } + private: friend class base::RefCounted<Compositor>; @@ -418,6 +460,12 @@ class COMPOSITOR_EXPORT Compositor // The manager of vsync parameters for this compositor. scoped_refptr<CompositorVSyncManager> vsync_manager_; + bool external_begin_frames_enabled_; + ExternalBeginFrameClient* external_begin_frame_client_ = nullptr; + bool needs_external_begin_frames_ = false; + + const bool force_software_compositor_; + // The device scale factor of the monitor that this compositor is compositing // layers on. float device_scale_factor_ = 0.f; @@ -435,6 +483,8 @@ class COMPOSITOR_EXPORT Compositor base::TimeTicks scheduled_timeout_; // If true, the |scheduled_timeout_| might be recalculated and extended. bool allow_locks_to_extend_timeout_; + // If true, all paint commands are recorded at pixel size instead of DIP. + const bool is_pixel_canvas_; base::WeakPtrFactory<Compositor> weak_ptr_factory_; base::WeakPtrFactory<Compositor> lock_timeout_weak_ptr_factory_; diff --git a/chromium/ui/compositor/compositor_constants.h b/chromium/ui/compositor/compositor_constants.h deleted file mode 100644 index ef007b0ff7f..00000000000 --- a/chromium/ui/compositor/compositor_constants.h +++ /dev/null @@ -1,18 +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_COMPOSITOR_COMPOSITOR_CONSTANTS_H_ -#define UI_COMPOSITOR_COMPOSITOR_CONSTANTS_H_ - -// This flag is used to work around a rendering bug in Windows where the -// software surface becomes inconsistent with the hardware surface. This -// happens in particular circumstances (Classic mode, Chrome using hardware -// rendering, and the window on a secondary monitor) and when moving the mouse -// over the window, Windows restores the GDI software surface underneath the -// mouse, rather than the hardware surface we've painted. As a result, for -// small or infrequently painting windows, we force back to the software -// compositor to avoid this bug. See http://crbug.com/333380. -const wchar_t kForceSoftwareCompositor[] = L"Chrome.ForceSoftwareCompositor"; - -#endif // UI_COMPOSITOR_COMPOSITOR_CONSTANTS_H_ diff --git a/chromium/ui/compositor/compositor_switches.cc b/chromium/ui/compositor/compositor_switches.cc index 991aba5e0ef..f8179b84a74 100644 --- a/chromium/ui/compositor/compositor_switches.cc +++ b/chromium/ui/compositor/compositor_switches.cc @@ -21,9 +21,6 @@ const char kEnablePixelOutputInTests[] = "enable-pixel-output-in-tests"; // maximum. const char kLimitFps[] = "limit-fps"; -// Disable partial swap which is needed for some OpenGL drivers / emulators. -const char kUIDisablePartialSwap[] = "ui-disable-partial-swap"; - const char kUIEnableRGBA4444Textures[] = "ui-enable-rgba-4444-textures"; const char kUIEnableZeroCopy[] = "ui-enable-zero-copy"; @@ -32,6 +29,11 @@ const char kUIShowPaintRects[] = "ui-show-paint-rects"; const char kUISlowAnimations[] = "ui-slow-animations"; +// If enabled, all draw commands recorded on canvas are done in pixel aligned +// measurements. This also enables scaling of all elements in views and layers +// to be done via corner points. See https://goo.gl/Dqig5s +const char kEnablePixelCanvasRecording[] = "enable-pixel-canvas-recording"; + } // namespace switches namespace ui { @@ -42,4 +44,9 @@ bool IsUIZeroCopyEnabled() { return command_line.HasSwitch(switches::kUIEnableZeroCopy); } +bool IsPixelCanvasRecordingEnabled() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnablePixelCanvasRecording); +} + } // namespace ui diff --git a/chromium/ui/compositor/compositor_switches.h b/chromium/ui/compositor/compositor_switches.h index 54fc3354217..5c03be4898b 100644 --- a/chromium/ui/compositor/compositor_switches.h +++ b/chromium/ui/compositor/compositor_switches.h @@ -12,11 +12,11 @@ namespace switches { COMPOSITOR_EXPORT extern const char kEnableHardwareOverlays[]; COMPOSITOR_EXPORT extern const char kEnablePixelOutputInTests[]; COMPOSITOR_EXPORT extern const char kLimitFps[]; -COMPOSITOR_EXPORT extern const char kUIDisablePartialSwap[]; COMPOSITOR_EXPORT extern const char kUIEnableRGBA4444Textures[]; COMPOSITOR_EXPORT extern const char kUIEnableZeroCopy[]; COMPOSITOR_EXPORT extern const char kUIShowPaintRects[]; COMPOSITOR_EXPORT extern const char kUISlowAnimations[]; +COMPOSITOR_EXPORT extern const char kEnablePixelCanvasRecording[]; } // namespace switches @@ -24,6 +24,8 @@ namespace ui { bool IsUIZeroCopyEnabled(); +bool COMPOSITOR_EXPORT IsPixelCanvasRecordingEnabled(); + } // namespace ui #endif // UI_COMPOSITOR_COMPOSITOR_SWITCHES_H_ diff --git a/chromium/ui/compositor/compositor_unittest.cc b/chromium/ui/compositor/compositor_unittest.cc index 12fa0ba4974..83878b0a424 100644 --- a/chromium/ui/compositor/compositor_unittest.cc +++ b/chromium/ui/compositor/compositor_unittest.cc @@ -9,10 +9,10 @@ #include "base/single_thread_task_runner.h" #include "base/test/test_mock_time_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "cc/output/begin_frame_args.h" -#include "cc/surfaces/surface_manager.h" -#include "cc/test/begin_frame_args_test.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" #include "components/viz/common/surfaces/local_surface_id_allocator.h" +#include "components/viz/service/surfaces/surface_manager.h" +#include "components/viz/test/begin_frame_args_test.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/compositor.h" @@ -40,7 +40,8 @@ class CompositorTest : public testing::Test { compositor_.reset(new ui::Compositor( context_factory_private->AllocateFrameSinkId(), context_factory, context_factory_private, CreateTaskRunner(), - false /* enable_surface_synchronization */)); + false /* enable_surface_synchronization */, + false /* enable_pixel_canvas */)); compositor_->SetAcceleratedWidget(gfx::kNullAcceleratedWidget); } @@ -114,8 +115,7 @@ class CompositorObserverForLocks : public CompositorObserver { bool locked_ = false; }; -class MockCompositorLockClient - : NON_EXPORTED_BASE(public ui::CompositorLockClient) { +class MockCompositorLockClient : public ui::CompositorLockClient { public: MOCK_METHOD0(CompositorLockTimedOut, void()); }; diff --git a/chromium/ui/compositor/compositor_util.cc b/chromium/ui/compositor/compositor_util.cc deleted file mode 100644 index 91b254340c0..00000000000 --- a/chromium/ui/compositor/compositor_util.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/compositor/compositor_util.h" - -#include "base/command_line.h" -#include "cc/base/switches.h" -#include "components/viz/common/display/renderer_settings.h" -#include "ui/compositor/compositor_switches.h" -#include "ui/display/display_switches.h" -#include "ui/gfx/color_space_switches.h" - -namespace ui { - -viz::RendererSettings CreateRendererSettings( - const viz::BufferToTextureTargetMap& image_targets) { - viz::RendererSettings renderer_settings; - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - renderer_settings.partial_swap_enabled = - !command_line->HasSwitch(switches::kUIDisablePartialSwap); -#if defined(OS_WIN) - renderer_settings.finish_rendering_on_resize = true; -#elif defined(OS_MACOSX) - renderer_settings.release_overlay_resources_after_gpu_query = true; -#endif - renderer_settings.gl_composited_overlay_candidate_quad_border = - command_line->HasSwitch( - cc::switches::kGlCompositedOverlayCandidateQuadBorder); - renderer_settings.show_overdraw_feedback = - command_line->HasSwitch(cc::switches::kShowOverdrawFeedback); - renderer_settings.enable_color_correct_rendering = - base::FeatureList::IsEnabled(features::kColorCorrectRendering) || - command_line->HasSwitch(switches::kEnableHDR); - renderer_settings.resource_settings.buffer_to_texture_target_map = - image_targets; - - renderer_settings.disallow_non_exact_resource_reuse = - command_line->HasSwitch(cc::switches::kDisallowNonExactResourceReuse); - renderer_settings.allow_antialiasing = - !command_line->HasSwitch(cc::switches::kDisableCompositedAntialiasing); - - return renderer_settings; -} - -} // namespace ui diff --git a/chromium/ui/compositor/compositor_util.h b/chromium/ui/compositor/compositor_util.h deleted file mode 100644 index 1939514833f..00000000000 --- a/chromium/ui/compositor/compositor_util.h +++ /dev/null @@ -1,24 +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_COMPOSITOR_COMPOSITOR_UTIL_H_ -#define UI_COMPOSITOR_COMPOSITOR_UTIL_H_ - -#include <stdint.h> - -#include "components/viz/common/resources/buffer_to_texture_target_map.h" -#include "ui/compositor/compositor_export.h" - -namespace viz { -class RendererSettings; -} - -namespace ui { - -COMPOSITOR_EXPORT viz::RendererSettings CreateRendererSettings( - const viz::BufferToTextureTargetMap& image_targets); - -} // namespace ui - -#endif // UI_COMPOSITOR_COMPOSITOR_UTIL_H_ diff --git a/chromium/ui/compositor/external_begin_frame_client.h b/chromium/ui/compositor/external_begin_frame_client.h new file mode 100644 index 00000000000..764c901df30 --- /dev/null +++ b/chromium/ui/compositor/external_begin_frame_client.h @@ -0,0 +1,23 @@ +// Copyright (c) 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_COMPOSITOR_EXTERNAL_BEGIN_FRAME_CLIENT_H_ +#define UI_COMPOSITOR_EXTERNAL_BEGIN_FRAME_CLIENT_H_ + +#include "components/viz/common/frame_sinks/begin_frame_args.h" +#include "ui/compositor/compositor_export.h" + +namespace ui { + +class COMPOSITOR_EXPORT ExternalBeginFrameClient { + public: + virtual ~ExternalBeginFrameClient() {} + + virtual void OnDisplayDidFinishFrame(const viz::BeginFrameAck& ack) = 0; + virtual void OnNeedsExternalBeginFrames(bool needs_begin_frames) = 0; +}; + +} // namespace ui + +#endif // UI_COMPOSITOR_EXTERNAL_BEGIN_FRAME_CLIENT_H_ diff --git a/chromium/ui/compositor/layer.cc b/chromium/ui/compositor/layer.cc index d2248572c2a..72ccf5e861c 100644 --- a/chromium/ui/compositor/layer.cc +++ b/chromium/ui/compositor/layer.cc @@ -21,9 +21,9 @@ #include "cc/layers/solid_color_layer.h" #include "cc/layers/surface_layer.h" #include "cc/layers/texture_layer.h" -#include "cc/output/copy_output_request.h" -#include "cc/resources/transferable_resource.h" #include "cc/trees/layer_tree_settings.h" +#include "components/viz/common/quads/copy_output_request.h" +#include "components/viz/common/resources/transferable_resource.h" #include "ui/compositor/compositor_switches.h" #include "ui/compositor/dip_util.h" #include "ui/compositor/layer_animator.h" @@ -40,6 +40,10 @@ namespace { const ui::Layer* GetRoot(const ui::Layer* layer) { + // Parent walk cannot be done on a layer that is being used as a mask. Get the + // layer to which this layer is a mask of. + if (layer->layer_mask_back_link()) + layer = layer->layer_mask_back_link(); while (layer->parent()) layer = layer->parent(); return layer; @@ -108,7 +112,8 @@ Layer::Layer() delegate_(NULL), owner_(NULL), cc_layer_(NULL), - device_scale_factor_(1.0f) { + device_scale_factor_(1.0f), + cache_render_surface_requests_(0) { CreateCcLayer(); } @@ -135,7 +140,8 @@ Layer::Layer(LayerType type) delegate_(NULL), owner_(NULL), cc_layer_(NULL), - device_scale_factor_(1.0f) { + device_scale_factor_(1.0f), + cache_render_surface_requests_(0) { CreateCcLayer(); } @@ -178,7 +184,7 @@ std::unique_ptr<Layer> Layer::Clone() const { clone->SetLayerInverted(layer_inverted_); clone->SetLayerBlur(layer_blur_sigma_); if (alpha_shape_) - clone->SetAlphaShape(base::MakeUnique<SkRegion>(*alpha_shape_)); + clone->SetAlphaShape(base::MakeUnique<ShapeRects>(*alpha_shape_)); // cc::Layer state. if (surface_layer_) { @@ -315,7 +321,7 @@ void Layer::SetAnimator(LayerAnimator* animator) { Compositor* compositor = GetCompositor(); if (animator_) { - if (compositor) + if (compositor && !layer_mask_back_link()) animator_->DetachLayerAndTimeline(compositor); animator_->SetDelegate(nullptr); } @@ -324,7 +330,7 @@ void Layer::SetAnimator(LayerAnimator* animator) { if (animator_) { animator_->SetDelegate(this); - if (compositor) + if (compositor && !layer_mask_back_link()) animator_->AttachLayerAndTimeline(compositor); } } @@ -445,14 +451,13 @@ void Layer::SetLayerInverted(bool inverted) { } void Layer::SetMaskLayer(Layer* layer_mask) { + if (layer_mask_ == layer_mask) + return; // The provided mask should not have a layer mask itself. DCHECK(!layer_mask || - (!layer_mask->layer_mask_layer() && - layer_mask->children().empty() && + (!layer_mask->layer_mask_layer() && layer_mask->children().empty() && !layer_mask->layer_mask_back_link_)); DCHECK(!layer_mask_back_link_); - if (layer_mask_ == layer_mask) - return; // We need to de-reference the currently linked object so that no problem // arises if the mask layer gets deleted before this object. if (layer_mask_) @@ -474,8 +479,8 @@ void Layer::SetBackgroundZoom(float zoom, int inset) { SetLayerBackgroundFilters(); } -void Layer::SetAlphaShape(std::unique_ptr<SkRegion> region) { - alpha_shape_ = std::move(region); +void Layer::SetAlphaShape(std::unique_ptr<ShapeRects> shape) { + alpha_shape_ = std::move(shape); SetLayerFilters(); } @@ -661,13 +666,27 @@ void Layer::SwitchCCLayerForTest() { // which could be a supprise. But we want to preserve it after switching to a // new cc::Layer. There could be a whole subtree and the root changed, but does // not mean we want to treat the cache all different. -void Layer::SetCacheRenderSurface(bool cache_render_surface) { - cc_layer_->SetCacheRenderSurface(cache_render_surface); +void Layer::AddCacheRenderSurfaceRequest() { + ++cache_render_surface_requests_; + TRACE_COUNTER_ID1("ui", "CacheRenderSurfaceRequests", this, + cache_render_surface_requests_); + if (cache_render_surface_requests_ == 1) + cc_layer_->SetCacheRenderSurface(true); +} + +void Layer::RemoveCacheRenderSurfaceRequest() { + DCHECK_GT(cache_render_surface_requests_, 0u); + + --cache_render_surface_requests_; + TRACE_COUNTER_ID1("ui", "CacheRenderSurfaceRequests", this, + cache_render_surface_requests_); + if (cache_render_surface_requests_ == 0) + cc_layer_->SetCacheRenderSurface(false); } void Layer::SetTextureMailbox( const viz::TextureMailbox& mailbox, - std::unique_ptr<cc::SingleReleaseCallback> release_callback, + std::unique_ptr<viz::SingleReleaseCallback> release_callback, gfx::Size texture_size_in_dip) { DCHECK(type_ == LAYER_TEXTURED || type_ == LAYER_SOLID_COLOR); DCHECK(mailbox.IsValid()); @@ -741,6 +760,12 @@ void Layer::SetFallbackSurface(const viz::SurfaceInfo& surface_info) { mirror->dest()->SetFallbackSurface(surface_info); } +const viz::SurfaceInfo* Layer::GetPrimarySurfaceInfo() const { + if (surface_layer_) + return &surface_layer_->primary_surface_info(); + return nullptr; +} + const viz::SurfaceInfo* Layer::GetFallbackSurfaceInfo() const { if (surface_layer_) return &surface_layer_->fallback_surface_info(); @@ -896,7 +921,8 @@ void Layer::OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) { } void Layer::SetDidScrollCallback( - base::Callback<void(const gfx::ScrollOffset&)> callback) { + base::Callback<void(const gfx::ScrollOffset&, const cc::ElementId&)> + callback) { cc_layer_->set_did_scroll_callback(std::move(callback)); } @@ -927,7 +953,7 @@ void Layer::SetScrollOffset(const gfx::ScrollOffset& offset) { } void Layer::RequestCopyOfOutput( - std::unique_ptr<cc::CopyOutputRequest> request) { + std::unique_ptr<viz::CopyOutputRequest> request) { cc_layer_->RequestCopyOfOutput(std::move(request)); } @@ -944,8 +970,9 @@ scoped_refptr<cc::DisplayItemList> Layer::PaintContentsToDisplayList( paint_region_.Clear(); auto display_list = make_scoped_refptr(new cc::DisplayItemList); if (delegate_) { - delegate_->OnPaintLayer( - PaintContext(display_list.get(), device_scale_factor_, invalidation)); + delegate_->OnPaintLayer(PaintContext(display_list.get(), + device_scale_factor_, invalidation, + GetCompositor()->is_pixel_canvas())); } display_list->Finalize(); // TODO(domlaskowski): Move mirror invalidation to Layer::SchedulePaint. @@ -964,7 +991,7 @@ size_t Layer::GetApproximateUnsharedMemoryUsage() const { bool Layer::PrepareTextureMailbox( viz::TextureMailbox* mailbox, - std::unique_ptr<cc::SingleReleaseCallback>* release_callback) { + std::unique_ptr<viz::SingleReleaseCallback>* release_callback) { if (!mailbox_release_callback_) return false; *mailbox = mailbox_; @@ -1181,6 +1208,10 @@ float Layer::GetRefreshRate() const { return compositor ? compositor->refresh_rate() : 60.0; } +ui::Layer* Layer::GetLayer() { + return this; +} + cc::Layer* Layer::GetCcLayer() const { return cc_layer_; } diff --git a/chromium/ui/compositor/layer.h b/chromium/ui/compositor/layer.h index e89acf51120..869c00c6f91 100644 --- a/chromium/ui/compositor/layer.h +++ b/chromium/ui/compositor/layer.h @@ -24,7 +24,6 @@ #include "components/viz/common/quads/texture_mailbox.h" #include "components/viz/common/surfaces/sequence_surface_reference_factory.h" #include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkRegion.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer_animation_delegate.h" #include "ui/compositor/layer_delegate.h" @@ -34,7 +33,6 @@ #include "ui/gfx/transform.h" namespace cc { -class CopyOutputRequest; class Layer; class NinePatchLayer; class SolidColorLayer; @@ -42,6 +40,10 @@ class SurfaceLayer; class TextureLayer; } +namespace viz { +class CopyOutputRequest; +} + namespace ui { class Compositor; @@ -60,12 +62,12 @@ class LayerThreadedAnimationDelegate; // NOTE: Unlike Views, each Layer does *not* own its child Layers. If you // delete a Layer and it has children, the parent of each child Layer is set to // NULL, but the children are not deleted. -class COMPOSITOR_EXPORT Layer - : public LayerAnimationDelegate, - NON_EXPORTED_BASE(public cc::ContentLayerClient), - NON_EXPORTED_BASE(public cc::TextureLayerClient), - NON_EXPORTED_BASE(public cc::LayerClient) { +class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, + public cc::ContentLayerClient, + public cc::TextureLayerClient, + public cc::LayerClient { public: + using ShapeRects = std::vector<gfx::Rect>; Layer(); explicit Layer(LayerType type); ~Layer() override; @@ -233,8 +235,8 @@ class COMPOSITOR_EXPORT Layer void SetBackgroundZoom(float zoom, int inset); // Set the shape of this layer. - SkRegion* alpha_shape() const { return alpha_shape_.get(); } - void SetAlphaShape(std::unique_ptr<SkRegion> region); + const ShapeRects* alpha_shape() const { return alpha_shape_.get(); } + void SetAlphaShape(std::unique_ptr<ShapeRects> shape); // Invert the layer. bool layer_inverted() const { return layer_inverted_; } @@ -299,7 +301,7 @@ class COMPOSITOR_EXPORT Layer // shared memory resource or an actual mailbox for a texture. void SetTextureMailbox( const viz::TextureMailbox& mailbox, - std::unique_ptr<cc::SingleReleaseCallback> release_callback, + std::unique_ptr<viz::SingleReleaseCallback> release_callback, gfx::Size texture_size_in_dip); void SetTextureSize(gfx::Size texture_size_in_dip); void SetTextureFlipped(bool flipped); @@ -314,6 +316,9 @@ class COMPOSITOR_EXPORT Layer // display compositor, the fallback surface will be used. void SetFallbackSurface(const viz::SurfaceInfo& surface_info); + // Returns the primary SurfaceInfo set by SetShowPrimarySurface. + const viz::SurfaceInfo* GetPrimarySurfaceInfo() const; + // Returns the fallback SurfaceInfo set by SetFallbackSurface. const viz::SurfaceInfo* GetFallbackSurfaceInfo() const; @@ -366,12 +371,15 @@ class COMPOSITOR_EXPORT Layer void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip); // Requets a copy of the layer's output as a texture or bitmap. - void RequestCopyOfOutput(std::unique_ptr<cc::CopyOutputRequest> request); + void RequestCopyOfOutput(std::unique_ptr<viz::CopyOutputRequest> request); // Invoked when scrolling performed by the cc::InputHandler is committed. This // will only occur if the Layer has set scroll container bounds. void SetDidScrollCallback( - base::Callback<void(const gfx::ScrollOffset&)> callback); + base::Callback<void(const gfx::ScrollOffset&, const cc::ElementId&)> + callback); + + cc::ElementId element_id() const { return cc_layer_->element_id(); } // Marks this layer as scrollable inside the provided bounds. This size only // affects scrolling so if clipping is desired, a separate clipping layer @@ -394,7 +402,7 @@ class COMPOSITOR_EXPORT Layer // TextureLayerClient bool PrepareTextureMailbox( viz::TextureMailbox* mailbox, - std::unique_ptr<cc::SingleReleaseCallback>* release_callback) override; + std::unique_ptr<viz::SingleReleaseCallback>* release_callback) override; float device_scale_factor() const { return device_scale_factor_; } @@ -419,7 +427,13 @@ class COMPOSITOR_EXPORT Layer // occlusion culling in favor of efficient caching. This should // only be used when paying the cost of creating a render // surface even if layer is invisible is not a problem. - void SetCacheRenderSurface(bool cache_render_surface); + void AddCacheRenderSurfaceRequest(); + void RemoveCacheRenderSurfaceRequest(); + + // The back link from the mask layer to it's associated masked layer. + // We keep this reference for the case that if the mask layer gets deleted + // while attached to the main layer before the main layer is deleted. + const Layer* layer_mask_back_link() const { return layer_mask_back_link_; } private: friend class LayerOwner; @@ -453,6 +467,7 @@ class COMPOSITOR_EXPORT Layer SkColor GetColorForAnimation() const override; float GetTemperatureFromAnimation() const override; float GetDeviceScaleFactor() const override; + ui::Layer* GetLayer() override; cc::Layer* GetCcLayer() const override; LayerThreadedAnimationDelegate* GetThreadedAnimationDelegate() override; LayerAnimatorCollection* GetLayerAnimatorCollection() override; @@ -546,7 +561,7 @@ class COMPOSITOR_EXPORT Layer int zoom_inset_; // Shape of the window. - std::unique_ptr<SkRegion> alpha_shape_; + std::unique_ptr<ShapeRects> alpha_shape_; std::string name_; @@ -580,12 +595,17 @@ class COMPOSITOR_EXPORT Layer // The callback to release the mailbox. This is only set after // SetTextureMailbox is called, before we give it to the TextureLayer. - std::unique_ptr<cc::SingleReleaseCallback> mailbox_release_callback_; + std::unique_ptr<viz::SingleReleaseCallback> mailbox_release_callback_; // The size of the frame or texture in DIP, set when SetShowDelegatedContent // or SetTextureMailbox was called. gfx::Size frame_size_in_dip_; + // The counter to maintain how many cache render surface requests we have. If + // the value > 0, means we need to cache the render surface. If the value + // == 0, means we should not cache the render surface. + unsigned cache_render_surface_requests_; + DISALLOW_COPY_AND_ASSIGN(Layer); }; diff --git a/chromium/ui/compositor/layer_animation_delegate.h b/chromium/ui/compositor/layer_animation_delegate.h index 827647be7a4..45bf8bad926 100644 --- a/chromium/ui/compositor/layer_animation_delegate.h +++ b/chromium/ui/compositor/layer_animation_delegate.h @@ -16,6 +16,7 @@ class Layer; namespace ui { +class Layer; class LayerAnimatorCollection; class LayerThreadedAnimationDelegate; @@ -40,6 +41,7 @@ class COMPOSITOR_EXPORT LayerAnimationDelegate { virtual SkColor GetColorForAnimation() const = 0; virtual float GetTemperatureFromAnimation() const = 0; virtual float GetDeviceScaleFactor() const = 0; + virtual ui::Layer* GetLayer() = 0; virtual cc::Layer* GetCcLayer() const = 0; virtual LayerAnimatorCollection* GetLayerAnimatorCollection() = 0; virtual LayerThreadedAnimationDelegate* GetThreadedAnimationDelegate() = 0; diff --git a/chromium/ui/compositor/layer_animation_element.cc b/chromium/ui/compositor/layer_animation_element.cc index 9f6797fa9cd..82cdb89f5d6 100644 --- a/chromium/ui/compositor/layer_animation_element.cc +++ b/chromium/ui/compositor/layer_animation_element.cc @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/strings/stringprintf.h" #include "cc/animation/animation.h" #include "cc/animation/animation_id_provider.h" #include "ui/compositor/float_animation_curve_adapter.h" @@ -39,6 +40,7 @@ class Pause : public LayerAnimationElement { ~Pause() override {} private: + std::string DebugName() const override { return "Pause"; } void OnStart(LayerAnimationDelegate* delegate) override {} bool OnProgress(double t, LayerAnimationDelegate* delegate) override { return false; @@ -61,6 +63,9 @@ class InterpolatedTransformTransition : public LayerAnimationElement { ~InterpolatedTransformTransition() override {} protected: + std::string DebugName() const override { + return "InterpolatedTransformTransition"; + } void OnStart(LayerAnimationDelegate* delegate) override {} bool OnProgress(double t, LayerAnimationDelegate* delegate) override { @@ -92,6 +97,7 @@ class BoundsTransition : public LayerAnimationElement { ~BoundsTransition() override {} protected: + std::string DebugName() const override { return "BoundsTransition"; } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetBoundsForAnimation(); } @@ -127,6 +133,7 @@ class VisibilityTransition : public LayerAnimationElement { ~VisibilityTransition() override {} protected: + std::string DebugName() const override { return "VisibilityTransition"; } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetVisibilityForAnimation(); } @@ -161,6 +168,7 @@ class BrightnessTransition : public LayerAnimationElement { ~BrightnessTransition() override {} protected: + std::string DebugName() const override { return "BrightnessTransition"; } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetBrightnessForAnimation(); } @@ -196,6 +204,7 @@ class GrayscaleTransition : public LayerAnimationElement { ~GrayscaleTransition() override {} protected: + std::string DebugName() const override { return "GrayscaleTransition"; } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetGrayscaleForAnimation(); } @@ -231,6 +240,7 @@ class ColorTransition : public LayerAnimationElement { ~ColorTransition() override {} protected: + std::string DebugName() const override { return "ColorTransition"; } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetColorForAnimation(); } @@ -265,6 +275,7 @@ class TemperatureTransition : public LayerAnimationElement { ~TemperatureTransition() override {} protected: + std::string DebugName() const override { return "TemperatureTransition"; } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetTemperatureFromAnimation(); } @@ -304,6 +315,9 @@ class ThreadedLayerAnimationElement : public LayerAnimationElement { explicit ThreadedLayerAnimationElement(const LayerAnimationElement& element) : LayerAnimationElement(element) { } + std::string DebugName() const override { + return "ThreadedLayerAnimationElement"; + } bool OnProgress(double t, LayerAnimationDelegate* delegate) override { if (t < 1.0) @@ -365,6 +379,7 @@ class ThreadedOpacityTransition : public ThreadedLayerAnimationElement { ~ThreadedOpacityTransition() override {} protected: + std::string DebugName() const override { return "ThreadedOpacityTransition"; } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetOpacityForAnimation(); } @@ -416,6 +431,9 @@ class ThreadedTransformTransition : public ThreadedLayerAnimationElement { ~ThreadedTransformTransition() override {} protected: + std::string DebugName() const override { + return "ThreadedTransformTransition"; + } void OnStart(LayerAnimationDelegate* delegate) override { start_ = delegate->GetTransformForAnimation(); } @@ -621,6 +639,21 @@ void LayerAnimationElement::RequestEffectiveStart( effective_start_time_ = requested_start_time_; } +std::string LayerAnimationElement::ToString() const { + // TODO(wkorman): Add support for subclasses to tack on more info + // beyond just their name. + return base::StringPrintf( + "LayerAnimationElement{name=%s, id=%d, group=%d, " + "last_progressed_fraction=%0.2f, " + "start_frame_number=%d}", + DebugName().c_str(), animation_id_, animation_group_id_, + last_progressed_fraction_, start_frame_number_); +} + +std::string LayerAnimationElement::DebugName() const { + return "Default"; +} + // static LayerAnimationElement::AnimatableProperty LayerAnimationElement::ToAnimatableProperty(cc::TargetProperty::Type property) { @@ -636,6 +669,57 @@ LayerAnimationElement::ToAnimatableProperty(cc::TargetProperty::Type property) { } // static +std::string LayerAnimationElement::AnimatablePropertiesToString( + AnimatableProperties properties) { + std::string str; + int property_count = 0; + for (unsigned i = FIRST_PROPERTY; i != SENTINEL; i = i << 1) { + if (i & properties) { + LayerAnimationElement::AnimatableProperty property = + static_cast<LayerAnimationElement::AnimatableProperty>(i); + if (property_count > 0) + str.append("|"); + // TODO(wkorman): Consider reworking enum definition to follow + // #define pattern that includes easier string output. + switch (property) { + case UNKNOWN: + str.append("UNKNOWN"); + break; + case TRANSFORM: + str.append("TRANSFORM"); + break; + case BOUNDS: + str.append("BOUNDS"); + break; + case OPACITY: + str.append("OPACITY"); + break; + case VISIBILITY: + str.append("VISIBILITY"); + break; + case BRIGHTNESS: + str.append("BRIGHTNESS"); + break; + case GRAYSCALE: + str.append("GRAYSCALE"); + break; + case COLOR: + str.append("COLOR"); + break; + case TEMPERATURE: + str.append("TEMPERATURE"); + break; + case SENTINEL: + NOTREACHED(); + break; + } + property_count++; + } + } + return str; +} + +// static base::TimeDelta LayerAnimationElement::GetEffectiveDuration( const base::TimeDelta& duration) { switch (ScopedAnimationDurationScaleMode::duration_scale_mode()) { diff --git a/chromium/ui/compositor/layer_animation_element.h b/chromium/ui/compositor/layer_animation_element.h index d4f248dece5..9a11b97cc5e 100644 --- a/chromium/ui/compositor/layer_animation_element.h +++ b/chromium/ui/compositor/layer_animation_element.h @@ -14,6 +14,7 @@ #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "cc/animation/animation.h" +#include "cc/trees/target_property.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/compositor/compositor_export.h" #include "ui/gfx/animation/tween.h" @@ -77,6 +78,9 @@ class COMPOSITOR_EXPORT LayerAnimationElement { virtual ~LayerAnimationElement(); + static std::string AnimatablePropertiesToString( + AnimatableProperties properties); + // Creates an element that transitions to the given transform. The caller owns // the return value. static std::unique_ptr<LayerAnimationElement> CreateTransformElement( @@ -213,7 +217,11 @@ class COMPOSITOR_EXPORT LayerAnimationElement { // call made to {Progress, ProgressToEnd}. double last_progressed_fraction() const { return last_progressed_fraction_; } + std::string ToString() const; + protected: + virtual std::string DebugName() const; + // Called once each time the animation element is run before any call to // OnProgress. virtual void OnStart(LayerAnimationDelegate* delegate) = 0; diff --git a/chromium/ui/compositor/layer_animation_element_unittest.cc b/chromium/ui/compositor/layer_animation_element_unittest.cc index 93714e505e8..6baad140ab6 100644 --- a/chromium/ui/compositor/layer_animation_element_unittest.cc +++ b/chromium/ui/compositor/layer_animation_element_unittest.cc @@ -400,6 +400,20 @@ TEST(LayerAnimationElementTest, AbortTransformElement) { delegate.GetTransformForAnimation()); } +TEST(LayerAnimationElementTest, ToString) { + float target = 1.0; + base::TimeDelta delta = base::TimeDelta::FromSeconds(1); + std::unique_ptr<LayerAnimationElement> element = + LayerAnimationElement::CreateOpacityElement(target, delta); + element->set_animation_group_id(42); + // TODO(wkorman): Test varying last_progressed_fraction and + // start_frame_number. + EXPECT_EQ( + "LayerAnimationElement{name=ThreadedOpacityTransition, id=1, group=42, " + "last_progressed_fraction=0.00, start_frame_number=0}", + element->ToString()); +} + } // namespace } // namespace ui diff --git a/chromium/ui/compositor/layer_animation_sequence.cc b/chromium/ui/compositor/layer_animation_sequence.cc index f13db19d1d1..a7a7b1c596e 100644 --- a/chromium/ui/compositor/layer_animation_sequence.cc +++ b/chromium/ui/compositor/layer_animation_sequence.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <iterator> +#include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "cc/animation/animation_id_provider.h" #include "ui/compositor/layer_animation_delegate.h" @@ -290,4 +291,23 @@ LayerAnimationElement* LayerAnimationSequence::CurrentElement() const { return elements_[current_index].get(); } +std::string LayerAnimationSequence::ElementsToString() const { + std::string str; + for (size_t i = 0; i < elements_.size(); i++) { + if (i > 0) + str.append(", "); + str.append(elements_[i]->ToString()); + } + return str; +} + +std::string LayerAnimationSequence::ToString() const { + return base::StringPrintf( + "LayerAnimationSequence{size=%zu, properties=%s, " + "elements=[%s], is_cyclic=%d, group_id=%d}", + size(), + LayerAnimationElement::AnimatablePropertiesToString(properties_).c_str(), + ElementsToString().c_str(), is_cyclic_, animation_group_id_); +} + } // namespace ui diff --git a/chromium/ui/compositor/layer_animation_sequence.h b/chromium/ui/compositor/layer_animation_sequence.h index 379ff54a020..cee0500a7f0 100644 --- a/chromium/ui/compositor/layer_animation_sequence.h +++ b/chromium/ui/compositor/layer_animation_sequence.h @@ -138,6 +138,8 @@ class COMPOSITOR_EXPORT LayerAnimationSequence LayerAnimationElement* FirstElement() const; + std::string ToString() const; + private: friend class LayerAnimatorTestController; @@ -146,6 +148,8 @@ class COMPOSITOR_EXPORT LayerAnimationSequence FRIEND_TEST_ALL_PREFIXES(LayerAnimatorTest, ObserverReleasedBeforeAnimationSequenceEnds); + std::string ElementsToString() const; + // Notifies the observers that this sequence has been scheduled. void NotifyScheduled(); diff --git a/chromium/ui/compositor/layer_animation_sequence_unittest.cc b/chromium/ui/compositor/layer_animation_sequence_unittest.cc index 9222046fb28..3ce4001408c 100644 --- a/chromium/ui/compositor/layer_animation_sequence_unittest.cc +++ b/chromium/ui/compositor/layer_animation_sequence_unittest.cc @@ -7,6 +7,7 @@ #include <memory> #include "base/compiler_specific.h" +#include "base/strings/stringprintf.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/layer_animation_delegate.h" @@ -268,6 +269,46 @@ TEST(LayerAnimationSequenceTest, AddObserver) { } } +TEST(LayerAnimationSequenceTest, ToString) { + base::TimeDelta delta = base::TimeDelta::FromSeconds(1); + LayerAnimationSequence sequence; + EXPECT_EQ( + "LayerAnimationSequence{size=0, properties=, elements=[], is_cyclic=0, " + "group_id=0}", + sequence.ToString()); + + std::unique_ptr<LayerAnimationElement> brightness = + LayerAnimationElement::CreateBrightnessElement(1.0f, delta); + int brightness_id = brightness->animation_id(); + sequence.AddElement(std::move(brightness)); + EXPECT_EQ( + base::StringPrintf( + "LayerAnimationSequence{size=1, properties=BRIGHTNESS, " + "elements=[LayerAnimationElement{name=BrightnessTransition, id=%d, " + "group=0, last_progressed_fraction=0.00, start_frame_number=0}], " + "is_cyclic=0, group_id=0}", + brightness_id), + sequence.ToString()); + + std::unique_ptr<LayerAnimationElement> opacity = + LayerAnimationElement::CreateOpacityElement(1.0f, delta); + int opacity_id = opacity->animation_id(); + sequence.AddElement(std::move(opacity)); + sequence.set_is_cyclic(true); + sequence.set_animation_group_id(1973); + EXPECT_EQ( + base::StringPrintf( + "LayerAnimationSequence{size=2, properties=OPACITY|BRIGHTNESS, " + "elements=[LayerAnimationElement{name=BrightnessTransition, id=%d, " + "group=0, last_progressed_fraction=0.00, start_frame_number=0}, " + "LayerAnimationElement{name=ThreadedOpacityTransition, id=%d, " + "group=0, " + "last_progressed_fraction=0.00, start_frame_number=0}], is_cyclic=1, " + "group_id=1973}", + brightness_id, opacity_id), + sequence.ToString()); +} + } // namespace } // namespace ui diff --git a/chromium/ui/compositor/layer_animator.cc b/chromium/ui/compositor/layer_animator.cc index 09e5b17f16c..5902c14a039 100644 --- a/chromium/ui/compositor/layer_animator.cc +++ b/chromium/ui/compositor/layer_animator.cc @@ -15,7 +15,7 @@ #include "cc/animation/animation_player.h" #include "cc/animation/animation_timeline.h" #include "cc/animation/element_animations.h" -#include "cc/output/begin_frame_args.h" +#include "components/viz/common/frame_sinks/begin_frame_args.h" #include "ui/compositor/compositor.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animation_delegate.h" @@ -919,11 +919,12 @@ LayerAnimatorCollection* LayerAnimator::GetLayerAnimatorCollection() { return delegate_ ? delegate_->GetLayerAnimatorCollection() : NULL; } -void LayerAnimator::NotifyAnimationStarted( - base::TimeTicks monotonic_time, - cc::TargetProperty::Type target_property, - int group) { - OnThreadedAnimationStarted(monotonic_time, target_property, group); +void LayerAnimator::NotifyAnimationStarted(base::TimeTicks monotonic_time, + int target_property, + int group) { + OnThreadedAnimationStarted( + monotonic_time, static_cast<cc::TargetProperty::Type>(target_property), + group); } LayerAnimator::RunningAnimation::RunningAnimation( diff --git a/chromium/ui/compositor/layer_animator.h b/chromium/ui/compositor/layer_animator.h index 19dc61dad58..a86864cb315 100644 --- a/chromium/ui/compositor/layer_animator.h +++ b/chromium/ui/compositor/layer_animator.h @@ -53,10 +53,9 @@ class ScopedLayerAnimationSettings; // ensure that it is not disposed of until it finishes executing. It does this // by holding a reference to itself for the duration of methods for which it // must guarantee that |this| is valid. -class COMPOSITOR_EXPORT LayerAnimator - : public base::RefCounted<LayerAnimator>, - public LayerThreadedAnimationDelegate, - NON_EXPORTED_BASE(public cc::AnimationDelegate) { +class COMPOSITOR_EXPORT LayerAnimator : public base::RefCounted<LayerAnimator>, + public LayerThreadedAnimationDelegate, + public cc::AnimationDelegate { public: enum PreemptionStrategy { IMMEDIATELY_SET_NEW_TARGET, @@ -345,18 +344,18 @@ class COMPOSITOR_EXPORT LayerAnimator // cc::AnimationDelegate implementation. void NotifyAnimationStarted(base::TimeTicks monotonic_time, - cc::TargetProperty::Type target_property, + int target_property, int group_id) override; void NotifyAnimationFinished(base::TimeTicks monotonic_time, - cc::TargetProperty::Type target_property, + int target_property, int group_id) override {} void NotifyAnimationAborted(base::TimeTicks monotonic_time, - cc::TargetProperty::Type target_property, + int target_property, int group_id) override {} void NotifyAnimationTakeover( base::TimeTicks monotonic_time, - cc::TargetProperty::Type target_property, - double animation_start_time, + int target_property, + base::TimeTicks animation_start_time, std::unique_ptr<cc::AnimationCurve> curve) override {} // Implementation of LayerThreadedAnimationDelegate. diff --git a/chromium/ui/compositor/layer_animator_unittest.cc b/chromium/ui/compositor/layer_animator_unittest.cc index 4b9575b4ba2..3e09d10b574 100644 --- a/chromium/ui/compositor/layer_animator_unittest.cc +++ b/chromium/ui/compositor/layer_animator_unittest.cc @@ -1694,6 +1694,178 @@ TEST(LayerAnimatorTest, ImplicitAnimationObservers) { EXPECT_FLOAT_EQ(0.0f, delegate.GetBrightnessForAnimation()); } +// Tests that caching render surface added to a scoped settings object is still +// reset when the object goes out of scope. +TEST(LayerAnimatorTest, CacheRenderSurface) { + ui::Layer layer; + scoped_refptr<LayerAnimator> animator(layer.GetAnimator()); + animator->set_disable_timer_for_test(true); + TestImplicitAnimationObserver observer(false); + + EXPECT_FALSE(observer.animations_completed()); + EXPECT_FALSE(layer.cc_layer_for_testing()->cache_render_surface()); + animator->SetOpacity(1.0f); + + { + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + settings.AddObserver(&observer); + animator->SetOpacity(0.0f); + } + + EXPECT_FALSE(observer.animations_completed()); + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + animator->StopAnimatingProperty(LayerAnimationElement::OPACITY); + EXPECT_TRUE(observer.animations_completed()); + EXPECT_TRUE(observer.WasAnimationCompletedForProperty( + LayerAnimationElement::OPACITY)); + EXPECT_FLOAT_EQ(0.0f, layer.opacity()); + EXPECT_FALSE(layer.cc_layer_for_testing()->cache_render_surface()); +} + +// Tests that caching render surface added to a scoped settings object will not +// crash when the layer was destroyed. +TEST(LayerAnimatorTest, CacheRenderSurfaceOnWillBeDestroyedLayer) { + // Case 1: layer is a pointer. + ui::Layer* layer1 = new Layer(); + scoped_refptr<LayerAnimator> animator(layer1->GetAnimator()); + animator->set_disable_timer_for_test(true); + TestImplicitAnimationObserver observer1(false); + + EXPECT_FALSE(observer1.animations_completed()); + animator->SetOpacity(1.0f); + + { + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + settings.AddObserver(&observer1); + animator->SetOpacity(0.0f); + } + + EXPECT_FALSE(observer1.animations_completed()); + delete layer1; + layer1 = nullptr; + animator->StopAnimatingProperty(LayerAnimationElement::OPACITY); + EXPECT_TRUE(observer1.animations_completed()); + + // Case 2: layer is a local variable. + TestImplicitAnimationObserver observer2(false); + { + ui::Layer layer2; + animator = layer2.GetAnimator(); + + EXPECT_FALSE(observer2.animations_completed()); + animator->SetBrightness(1.0f); + + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + settings.AddObserver(&observer2); + animator->SetBrightness(0.0f); + } + + EXPECT_FALSE(observer2.animations_completed()); + animator->StopAnimatingProperty(LayerAnimationElement::BRIGHTNESS); + EXPECT_TRUE(observer2.animations_completed()); + + // Case 3: Animation finishes before layer is destroyed. + ui::Layer layer3; + animator = layer3.GetAnimator(); + TestImplicitAnimationObserver observer3(false); + + EXPECT_FALSE(observer3.animations_completed()); + animator->SetGrayscale(1.0f); + + { + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + settings.AddObserver(&observer3); + animator->SetGrayscale(0.0f); + } + + EXPECT_FALSE(observer3.animations_completed()); + animator->StopAnimatingProperty(LayerAnimationElement::GRAYSCALE); + EXPECT_TRUE(observer3.animations_completed()); +} + +// Tests that caching render surface added to two scoped settings objects is +// still reset when animation finishes. +TEST(LayerAnimatorTest, CacheRenderSurfaceInTwoAnimations) { + ui::Layer layer; + scoped_refptr<LayerAnimator> animator(layer.GetAnimator()); + animator->set_disable_timer_for_test(true); + + // Case 1: the original cache status if false. + EXPECT_FALSE(layer.cc_layer_for_testing()->cache_render_surface()); + animator->SetBrightness(1.0f); + animator->SetOpacity(1.0f); + + // Start the brightness animation. + { + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + animator->SetBrightness(0.0f); + } + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + + // Start the opacity animation. + { + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + animator->SetOpacity(0.0f); + } + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + + // Finish the brightness animation. + { + animator->StopAnimatingProperty(LayerAnimationElement::BRIGHTNESS); + EXPECT_FLOAT_EQ(0.0f, layer.layer_brightness()); + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + } + + // Finish the opacity animation. + { + animator->StopAnimatingProperty(LayerAnimationElement::OPACITY); + EXPECT_FLOAT_EQ(0.0f, layer.opacity()); + EXPECT_FALSE(layer.cc_layer_for_testing()->cache_render_surface()); + } + + // Case 2: the original cache status if true. + layer.AddCacheRenderSurfaceRequest(); + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + animator->SetBrightness(1.0f); + animator->SetOpacity(1.0f); + + // Start the brightness animation. + { + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + animator->SetBrightness(0.0f); + } + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + + // Start the opacity animation. + { + ScopedLayerAnimationSettings settings(animator.get()); + settings.CacheRenderSurface(); + animator->SetOpacity(0.0f); + } + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + + // Finish the brightness animation. + { + animator->StopAnimatingProperty(LayerAnimationElement::BRIGHTNESS); + EXPECT_FLOAT_EQ(0.0f, layer.layer_brightness()); + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + } + + // Finish the opacity animation. + { + animator->StopAnimatingProperty(LayerAnimationElement::OPACITY); + EXPECT_FLOAT_EQ(0.0f, layer.opacity()); + EXPECT_TRUE(layer.cc_layer_for_testing()->cache_render_surface()); + } +} + // Tests that an observer added to a scoped settings object is still notified // when the object goes out of scope due to the animation being interrupted. TEST(LayerAnimatorTest, InterruptedImplicitAnimationObservers) { diff --git a/chromium/ui/compositor/layer_observer.h b/chromium/ui/compositor/layer_observer.h index fa3480bdabf..5e59deb5dab 100644 --- a/chromium/ui/compositor/layer_observer.h +++ b/chromium/ui/compositor/layer_observer.h @@ -11,8 +11,6 @@ namespace ui { class Layer; -// TODO(domlaskowski): This is only used for layer mirroring, so it should be -// removed and replaced by a private equivalent in the Layer class. class COMPOSITOR_EXPORT LayerObserver { public: virtual void LayerDestroyed(Layer* layer) {} diff --git a/chromium/ui/compositor/layer_owner_unittest.cc b/chromium/ui/compositor/layer_owner_unittest.cc index 94992e09755..3c1a65d666c 100644 --- a/chromium/ui/compositor/layer_owner_unittest.cc +++ b/chromium/ui/compositor/layer_owner_unittest.cc @@ -81,7 +81,8 @@ void LayerOwnerTestWithCompositor::SetUp() { compositor_.reset( new ui::Compositor(context_factory_private->AllocateFrameSinkId(), context_factory, context_factory_private, task_runner, - false /* enable_surface_synchronization */)); + false /* enable_surface_synchronization */, + false /* enable_pixel_canvas */)); compositor_->SetAcceleratedWidget(gfx::kNullAcceleratedWidget); } diff --git a/chromium/ui/compositor/layer_unittest.cc b/chromium/ui/compositor/layer_unittest.cc index fbb15a8e4eb..dcb2cf9ee7d 100644 --- a/chromium/ui/compositor/layer_unittest.cc +++ b/chromium/ui/compositor/layer_unittest.cc @@ -18,15 +18,16 @@ #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" +#include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" #include "cc/animation/animation_player.h" #include "cc/layers/layer.h" -#include "cc/output/copy_output_request.h" -#include "cc/output/copy_output_result.h" #include "cc/test/pixel_test_utils.h" +#include "components/viz/common/quads/copy_output_request.h" +#include "components/viz/common/quads/copy_output_result.h" #include "components/viz/common/surfaces/sequence_surface_reference_factory.h" #include "components/viz/common/surfaces/surface_id.h" #include "components/viz/common/surfaces/surface_reference_factory.h" @@ -197,8 +198,8 @@ class LayerWithRealCompositorTest : public testing::Test { void ReadPixels(SkBitmap* bitmap, gfx::Rect source_rect) { scoped_refptr<ReadbackHolder> holder(new ReadbackHolder); - std::unique_ptr<cc::CopyOutputRequest> request = - cc::CopyOutputRequest::CreateBitmapRequest( + std::unique_ptr<viz::CopyOutputRequest> request = + viz::CopyOutputRequest::CreateBitmapRequest( base::BindOnce(&ReadbackHolder::OutputRequestCallback, holder)); request->set_area(source_rect); @@ -245,7 +246,7 @@ class LayerWithRealCompositorTest : public testing::Test { public: ReadbackHolder() : run_loop_(new base::RunLoop) {} - void OutputRequestCallback(std::unique_ptr<cc::CopyOutputResult> result) { + void OutputRequestCallback(std::unique_ptr<viz::CopyOutputResult> result) { result_ = result->TakeBitmap(); run_loop_->Quit(); } @@ -550,7 +551,7 @@ TEST(LayerStandaloneTest, ReleaseMailboxOnDestruction) { bool callback_run = false; viz::TextureMailbox mailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), 0); layer->SetTextureMailbox(mailbox, - cc::SingleReleaseCallback::Create( + viz::SingleReleaseCallback::Create( base::Bind(ReturnMailbox, &callback_run)), gfx::Size(10, 10)); EXPECT_FALSE(callback_run); @@ -735,7 +736,7 @@ TEST_F(LayerWithDelegateTest, Cloning) { layer->SetLayerInverted(true); const float temperature = 0.8f; layer->SetLayerTemperature(temperature); - layer->SetCacheRenderSurface(true); + layer->AddCacheRenderSurfaceRequest(); auto clone = layer->Clone(); @@ -944,8 +945,9 @@ TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { bool callback1_run = false; viz::TextureMailbox mailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), 0); - l1->SetTextureMailbox(mailbox, cc::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, &callback1_run)), + l1->SetTextureMailbox(mailbox, + viz::SingleReleaseCallback::Create( + base::Bind(ReturnMailbox, &callback1_run)), gfx::Size(10, 10)); EXPECT_NE(before_layer, l1->cc_layer_for_testing()); @@ -959,8 +961,9 @@ TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { bool callback2_run = false; mailbox = viz::TextureMailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), 0); - l1->SetTextureMailbox(mailbox, cc::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, &callback2_run)), + l1->SetTextureMailbox(mailbox, + viz::SingleReleaseCallback::Create( + base::Bind(ReturnMailbox, &callback2_run)), gfx::Size(10, 10)); EXPECT_TRUE(callback1_run); EXPECT_FALSE(callback2_run); @@ -979,8 +982,9 @@ TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) { // Back to a texture, without changing the bounds of the layer or the texture. bool callback3_run = false; mailbox = viz::TextureMailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), 0); - l1->SetTextureMailbox(mailbox, cc::SingleReleaseCallback::Create( - base::Bind(ReturnMailbox, &callback3_run)), + l1->SetTextureMailbox(mailbox, + viz::SingleReleaseCallback::Create( + base::Bind(ReturnMailbox, &callback3_run)), gfx::Size(10, 10)); EXPECT_NE(before_layer, l1->cc_layer_for_testing()); @@ -1126,7 +1130,7 @@ 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; - cc::ReleaseCallback callback = + viz::ReleaseCallback callback = base::Bind([](base::RunLoop* run_loop, const gpu::SyncToken& sync_token, bool is_lost) { run_loop->Quit(); }, base::Unretained(&run_loop)); @@ -1134,7 +1138,7 @@ TEST_F(LayerWithNullDelegateTest, EmptyDamagedRect) { std::unique_ptr<Layer> root(CreateLayer(LAYER_SOLID_COLOR)); viz::TextureMailbox mailbox(gpu::Mailbox::Generate(), gpu::SyncToken(), GL_TEXTURE_2D); - root->SetTextureMailbox(mailbox, cc::SingleReleaseCallback::Create(callback), + root->SetTextureMailbox(mailbox, viz::SingleReleaseCallback::Create(callback), gfx::Size(10, 10)); compositor()->SetRootLayer(root.get()); @@ -1261,9 +1265,9 @@ TEST_F(LayerWithRealCompositorTest, DrawAlphaThresholdFilterPixels) { CreateColorLayer(blue_with_alpha, gfx::Rect(viewport_size))); // Add a shape to restrict the visible part of the layer. - SkRegion shape; - shape.setRect(0, 0, viewport_size.width(), blue_height); - foreground_layer->SetAlphaShape(base::WrapUnique(new SkRegion(shape))); + auto shape = base::MakeUnique<Layer::ShapeRects>(); + shape->emplace_back(0, 0, viewport_size.width(), blue_height); + foreground_layer->SetAlphaShape(std::move(shape)); foreground_layer->SetFillsBoundsOpaquely(false); @@ -2006,7 +2010,7 @@ TEST_F(LayerWithRealCompositorTest, SwitchCCLayerCacheRenderSurface) { GetCompositor()->SetRootLayer(root.get()); root->Add(l1.get()); - l1->SetCacheRenderSurface(true); + l1->AddCacheRenderSurfaceRequest(); // Change l1's cc::Layer. l1->SwitchCCLayerForTest(); diff --git a/chromium/ui/compositor/paint_cache.cc b/chromium/ui/compositor/paint_cache.cc index 417a1798083..71b2a0e5ed3 100644 --- a/chromium/ui/compositor/paint_cache.cc +++ b/chromium/ui/compositor/paint_cache.cc @@ -10,30 +10,23 @@ namespace ui { -PaintCache::PaintCache() {} - -PaintCache::~PaintCache() { -} +PaintCache::PaintCache() = default; +PaintCache::~PaintCache() = default; bool PaintCache::UseCache(const PaintContext& context, const gfx::Size& size_in_context) { if (!paint_op_buffer_) return false; DCHECK(context.list_); - cc::PaintOpBuffer* buffer = context.list_->StartPaint(); - buffer->push<cc::DrawRecordOp>(paint_op_buffer_); + context.list_->StartPaint(); + context.list_->push<cc::DrawRecordOp>(paint_op_buffer_); gfx::Rect bounds_in_layer = context.ToLayerSpaceBounds(size_in_context); context.list_->EndPaintOfUnpaired(bounds_in_layer); return true; } -cc::PaintOpBuffer* PaintCache::ResetCache() { - paint_op_buffer_ = sk_make_sp<cc::PaintOpBuffer>(); - return paint_op_buffer_.get(); -} - -void PaintCache::FinalizeCache() { - paint_op_buffer_->ShrinkToFit(); +void PaintCache::SetPaintOpBuffer(sk_sp<cc::PaintOpBuffer> paint_op_buffer) { + paint_op_buffer_ = std::move(paint_op_buffer); } } // namespace ui diff --git a/chromium/ui/compositor/paint_cache.h b/chromium/ui/compositor/paint_cache.h index c32e96e54dd..04ae875a64e 100644 --- a/chromium/ui/compositor/paint_cache.h +++ b/chromium/ui/compositor/paint_cache.h @@ -36,12 +36,7 @@ class COMPOSITOR_EXPORT PaintCache { // Only PaintRecorder can modify these. friend PaintRecorder; - // Resets the cache to be empty, and returns a PaintOpBuffer that is the new - // empty cache. Adding PaintOps to the buffer will put them in the cache. - cc::PaintOpBuffer* ResetCache(); - - // Call when done recording into the cache's PaintOpBuffer. - void FinalizeCache(); + void SetPaintOpBuffer(sk_sp<cc::PaintOpBuffer> paint_op_buffer); // Stored in an sk_sp because PaintOpBuffer requires this to append the cached // items into it. diff --git a/chromium/ui/compositor/paint_context.cc b/chromium/ui/compositor/paint_context.cc index b2b4fac54b3..48e2e9d8f68 100644 --- a/chromium/ui/compositor/paint_context.cc +++ b/chromium/ui/compositor/paint_context.cc @@ -10,10 +10,14 @@ namespace ui { PaintContext::PaintContext(cc::DisplayItemList* list, float device_scale_factor, - const gfx::Rect& invalidation) + const gfx::Rect& invalidation, + bool is_pixel_canvas) : list_(list), device_scale_factor_(device_scale_factor), - invalidation_(invalidation) { + invalidation_(gfx::ScaleToRoundedRect( + invalidation, + is_pixel_canvas ? device_scale_factor_ : 1.f)), + is_pixel_canvas_(is_pixel_canvas) { #if DCHECK_IS_ON() root_visited_ = nullptr; inside_paint_recorder_ = false; @@ -25,7 +29,8 @@ PaintContext::PaintContext(const PaintContext& other, : list_(other.list_), device_scale_factor_(other.device_scale_factor_), invalidation_(other.invalidation_), - offset_(other.offset_ + offset) { + offset_(other.offset_ + offset), + is_pixel_canvas_(other.is_pixel_canvas_) { #if DCHECK_IS_ON() root_visited_ = other.root_visited_; inside_paint_recorder_ = other.inside_paint_recorder_; @@ -37,7 +42,8 @@ PaintContext::PaintContext(const PaintContext& other, : list_(other.list_), device_scale_factor_(other.device_scale_factor_), invalidation_(), - offset_(other.offset_) { + offset_(other.offset_), + is_pixel_canvas_(other.is_pixel_canvas_) { #if DCHECK_IS_ON() root_visited_ = other.root_visited_; inside_paint_recorder_ = other.inside_paint_recorder_; diff --git a/chromium/ui/compositor/paint_context.h b/chromium/ui/compositor/paint_context.h index bc17c50f46b..96385275c43 100644 --- a/chromium/ui/compositor/paint_context.h +++ b/chromium/ui/compositor/paint_context.h @@ -29,7 +29,8 @@ class COMPOSITOR_EXPORT PaintContext { // |invalidation|. PaintContext(cc::DisplayItemList* list, float device_scale_factor, - const gfx::Rect& invalidation); + const gfx::Rect& invalidation, + bool is_pixel_canvas); // Clone a PaintContext with an additional |offset|. PaintContext(const PaintContext& other, const gfx::Vector2d& offset); @@ -46,6 +47,13 @@ class COMPOSITOR_EXPORT PaintContext { // invalid. bool CanCheckInvalid() const { return !invalidation_.IsEmpty(); } + // The device scale of the frame being painted. + float device_scale_factor() const { return device_scale_factor_; } + + // Returns true if the paint commands are recorded at pixel size instead of + // DIP. + bool is_pixel_canvas() const { return is_pixel_canvas_; } + // When true, the |bounds| touches an invalidated area, so should be // re-painted. When false, re-painting can be skipped. Bounds should be in // the local space with offsets up to the painting root in the PaintContext. @@ -94,6 +102,8 @@ class COMPOSITOR_EXPORT PaintContext { // Offset from the PaintContext to the space of the paint root and the // |invalidation_|. gfx::Vector2d offset_; + // If enabled, the paint commands are recorded at pixel size. + const bool is_pixel_canvas_; #if DCHECK_IS_ON() // Used to verify that the |invalidation_| is only used to compare against diff --git a/chromium/ui/compositor/paint_recorder.cc b/chromium/ui/compositor/paint_recorder.cc index 8a8c4c28caf..f85d3cbdc76 100644 --- a/chromium/ui/compositor/paint_recorder.cc +++ b/chromium/ui/compositor/paint_recorder.cc @@ -21,32 +21,60 @@ namespace ui { // to the |context|'s PaintOpBuffer. PaintRecorder::PaintRecorder(const PaintContext& context, const gfx::Size& recording_size, + float recording_scale_x, + float recording_scale_y, PaintCache* cache) : context_(context), - record_canvas_(cache ? cache->ResetCache() : context_.list_->StartPaint(), + local_list_(cache ? base::MakeRefCounted<cc::DisplayItemList>( + cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer) + : nullptr), + record_canvas_(cache ? local_list_.get() : context_.list_, gfx::RectToSkRect(gfx::Rect(recording_size))), canvas_(&record_canvas_, context.device_scale_factor_), cache_(cache), recording_size_(recording_size) { + if (cache) { + local_list_->StartPaint(); + } else { + context_.list_->StartPaint(); + } + #if DCHECK_IS_ON() - DCHECK(!context.inside_paint_recorder_); - context.inside_paint_recorder_ = true; + DCHECK(!context_.inside_paint_recorder_); + context_.inside_paint_recorder_ = true; #endif + if (context_.is_pixel_canvas()) { + canvas()->Save(); + canvas()->Scale(recording_scale_x, recording_scale_y); + } } +// TODO(malaykeshav): The scaling of recording size needs to be handled case +// by case and the decision to perform the scale should be moved to the caller. PaintRecorder::PaintRecorder(const PaintContext& context, const gfx::Size& recording_size) - : PaintRecorder(context, recording_size, nullptr) {} + : PaintRecorder( + context, + gfx::ScaleToRoundedSize( + recording_size, + context.is_pixel_canvas() ? context.device_scale_factor_ : 1.f), + context.device_scale_factor_, + context.device_scale_factor_, + nullptr) {} PaintRecorder::~PaintRecorder() { #if DCHECK_IS_ON() context_.inside_paint_recorder_ = false; #endif + if (context_.is_pixel_canvas()) + canvas()->Restore(); // If using cache, append what we've saved there to the PaintContext. // Otherwise, the content is already stored in the PaintContext, and we can // just close it. if (cache_) { - cache_->FinalizeCache(); + local_list_->EndPaintOfUnpaired(gfx::Rect()); + local_list_->Finalize(); + cache_->SetPaintOpBuffer(local_list_->ReleaseAsRecord()); cache_->UseCache(context_, recording_size_); } else { gfx::Rect bounds_in_layer = context_.ToLayerSpaceBounds(recording_size_); diff --git a/chromium/ui/compositor/paint_recorder.h b/chromium/ui/compositor/paint_recorder.h index 3785c1bbc23..609d6f0bf42 100644 --- a/chromium/ui/compositor/paint_recorder.h +++ b/chromium/ui/compositor/paint_recorder.h @@ -8,6 +8,7 @@ #include <memory> #include "base/macros.h" +#include "cc/paint/display_item_list.h" #include "cc/paint/record_paint_canvas.h" #include "ui/compositor/compositor_export.h" #include "ui/gfx/canvas.h" @@ -31,6 +32,8 @@ class COMPOSITOR_EXPORT PaintRecorder { // PaintRecorder is in use. Canvas is bounded by |recording_size|. PaintRecorder(const PaintContext& context, const gfx::Size& recording_size, + float recording_scale_x, + float recording_scale_y, PaintCache* cache); PaintRecorder(const PaintContext& context, const gfx::Size& recording_size); ~PaintRecorder(); @@ -40,6 +43,7 @@ class COMPOSITOR_EXPORT PaintRecorder { private: const PaintContext& context_; + scoped_refptr<cc::DisplayItemList> local_list_; cc::RecordPaintCanvas record_canvas_; gfx::Canvas canvas_; PaintCache* cache_; diff --git a/chromium/ui/compositor/scoped_layer_animation_settings.cc b/chromium/ui/compositor/scoped_layer_animation_settings.cc index 71db455afb0..2ffc1250b13 100644 --- a/chromium/ui/compositor/scoped_layer_animation_settings.cc +++ b/chromium/ui/compositor/scoped_layer_animation_settings.cc @@ -10,11 +10,49 @@ #include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/layer_animator.h" +#include "ui/compositor/layer_observer.h" namespace { const int kDefaultTransitionDurationMs = 200; +class CacheRenderSurfaceObserver : public ui::ImplicitAnimationObserver, + public ui::LayerObserver { + public: + CacheRenderSurfaceObserver(ui::Layer* layer) : layer_(layer) { + layer_->AddObserver(this); + layer_->AddCacheRenderSurfaceRequest(); + } + ~CacheRenderSurfaceObserver() override { + if (layer_) + layer_->RemoveObserver(this); + } + + // ui::ImplicitAnimationObserver overrides: + void OnImplicitAnimationsCompleted() override { + // If animation finishes before |layer_| is destoyed, we will reset the + // cache and remove |this| from the |layer_| observer list when deleting + // |this|. + if (layer_) + layer_->RemoveCacheRenderSurfaceRequest(); + delete this; + } + + // ui::LayerObserver overrides: + void LayerDestroyed(ui::Layer* layer) override { + // If the animation is still going past layer destruction then we want the + // layer too keep being cached until the animation has finished. We will + // defer deleting |this| until the animation finishes. + layer_->RemoveObserver(this); + layer_ = nullptr; + } + + private: + ui::Layer* layer_; + + DISALLOW_COPY_AND_ASSIGN(CacheRenderSurfaceObserver); +}; + } // namespace namespace ui { @@ -63,6 +101,11 @@ void ScopedLayerAnimationSettings::SetTransitionDuration( animator_->SetTransitionDuration(duration); } +void ScopedLayerAnimationSettings::CacheRenderSurface() { + AddObserver( + new CacheRenderSurfaceObserver(animator_->delegate()->GetLayer())); +} + void ScopedLayerAnimationSettings::LockTransitionDuration() { animator_->is_transition_duration_locked_ = true; } diff --git a/chromium/ui/compositor/scoped_layer_animation_settings.h b/chromium/ui/compositor/scoped_layer_animation_settings.h index ddc6d68499b..61867126bca 100644 --- a/chromium/ui/compositor/scoped_layer_animation_settings.h +++ b/chromium/ui/compositor/scoped_layer_animation_settings.h @@ -31,6 +31,9 @@ class COMPOSITOR_EXPORT ScopedLayerAnimationSettings { void SetAnimationMetricsReporter(AnimationMetricsReporter* reporter); void SetTransitionDuration(base::TimeDelta duration); + // This will request render surface caching on the animating layer. The cache + // request will be removed at the end of the animation. + void CacheRenderSurface(); base::TimeDelta GetTransitionDuration() const; // Locks transition duration in |animator_|. When transition duration diff --git a/chromium/ui/compositor/test/test_compositor_host_ozone.cc b/chromium/ui/compositor/test/test_compositor_host_ozone.cc index 97d33afa448..1659d9331d9 100644 --- a/chromium/ui/compositor/test/test_compositor_host_ozone.cc +++ b/chromium/ui/compositor/test/test_compositor_host_ozone.cc @@ -84,7 +84,8 @@ TestCompositorHostOzone::TestCompositorHostOzone( context_factory, context_factory_private, base::ThreadTaskRunnerHandle::Get(), - false /* enable_surface_synchronization */) {} + false /* enable_surface_synchronization */, + false /* enable_pixel_canvas */) {} TestCompositorHostOzone::~TestCompositorHostOzone() {} diff --git a/chromium/ui/compositor/transform_animation_curve_adapter_unittest.cc b/chromium/ui/compositor/transform_animation_curve_adapter_unittest.cc index b5b44e90270..7c80133bb28 100644 --- a/chromium/ui/compositor/transform_animation_curve_adapter_unittest.cc +++ b/chromium/ui/compositor/transform_animation_curve_adapter_unittest.cc @@ -7,7 +7,6 @@ #include <sstream> #include "base/time/time.h" -#include "cc/base/time_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/test/test_utils.h" @@ -39,7 +38,7 @@ TEST(InverseTransformCurveAdapterTest, InversesTransform) { static const int kSteps = 1000; double step = 1.0 / kSteps; for (int i = 0; i <= kSteps; ++i) { - base::TimeDelta time_step = cc::TimeUtil::Scale(duration, i * step); + base::TimeDelta time_step = duration * (i * step); std::ostringstream message; message << "Step " << i << " of " << kSteps; SCOPED_TRACE(message.str()); diff --git a/chromium/ui/compositor/transform_recorder.cc b/chromium/ui/compositor/transform_recorder.cc index 525de51cc98..4175f1e4fbc 100644 --- a/chromium/ui/compositor/transform_recorder.cc +++ b/chromium/ui/compositor/transform_recorder.cc @@ -17,8 +17,8 @@ TransformRecorder::~TransformRecorder() { if (!transformed_) return; - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::RestoreOp>(); + context_.list_->StartPaint(); + context_.list_->push<cc::RestoreOp>(); context_.list_->EndPaintOfPairedEnd(); } @@ -27,9 +27,9 @@ void TransformRecorder::Transform(const gfx::Transform& transform) { if (transform.IsIdentity()) return; - cc::PaintOpBuffer* buffer = context_.list_->StartPaint(); - buffer->push<cc::SaveOp>(); - buffer->push<cc::ConcatOp>(static_cast<SkMatrix>(transform.matrix())); + context_.list_->StartPaint(); + context_.list_->push<cc::SaveOp>(); + context_.list_->push<cc::ConcatOp>(static_cast<SkMatrix>(transform.matrix())); context_.list_->EndPaintOfPairedBegin(); transformed_ = true; diff --git a/chromium/ui/display/BUILD.gn b/chromium/ui/display/BUILD.gn index 0d7b80d03ac..f4a4f5b3c94 100644 --- a/chromium/ui/display/BUILD.gn +++ b/chromium/ui/display/BUILD.gn @@ -145,7 +145,6 @@ 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/mojo/touch_device_transform_struct_traits_unittest.cc", "manager/chromeos/query_content_protection_task_unittest.cc", "manager/chromeos/touch_transform_controller_unittest.cc", "manager/chromeos/touchscreen_util_unittest.cc", @@ -171,10 +170,10 @@ test("display_unittests") { "//testing/gmock", "//testing/gtest", "//ui/display/manager", - "//ui/display/manager/chromeos/mojo:interfaces", "//ui/display/mojo:interfaces", "//ui/display/types", "//ui/display/util", + "//ui/events:test_support", "//ui/events/devices", "//ui/gfx:test_support", "//ui/gfx/geometry", @@ -185,11 +184,4 @@ test("display_unittests") { # does not compile display_observer.cc sources -= [ "display_change_notifier_unittest.cc" ] } - - if (is_chromeos && use_x11) { - sources += [ - "manager/chromeos/x11/display_util_x11_unittest.cc", - "manager/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc", - ] - } } diff --git a/chromium/ui/display/display.cc b/chromium/ui/display/display.cc index e5a0414b3a9..c0901916615 100644 --- a/chromium/ui/display/display.cc +++ b/chromium/ui/display/display.cc @@ -61,7 +61,8 @@ int64_t internal_display_id_ = -1; } // namespace bool CompareDisplayIds(int64_t id1, int64_t id2) { - DCHECK_NE(id1, id2); + if (id1 == id2) + return false; // Output index is stored in the first 8 bits. See GetDisplayIdFromEDID // in edid_parser.cc. int index_1 = id1 & 0xFF; @@ -94,6 +95,7 @@ void Display::ResetForceDeviceScaleFactorForTesting() { // static void Display::SetForceDeviceScaleFactor(double dsf) { // Reset any previously set values and unset the flag. + g_has_forced_device_scale_factor = -1; g_forced_device_scale_factor = -1.0; base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( @@ -108,6 +110,12 @@ gfx::ColorSpace Display::GetForcedColorProfile() { switches::kForceColorProfile); if (value == "srgb") { return gfx::ColorSpace::CreateSRGB(); + } else if (value == "display-p3-d65") { + return gfx::ColorSpace::CreateDisplayP3D65(); + } else if (value == "scrgb-linear") { + return gfx::ColorSpace::CreateSCRGBLinear(); + } else if (value == "extended-srgb") { + return gfx::ColorSpace::CreateExtendedSRGB(); } else if (value == "generic-rgb") { return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB, gfx::ColorSpace::TransferID::GAMMA18); @@ -143,14 +151,11 @@ Display::Display(int64_t id, const gfx::Rect& bounds) bounds_(bounds), work_area_(bounds), device_scale_factor_(GetForcedDeviceScaleFactor()), - color_space_(HasForceColorProfile() ? GetForcedColorProfile() - : gfx::ColorSpace::CreateSRGB()), + color_space_(gfx::ColorSpace::CreateSRGB()), color_depth_(DEFAULT_BITS_PER_PIXEL), depth_per_component_(DEFAULT_BITS_PER_COMPONENT) { - if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableHDR)) { - color_depth_ = HDR_BITS_PER_PIXEL; - depth_per_component_ = HDR_BITS_PER_COMPONENT; - } + if (HasForceColorProfile()) + SetColorSpaceAndDepth(GetForcedColorProfile()); #if defined(USE_AURA) SetScaleAndBounds(device_scale_factor_, bounds); #endif @@ -231,6 +236,17 @@ void Display::SetSize(const gfx::Size& size_in_pixel) { SetScaleAndBounds(device_scale_factor_, gfx::Rect(origin, size_in_pixel)); } +void Display::SetColorSpaceAndDepth(const gfx::ColorSpace& color_space) { + color_space_ = color_space; + if (color_space_.IsHDR()) { + color_depth_ = HDR_BITS_PER_PIXEL; + depth_per_component_ = HDR_BITS_PER_COMPONENT; + } else { + color_depth_ = DEFAULT_BITS_PER_PIXEL; + depth_per_component_ = DEFAULT_BITS_PER_COMPONENT; + } +} + void Display::UpdateWorkAreaFromInsets(const gfx::Insets& insets) { work_area_ = bounds_; work_area_.Inset(insets); diff --git a/chromium/ui/display/display.h b/chromium/ui/display/display.h index d81227aabbb..4690594ad7c 100644 --- a/chromium/ui/display/display.h +++ b/chromium/ui/display/display.h @@ -192,12 +192,16 @@ class DISPLAY_EXPORT Display final { maximum_cursor_size_ = size; } - // The full color space of the display. + // The color space of the display. gfx::ColorSpace color_space() const { return color_space_; } void set_color_space(const gfx::ColorSpace& color_space) { color_space_ = color_space; } + // Set the color space of the display and reset the color depth and depth per + // component based on whether or not the color space is HDR. + void SetColorSpaceAndDepth(const gfx::ColorSpace& color_space); + // The number of bits per pixel. Used by media query APIs. int color_depth() const { return color_depth_; } void set_color_depth(int color_depth) { diff --git a/chromium/ui/display/display_switches.cc b/chromium/ui/display/display_switches.cc index d4f897ce487..354b483ba1f 100644 --- a/chromium/ui/display/display_switches.cc +++ b/chromium/ui/display/display_switches.cc @@ -43,15 +43,17 @@ const char kScreenConfig[] = "screen-config"; // This is for debugging on linux desktop. const char kUseFirstDisplayAsInternal[] = "use-first-display-as-internal"; -// Use an fp16 scRGB swap chain compatible with HDR output. -const char kEnableHDR[] = "enable-hdr"; - #if defined(OS_CHROMEOS) -const char kDisableDisplayColorCalibration[] = - "disable-display-color-calibration"; // Enables unified desktop mode. const char kEnableUnifiedDesktop[] = "ash-enable-unified-desktop"; #endif } // namespace switches + +namespace features { + +const base::Feature kHighDynamicRange{"HighDynamicRange", + base::FEATURE_DISABLED_BY_DEFAULT}; + +} // namespace features diff --git a/chromium/ui/display/display_switches.h b/chromium/ui/display/display_switches.h index 610ba111574..b88b99256f2 100644 --- a/chromium/ui/display/display_switches.h +++ b/chromium/ui/display/display_switches.h @@ -6,6 +6,7 @@ #define UI_DISPLAY_DISPLAY_SWITCHES_H_ #include "base/compiler_specific.h" +#include "base/feature_list.h" #include "build/build_config.h" #include "ui/display/display_export.h" @@ -20,13 +21,17 @@ DISPLAY_EXPORT extern const char kHostWindowBounds[]; DISPLAY_EXPORT extern const char kScreenConfig[]; DISPLAY_EXPORT extern const char kSecondaryDisplayLayout[]; DISPLAY_EXPORT extern const char kUseFirstDisplayAsInternal[]; -DISPLAY_EXPORT extern const char kEnableHDR[]; #if defined(OS_CHROMEOS) -DISPLAY_EXPORT extern const char kDisableDisplayColorCalibration[]; DISPLAY_EXPORT extern const char kEnableUnifiedDesktop[]; #endif } // namespace switches +namespace features { + +DISPLAY_EXPORT extern const base::Feature kHighDynamicRange; + +} // namespace features + #endif // UI_DISPLAY_DISPLAY_SWITCHES_H_ diff --git a/chromium/ui/display/display_unittest.cc b/chromium/ui/display/display_unittest.cc index aa0b55d5212..8fb0a0fdcf7 100644 --- a/chromium/ui/display/display_unittest.cc +++ b/chromium/ui/display/display_unittest.cc @@ -72,20 +72,17 @@ TEST(DisplayTest, ForcedDeviceScaleFactor) { } TEST(DisplayTest, DisplayHDRValues) { - base::test::ScopedCommandLine scoped_command_line; - base::CommandLine* command_line = scoped_command_line.GetProcessCommandLine(); - { - Display display; - EXPECT_EQ(24, display.color_depth()); - EXPECT_EQ(8, display.depth_per_component()); - } - - command_line->AppendSwitch(switches::kEnableHDR); - { - Display display; - EXPECT_EQ(48, display.color_depth()); - EXPECT_EQ(16, display.depth_per_component()); - } + Display display; + EXPECT_EQ(24, display.color_depth()); + EXPECT_EQ(8, display.depth_per_component()); + + display.SetColorSpaceAndDepth(gfx::ColorSpace::CreateSCRGBLinear()); + EXPECT_EQ(48, display.color_depth()); + EXPECT_EQ(16, display.depth_per_component()); + + display.SetColorSpaceAndDepth(gfx::ColorSpace::CreateSRGB()); + EXPECT_EQ(24, display.color_depth()); + EXPECT_EQ(8, display.depth_per_component()); } } // namespace display diff --git a/chromium/ui/display/fake_display_delegate.cc b/chromium/ui/display/fake_display_delegate.cc index ba6ad179280..bc31a7275f4 100644 --- a/chromium/ui/display/fake_display_delegate.cc +++ b/chromium/ui/display/fake_display_delegate.cc @@ -108,10 +108,6 @@ void FakeDisplayDelegate::Initialize() { initialized_ = true; } -void FakeDisplayDelegate::GrabServer() {} - -void FakeDisplayDelegate::UngrabServer() {} - void FakeDisplayDelegate::TakeDisplayControl( const DisplayControlCallback& callback) { callback.Run(false); @@ -122,12 +118,6 @@ void FakeDisplayDelegate::RelinquishDisplayControl( callback.Run(false); } -void FakeDisplayDelegate::SyncWithServer() {} - -void FakeDisplayDelegate::SetBackgroundColor(uint32_t color_argb) {} - -void FakeDisplayDelegate::ForceDPMSOn() {} - void FakeDisplayDelegate::GetDisplays(const GetDisplaysCallback& callback) { std::vector<DisplaySnapshot*> displays; for (auto& display : displays_) @@ -135,9 +125,6 @@ void FakeDisplayDelegate::GetDisplays(const GetDisplaysCallback& callback) { callback.Run(displays); } -void FakeDisplayDelegate::AddMode(const DisplaySnapshot& output, - const DisplayMode* mode) {} - void FakeDisplayDelegate::Configure(const DisplaySnapshot& output, const DisplayMode* mode, const gfx::Point& origin, @@ -169,8 +156,6 @@ void FakeDisplayDelegate::Configure(const DisplaySnapshot& output, } } -void FakeDisplayDelegate::CreateFrameBuffer(const gfx::Size& size) {} - void FakeDisplayDelegate::GetHDCPState(const DisplaySnapshot& output, const GetHDCPStateCallback& callback) { callback.Run(false, HDCP_STATE_UNDESIRED); @@ -182,18 +167,6 @@ void FakeDisplayDelegate::SetHDCPState(const DisplaySnapshot& output, callback.Run(false); } -std::vector<ColorCalibrationProfile> -FakeDisplayDelegate::GetAvailableColorCalibrationProfiles( - const DisplaySnapshot& output) { - return std::vector<ColorCalibrationProfile>(); -} - -bool FakeDisplayDelegate::SetColorCalibrationProfile( - const DisplaySnapshot& output, - ColorCalibrationProfile new_profile) { - return false; -} - bool FakeDisplayDelegate::SetColorCorrection( const DisplaySnapshot& output, const std::vector<GammaRampRGBEntry>& degamma_lut, diff --git a/chromium/ui/display/fake_display_delegate.h b/chromium/ui/display/fake_display_delegate.h index 73e5f36dc7d..17d34fefec6 100644 --- a/chromium/ui/display/fake_display_delegate.h +++ b/chromium/ui/display/fake_display_delegate.h @@ -81,30 +81,19 @@ class DISPLAY_EXPORT FakeDisplayDelegate : public NativeDisplayDelegate, // NativeDisplayDelegate overrides: void Initialize() override; - void GrabServer() override; - void UngrabServer() override; void TakeDisplayControl(const DisplayControlCallback& callback) override; void RelinquishDisplayControl( const DisplayControlCallback& callback) override; - void SyncWithServer() override; - void SetBackgroundColor(uint32_t color_argb) override; - void ForceDPMSOn() override; void GetDisplays(const GetDisplaysCallback& callback) override; - void AddMode(const DisplaySnapshot& output, const DisplayMode* mode) override; void Configure(const DisplaySnapshot& output, const DisplayMode* mode, const gfx::Point& origin, const ConfigureCallback& callback) override; - void CreateFrameBuffer(const gfx::Size& size) override; void GetHDCPState(const DisplaySnapshot& output, const GetHDCPStateCallback& callback) override; void SetHDCPState(const DisplaySnapshot& output, HDCPState state, const SetHDCPStateCallback& callback) override; - std::vector<ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( - const DisplaySnapshot& output) override; - bool SetColorCalibrationProfile(const DisplaySnapshot& output, - ColorCalibrationProfile new_profile) override; bool SetColorCorrection(const DisplaySnapshot& output, const std::vector<GammaRampRGBEntry>& degamma_lut, const std::vector<GammaRampRGBEntry>& gamma_lut, diff --git a/chromium/ui/display/fake_display_snapshot.cc b/chromium/ui/display/fake_display_snapshot.cc index c7294a56fb9..bd5951118a5 100644 --- a/chromium/ui/display/fake_display_snapshot.cc +++ b/chromium/ui/display/fake_display_snapshot.cc @@ -6,7 +6,6 @@ #include <inttypes.h> -#include <sstream> #include <utility> #include <vector> @@ -29,42 +28,6 @@ float PixelPitchMmFromDPI(float dpi) { return (1.0f / dpi) * kInchInMm; } -std::string ModeListString( - const std::vector<std::unique_ptr<const DisplayMode>>& modes) { - std::stringstream stream; - bool first = true; - for (auto& mode : modes) { - if (!first) - stream << ", "; - stream << mode->ToString(); - first = false; - } - return stream.str(); -} - -std::string DisplayConnectionTypeString(DisplayConnectionType type) { - switch (type) { - case DISPLAY_CONNECTION_TYPE_NONE: - return "none"; - case DISPLAY_CONNECTION_TYPE_UNKNOWN: - return "unknown"; - case DISPLAY_CONNECTION_TYPE_INTERNAL: - return "internal"; - case DISPLAY_CONNECTION_TYPE_VGA: - return "vga"; - case DISPLAY_CONNECTION_TYPE_HDMI: - return "hdmi"; - case DISPLAY_CONNECTION_TYPE_DVI: - return "dvi"; - case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: - return "dp"; - case DISPLAY_CONNECTION_TYPE_NETWORK: - return "network"; - } - NOTREACHED(); - return ""; -} - // Extracts text after specified delimiter. If the delimiter doesn't appear // exactly once the result will be empty and the input string will be // unmodified. Otherwise, the input string will contain the text before the @@ -200,8 +163,8 @@ std::unique_ptr<FakeDisplaySnapshot> Builder::Build() { return base::MakeUnique<FakeDisplaySnapshot>( id_, origin_, physical_size, type_, is_aspect_preserving_scaling_, - has_overscan_, has_color_correction_matrix_, name_, product_id_, - std::move(modes_), current_mode_, native_mode_); + has_overscan_, has_color_correction_matrix_, name_, std::move(modes_), + current_mode_, native_mode_, product_id_, maximum_cursor_size_); } Builder& Builder::SetId(int64_t id) { @@ -274,6 +237,11 @@ Builder& Builder::SetProductId(int64_t product_id) { return *this; } +Builder& Builder::SetMaximumCursorSize(const gfx::Size& maximum_cursor_size) { + maximum_cursor_size_ = maximum_cursor_size; + return *this; +} + Builder& Builder::SetDPI(int dpi) { dpi_ = static_cast<float>(dpi); return *this; @@ -320,10 +288,11 @@ FakeDisplaySnapshot::FakeDisplaySnapshot(int64_t display_id, bool has_overscan, bool has_color_correction_matrix, std::string display_name, - int64_t product_id, DisplayModeList modes, const DisplayMode* current_mode, - const DisplayMode* native_mode) + const DisplayMode* native_mode, + int64_t product_id, + const gfx::Size& maximum_cursor_size) : DisplaySnapshot(display_id, origin, physical_size, @@ -336,9 +305,9 @@ FakeDisplaySnapshot::FakeDisplaySnapshot(int64_t display_id, std::move(modes), std::vector<uint8_t>(), current_mode, - native_mode) { - product_id_ = product_id; -} + native_mode, + product_id, + maximum_cursor_size) {} FakeDisplaySnapshot::~FakeDisplaySnapshot() {} @@ -371,17 +340,4 @@ std::unique_ptr<DisplaySnapshot> FakeDisplaySnapshot::CreateFromSpec( return builder.Build(); } -std::string FakeDisplaySnapshot::ToString() const { - return base::StringPrintf( - "id=%" PRId64 - " current_mode=%s native_mode=%s origin=%s" - " physical_size=%s, type=%s name=\"%s\" 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()); -} - } // namespace display diff --git a/chromium/ui/display/fake_display_snapshot.h b/chromium/ui/display/fake_display_snapshot.h index 1cb0b46d0a9..c6502d9009b 100644 --- a/chromium/ui/display/fake_display_snapshot.h +++ b/chromium/ui/display/fake_display_snapshot.h @@ -60,6 +60,7 @@ class DISPLAY_EXPORT FakeDisplaySnapshot : public DisplaySnapshot { Builder& SetHasColorCorrectionMatrix(bool val); Builder& SetName(const std::string& name); Builder& SetProductId(int64_t product_id); + Builder& SetMaximumCursorSize(const gfx::Size& maximum_cursor_size); // Sets physical_size so that the screen has the specified DPI using the // native resolution. Builder& SetDPI(int dpi); @@ -85,6 +86,7 @@ class DISPLAY_EXPORT FakeDisplaySnapshot : public DisplaySnapshot { bool has_color_correction_matrix_ = false; std::string name_; int64_t product_id_ = DisplaySnapshot::kInvalidProductID; + gfx::Size maximum_cursor_size_ = gfx::Size(64, 64); DisplayModeList modes_; const DisplayMode* current_mode_ = nullptr; const DisplayMode* native_mode_ = nullptr; @@ -100,10 +102,11 @@ class DISPLAY_EXPORT FakeDisplaySnapshot : public DisplaySnapshot { bool has_overscan, bool has_color_correction_matrix, std::string display_name, - int64_t product_id, DisplayModeList modes, const DisplayMode* current_mode, - const DisplayMode* native_mode); + const DisplayMode* native_mode, + int64_t product_id, + const gfx::Size& maximum_cursor_size); ~FakeDisplaySnapshot() override; // Creates a display snapshot from the provided |spec| string. Returns null if @@ -113,9 +116,6 @@ class DISPLAY_EXPORT FakeDisplaySnapshot : public DisplaySnapshot { int64_t id, const std::string& spec); - // DisplaySnapshot: - std::string ToString() const override; - private: DISALLOW_COPY_AND_ASSIGN(FakeDisplaySnapshot); }; diff --git a/chromium/ui/display/mac/screen_mac.mm b/chromium/ui/display/mac/screen_mac.mm index 11eca13659c..da151150c5e 100644 --- a/chromium/ui/display/mac/screen_mac.mm +++ b/chromium/ui/display/mac/screen_mac.mm @@ -90,8 +90,10 @@ Display BuildDisplayForScreen(NSScreen* screen) { base::FeatureList::IsEnabled(features::kColorCorrectRendering); if (base::mac::IsAtLeastOS10_12() && !color_correct_rendering_enabled) color_space = base::mac::GetSystemColorSpace(); - display.set_color_space( - gfx::ICCProfile::FromCGColorSpace(color_space).GetColorSpace()); + gfx::ICCProfile icc_profile = + gfx::ICCProfile::FromCGColorSpace(color_space); + icc_profile.HistogramDisplay(display.id()); + display.set_color_space(icc_profile.GetColorSpace()); } display.set_color_depth(NSBitsPerPixelFromDepth([screen depth])); display.set_depth_per_component(NSBitsPerSampleFromDepth([screen depth])); diff --git a/chromium/ui/display/manager/BUILD.gn b/chromium/ui/display/manager/BUILD.gn index e646a6f0376..ba0f5911fcb 100644 --- a/chromium/ui/display/manager/BUILD.gn +++ b/chromium/ui/display/manager/BUILD.gn @@ -21,8 +21,6 @@ component("manager") { "chromeos/display_util.h", "chromeos/query_content_protection_task.cc", "chromeos/query_content_protection_task.h", - "chromeos/touch_device_transform.cc", - "chromeos/touch_device_transform.h", "chromeos/touch_transform_controller.cc", "chromeos/touch_transform_controller.h", "chromeos/touch_transform_setter.h", @@ -64,32 +62,4 @@ component("manager") { if (is_chromeos) { deps += [ "//chromeos" ] } - - if (use_x11) { - deps += [ "//ui/gfx/x" ] - } - - if (is_chromeos && use_x11) { - sources += [ - "chromeos/x11/display_mode_x11.cc", - "chromeos/x11/display_mode_x11.h", - "chromeos/x11/display_snapshot_x11.cc", - "chromeos/x11/display_snapshot_x11.h", - "chromeos/x11/display_util_x11.cc", - "chromeos/x11/display_util_x11.h", - "chromeos/x11/native_display_delegate_x11.cc", - "chromeos/x11/native_display_delegate_x11.h", - "chromeos/x11/native_display_event_dispatcher_x11.cc", - "chromeos/x11/native_display_event_dispatcher_x11.h", - ] - - configs += [ - "//build/config/linux:x11", - "//build/config/linux:xext", - "//build/config/linux:xi", - "//build/config/linux:xrandr", - ] - - deps += [ "//ui/events/platform" ] - } } diff --git a/chromium/ui/display/manager/chromeos/DEPS b/chromium/ui/display/manager/chromeos/DEPS index 5943a4fe475..5663dc26aa0 100644 --- a/chromium/ui/display/manager/chromeos/DEPS +++ b/chromium/ui/display/manager/chromeos/DEPS @@ -15,5 +15,6 @@ specific_include_rules = { ], "touch_transform_controller_unittest.cc": [ "+ui/events/devices/device_data_manager.h", + "+ui/events/test/device_data_manager_test_api.h", ], } diff --git a/chromium/ui/display/manager/chromeos/default_touch_transform_setter.cc b/chromium/ui/display/manager/chromeos/default_touch_transform_setter.cc index cd94a89c24a..1e9f12c9a84 100644 --- a/chromium/ui/display/manager/chromeos/default_touch_transform_setter.cc +++ b/chromium/ui/display/manager/chromeos/default_touch_transform_setter.cc @@ -4,8 +4,8 @@ #include "ui/display/manager/chromeos/default_touch_transform_setter.h" -#include "ui/display/manager/chromeos/touch_device_transform.h" #include "ui/events/devices/device_data_manager.h" +#include "ui/events/devices/touch_device_transform.h" namespace display { @@ -14,18 +14,8 @@ DefaultTouchTransformSetter::DefaultTouchTransformSetter() = default; DefaultTouchTransformSetter::~DefaultTouchTransformSetter() = default; void DefaultTouchTransformSetter::ConfigureTouchDevices( - const std::map<int32_t, double>& scales, - const std::vector<TouchDeviceTransform>& transforms) { - ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); - device_manager->ClearTouchDeviceAssociations(); - for (auto& device_scale_pair : scales) { - device_manager->UpdateTouchRadiusScale(device_scale_pair.first, - device_scale_pair.second); - } - for (const TouchDeviceTransform& transform : transforms) { - device_manager->UpdateTouchInfoForDisplay( - transform.display_id, transform.device_id, transform.transform); - } + const std::vector<ui::TouchDeviceTransform>& transforms) { + ui::DeviceDataManager::GetInstance()->ConfigureTouchDevices(transforms); } } // namespace display diff --git a/chromium/ui/display/manager/chromeos/default_touch_transform_setter.h b/chromium/ui/display/manager/chromeos/default_touch_transform_setter.h index aa451f750ae..7f64f27c10e 100644 --- a/chromium/ui/display/manager/chromeos/default_touch_transform_setter.h +++ b/chromium/ui/display/manager/chromeos/default_touch_transform_setter.h @@ -18,8 +18,7 @@ class DISPLAY_MANAGER_EXPORT DefaultTouchTransformSetter // TouchTransformSetter: void ConfigureTouchDevices( - const std::map<int32_t, double>& scales, - const std::vector<TouchDeviceTransform>& transforms) override; + const std::vector<ui::TouchDeviceTransform>& transforms) override; private: DISALLOW_COPY_AND_ASSIGN(DefaultTouchTransformSetter); diff --git a/chromium/ui/display/manager/chromeos/display_change_observer.cc b/chromium/ui/display/manager/chromeos/display_change_observer.cc index 392598069bb..de95a0090c4 100644 --- a/chromium/ui/display/manager/chromeos/display_change_observer.cc +++ b/chromium/ui/display/manager/chromeos/display_change_observer.cc @@ -54,17 +54,6 @@ const int kMinimumWidthFor4K = 3840; // available in external large monitors. const float kAdditionalDeviceScaleFactorsFor4k[] = {1.25f, 2.0f}; -void UpdateInternalDisplayId( - const DisplayConfigurator::DisplayStateList& display_states) { - for (auto* state : display_states) { - if (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) { - if (Display::HasInternalDisplay()) - DCHECK_EQ(Display::InternalDisplayId(), state->display_id()); - Display::SetInternalDisplayId(state->display_id()); - } - } -} - } // namespace // static @@ -152,8 +141,8 @@ DisplayChangeObserver::~DisplayChangeObserver() { } MultipleDisplayState DisplayChangeObserver::GetStateForDisplayIds( - const DisplayConfigurator::DisplayStateList& display_states) const { - UpdateInternalDisplayId(display_states); + const DisplayConfigurator::DisplayStateList& display_states) { + UpdateInternalDisplay(display_states); if (display_states.size() == 1) return MULTIPLE_DISPLAY_STATE_SINGLE; DisplayIdList list = @@ -180,78 +169,15 @@ bool DisplayChangeObserver::GetResolutionForDisplayId(int64_t display_id, void DisplayChangeObserver::OnDisplayModeChanged( const DisplayConfigurator::DisplayStateList& display_states) { - UpdateInternalDisplayId(display_states); + UpdateInternalDisplay(display_states); std::vector<ManagedDisplayInfo> displays; - std::set<int64_t> ids; for (const DisplaySnapshot* state : display_states) { const DisplayMode* mode_info = state->current_mode(); if (!mode_info) continue; - float device_scale_factor = 1.0f; - // Sets dpi only if the screen size is not blacklisted. - float dpi = IsDisplaySizeBlackListed(state->physical_size()) - ? 0 - : kInchInMm * mode_info->size().width() / - state->physical_size().width(); - if (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) { - if (dpi) - device_scale_factor = FindDeviceScaleFactor(dpi); - } else { - scoped_refptr<ManagedDisplayMode> mode = - display_manager_->GetSelectedModeForDisplayId(state->display_id()); - if (mode) { - device_scale_factor = mode->device_scale_factor(); - } else { - // For monitors that are 40 inches and 4K or above, set - // |device_scale_factor| to 2x. For margin purposes, 100 is subtracted - // 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()); - if (size_in_vec.LengthSquared() > k2xThreshouldSizeSquaredFor4KInMm && - mode_info->size().width() >= kMinimumWidthFor4K) { - // Make sure that additional device scale factors table has 2x. - DCHECK_EQ(2.0f, kAdditionalDeviceScaleFactorsFor4k[1]); - device_scale_factor = 2.0f; - } - } - } - gfx::Rect display_bounds(state->origin(), mode_info->size()); - - std::string name = (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) - ? l10n_util::GetStringUTF8(IDS_DISPLAY_NAME_INTERNAL) - : state->display_name(); - - if (name.empty()) - name = l10n_util::GetStringUTF8(IDS_DISPLAY_NAME_UNKNOWN); - - bool has_overscan = state->has_overscan(); - int64_t id = state->display_id(); - ids.insert(id); - - displays.push_back(ManagedDisplayInfo(id, name, has_overscan)); - ManagedDisplayInfo& new_info = displays.back(); - new_info.set_sys_path(state->sys_path()); - new_info.set_device_scale_factor(device_scale_factor); - new_info.SetBounds(display_bounds); - new_info.set_native(true); - new_info.set_is_aspect_preserving_scaling( - state->is_aspect_preserving_scaling()); - if (dpi) - new_info.set_device_dpi(dpi); - - ManagedDisplayInfo::ManagedDisplayModeList display_modes = - (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) - ? GetInternalManagedDisplayModeList(new_info, *state) - : GetExternalManagedDisplayModeList(*state); - new_info.SetManagedDisplayModes(display_modes); - - new_info.set_available_color_profiles( - display_configurator_->GetAvailableColorCalibrationProfiles(id)); - new_info.set_maximum_cursor_size(state->maximum_cursor_size()); + displays.emplace_back(CreateManagedDisplayInfo(state, mode_info)); } AssociateTouchscreens( @@ -288,6 +214,97 @@ void DisplayChangeObserver::OnTouchscreenDeviceConfigurationChanged() { OnDisplayModeChanged(cached_displays); } +void DisplayChangeObserver::UpdateInternalDisplay( + const DisplayConfigurator::DisplayStateList& display_states) { + for (auto* state : display_states) { + if (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) { + if (Display::HasInternalDisplay()) + DCHECK_EQ(Display::InternalDisplayId(), state->display_id()); + Display::SetInternalDisplayId(state->display_id()); + + if (state->native_mode() && + (!display_manager_->IsDisplayIdValid(state->display_id()) || + !state->current_mode())) { + // Register the internal display info if + // 1) If it's not already registered. It'll be treated as + // new display in |UpdateDisplaysWith()|. + // 2) If it's not connected, because the display info will not + // be updated in |UpdateDisplaysWith()|, which will skips the + // disconnected displays. + ManagedDisplayInfo new_info = + CreateManagedDisplayInfo(state, state->native_mode()); + display_manager_->UpdateInternalDisplay(new_info); + } + } + } +} + +ManagedDisplayInfo DisplayChangeObserver::CreateManagedDisplayInfo( + const DisplaySnapshot* state, + 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()) + ? 0 + : kInchInMm * mode_info->size().width() / + state->physical_size().width(); + + if (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) { + if (dpi) + device_scale_factor = FindDeviceScaleFactor(dpi); + } else { + scoped_refptr<ManagedDisplayMode> mode = + display_manager_->GetSelectedModeForDisplayId(state->display_id()); + if (mode) { + device_scale_factor = mode->device_scale_factor(); + } else { + // For monitors that are 40 inches and 4K or above, set + // |device_scale_factor| to 2x. For margin purposes, 100 is subtracted + // 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()); + if (size_in_vec.LengthSquared() > k2xThreshouldSizeSquaredFor4KInMm && + mode_info->size().width() >= kMinimumWidthFor4K) { + // Make sure that additional device scale factors table has 2x. + DCHECK_EQ(2.0f, kAdditionalDeviceScaleFactorsFor4k[1]); + device_scale_factor = 2.0f; + } + } + } + + std::string name = (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) + ? l10n_util::GetStringUTF8(IDS_DISPLAY_NAME_INTERNAL) + : state->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(); + + ManagedDisplayInfo new_info = ManagedDisplayInfo(id, name, has_overscan); + new_info.set_sys_path(state->sys_path()); + new_info.set_device_scale_factor(device_scale_factor); + const gfx::Rect display_bounds(state->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()); + if (dpi) + new_info.set_device_dpi(dpi); + + ManagedDisplayInfo::ManagedDisplayModeList display_modes = + (state->type() == DISPLAY_CONNECTION_TYPE_INTERNAL) + ? GetInternalManagedDisplayModeList(new_info, *state) + : GetExternalManagedDisplayModeList(*state); + new_info.SetManagedDisplayModes(display_modes); + + new_info.set_maximum_cursor_size(state->maximum_cursor_size()); + return new_info; +} + // static float DisplayChangeObserver::FindDeviceScaleFactor(float dpi) { for (size_t i = 0; i < arraysize(kThresholdTableForInternal); ++i) { diff --git a/chromium/ui/display/manager/chromeos/display_change_observer.h b/chromium/ui/display/manager/chromeos/display_change_observer.h index d9468f9973b..8ad853d4e40 100644 --- a/chromium/ui/display/manager/chromeos/display_change_observer.h +++ b/chromium/ui/display/manager/chromeos/display_change_observer.h @@ -43,7 +43,7 @@ class DISPLAY_MANAGER_EXPORT DisplayChangeObserver // DisplayConfigurator::StateController overrides: MultipleDisplayState GetStateForDisplayIds( - const DisplayConfigurator::DisplayStateList& outputs) const override; + const DisplayConfigurator::DisplayStateList& outputs) override; bool GetResolutionForDisplayId(int64_t display_id, gfx::Size* size) const override; @@ -61,6 +61,12 @@ class DISPLAY_MANAGER_EXPORT DisplayChangeObserver DISPLAY_EXPORT static float FindDeviceScaleFactor(float dpi); private: + void UpdateInternalDisplay( + const DisplayConfigurator::DisplayStateList& display_states); + + ManagedDisplayInfo CreateManagedDisplayInfo(const DisplaySnapshot* state, + const DisplayMode* mode_info); + // Both |display_configurator_| and |display_manager_| are not owned and must // outlive DisplayChangeObserver. DisplayConfigurator* display_configurator_; 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 cf74f786ea9..dbf22a33ab4 100644 --- a/chromium/ui/display/manager/chromeos/display_change_observer_unittest.cc +++ b/chromium/ui/display/manager/chromeos/display_change_observer_unittest.cc @@ -93,8 +93,7 @@ TEST(DisplayChangeObserverTest, GetExternalManagedDisplayModeList) { TEST(DisplayChangeObserverTest, GetEmptyExternalManagedDisplayModeList) { FakeDisplaySnapshot display_snapshot( 123, gfx::Point(), gfx::Size(), DISPLAY_CONNECTION_TYPE_UNKNOWN, false, - false, false, std::string(), 0, - std::vector<std::unique_ptr<const DisplayMode>>(), nullptr, nullptr); + false, false, std::string(), {}, nullptr, nullptr, 0, gfx::Size()); ManagedDisplayInfo::ManagedDisplayModeList display_modes = DisplayChangeObserver::GetExternalManagedDisplayModeList( diff --git a/chromium/ui/display/manager/chromeos/display_configurator.cc b/chromium/ui/display/manager/chromeos/display_configurator.cc index 163ecac3067..088cc973d0b 100644 --- a/chromium/ui/display/manager/chromeos/display_configurator.cc +++ b/chromium/ui/display/manager/chromeos/display_configurator.cc @@ -72,19 +72,19 @@ base::TimeDelta DisplayConfigurator::TestApi::GetConfigureDelay() const { class DisplayConfigurator::DisplayLayoutManagerImpl : public DisplayLayoutManager { public: - DisplayLayoutManagerImpl(DisplayConfigurator* configurator); + explicit DisplayLayoutManagerImpl(DisplayConfigurator* configurator); ~DisplayLayoutManagerImpl() override; - // DisplayConfigurator::DisplayLayoutManager: + // DisplayLayoutManager: SoftwareMirroringController* GetSoftwareMirroringController() const override; StateController* GetStateController() const override; MultipleDisplayState GetDisplayState() const override; chromeos::DisplayPowerState GetPowerState() const override; - bool GetDisplayLayout(const std::vector<DisplaySnapshot*>& displays, - MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - std::vector<DisplayConfigureRequest>* requests, - gfx::Size* framebuffer_size) const override; + bool GetDisplayLayout( + const std::vector<DisplaySnapshot*>& displays, + MultipleDisplayState new_display_state, + chromeos::DisplayPowerState new_power_state, + std::vector<DisplayConfigureRequest>* requests) const override; DisplayStateList GetDisplayStates() const override; bool IsMirroring() const override; @@ -204,8 +204,7 @@ bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout( const std::vector<DisplaySnapshot*>& displays, MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, - std::vector<DisplayConfigureRequest>* requests, - gfx::Size* framebuffer_size) const { + std::vector<DisplayConfigureRequest>* requests) const { std::vector<DisplayState> states = ParseDisplays(displays); std::vector<bool> display_power; int num_on_displays = @@ -328,7 +327,6 @@ bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout( } DCHECK(new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS || !size.IsEmpty()); - *framebuffer_size = size; return true; } @@ -410,8 +408,6 @@ bool DisplayConfigurator::DisplayLayoutManagerImpl::FindMirrorMode( external_mode->size().height() && !external_mode->is_interlaced(); if (can_fit) { - configurator_->native_display_delegate_->AddMode( - *internal_display->display, external_mode.get()); internal_display->display->add_mode(external_mode.get()); internal_display->mirror_mode = internal_display->display->modes().back().get(); @@ -622,8 +618,7 @@ void DisplayConfigurator::OnDisplayControlRelinquished( std::move(callback).Run(success); } -void DisplayConfigurator::ForceInitialConfigure( - uint32_t background_color_argb) { +void DisplayConfigurator::ForceInitialConfigure() { if (!configure_display_ || display_externally_controlled_) return; @@ -637,7 +632,7 @@ void DisplayConfigurator::ForceInitialConfigure( configuration_task_.reset(new UpdateDisplayConfigurationTask( native_display_delegate_.get(), layout_manager_.get(), requested_display_state_, requested_power_state_, - kSetDisplayPowerForceProbe, background_color_argb, true, + kSetDisplayPowerForceProbe, true, base::Bind(&DisplayConfigurator::OnConfigured, weak_ptr_factory_.GetWeakPtr()))); configuration_task_->Run(); @@ -812,36 +807,6 @@ void DisplayConfigurator::OnSetContentProtectionCompleted( content_protection_tasks_.front().Run(); } -std::vector<ColorCalibrationProfile> -DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) { - if (!base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableDisplayColorCalibration)) { - for (const DisplaySnapshot* display : cached_displays_) { - if (display->display_id() == display_id && - IsPhysicalDisplayType(display->type())) { - return native_display_delegate_->GetAvailableColorCalibrationProfiles( - *display); - } - } - } - - return std::vector<ColorCalibrationProfile>(); -} - -bool DisplayConfigurator::SetColorCalibrationProfile( - int64_t display_id, - ColorCalibrationProfile new_profile) { - for (const DisplaySnapshot* display : cached_displays_) { - if (display->display_id() == display_id && - IsPhysicalDisplayType(display->type())) { - return native_display_delegate_->SetColorCalibrationProfile(*display, - new_profile); - } - } - - return false; -} - bool DisplayConfigurator::SetColorCorrection( int64_t display_id, const std::vector<GammaRampRGBEntry>& degamma_lut, @@ -980,10 +945,6 @@ void DisplayConfigurator::SuspendDisplays( // SetDisplayPowerInternal so requested_power_state_ is maintained. SetDisplayPowerInternal(chromeos::DISPLAY_POWER_ALL_OFF, kSetDisplayPowerNoFlags, callback); - - // We need to make sure that the monitor configuration we just did actually - // completes before we return. - native_display_delegate_->SyncWithServer(); } void DisplayConfigurator::ResumeDisplays() { @@ -1035,9 +996,10 @@ void DisplayConfigurator::RunPendingConfiguration() { configuration_task_.reset(new UpdateDisplayConfigurationTask( native_display_delegate_.get(), layout_manager_.get(), - requested_display_state_, pending_power_state_, pending_power_flags_, 0, - force_configure_, base::Bind(&DisplayConfigurator::OnConfigured, - weak_ptr_factory_.GetWeakPtr()))); + requested_display_state_, pending_power_state_, pending_power_flags_, + force_configure_, + base::Bind(&DisplayConfigurator::OnConfigured, + weak_ptr_factory_.GetWeakPtr()))); // Reset the flags before running the task; otherwise it may end up scheduling // another configuration. @@ -1055,7 +1017,6 @@ void DisplayConfigurator::RunPendingConfiguration() { void DisplayConfigurator::OnConfigured( bool success, const std::vector<DisplaySnapshot*>& displays, - const gfx::Size& framebuffer_size, MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state) { VLOG(1) << "OnConfigured: success=" << success << " new_display_state=" @@ -1068,17 +1029,6 @@ void DisplayConfigurator::OnConfigured( current_display_state_ = new_display_state; current_power_state_ = new_power_state; - // |framebuffer_size| is empty in software mirroring mode, headless mode, - // or all displays are off. - DCHECK(!framebuffer_size.IsEmpty() || - (mirroring_controller_ && - mirroring_controller_->SoftwareMirroringEnabled()) || - new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS || - new_power_state == chromeos::DISPLAY_POWER_ALL_OFF); - - if (!framebuffer_size.IsEmpty()) - framebuffer_size_ = framebuffer_size; - // 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 diff --git a/chromium/ui/display/manager/chromeos/display_configurator.h b/chromium/ui/display/manager/chromeos/display_configurator.h index 3a7bd2394a9..822dfbc5668 100644 --- a/chromium/ui/display/manager/chromeos/display_configurator.h +++ b/chromium/ui/display/manager/chromeos/display_configurator.h @@ -95,7 +95,7 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator // Called when displays are detected. virtual MultipleDisplayState GetStateForDisplayIds( - const DisplayConfigurator::DisplayStateList& outputs) const = 0; + const DisplayConfigurator::DisplayStateList& outputs) = 0; // Queries the resolution (|size|) in pixels to select display mode for the // given display id. @@ -116,7 +116,8 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator // Helper class used by tests. class TestApi { public: - TestApi(DisplayConfigurator* configurator) : configurator_(configurator) {} + explicit TestApi(DisplayConfigurator* configurator) + : configurator_(configurator) {} ~TestApi() {} // If |configure_timer_| is started, stops the timer, runs @@ -173,7 +174,6 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator chromeos::DisplayPowerState requested_power_state() const { return requested_power_state_; } - const gfx::Size framebuffer_size() const { return framebuffer_size_; } const std::vector<DisplaySnapshot*>& cached_displays() const { return cached_displays_; } @@ -212,10 +212,7 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator bool is_panel_fitting_enabled); // Does initial configuration of displays during startup. - // If |background_color_argb| is non zero and there are multiple displays, - // DisplayConfigurator sets the background color of X's RootWindow to this - // color. - void ForceInitialConfigure(uint32_t background_color_argb); + void ForceInitialConfigure(); // Stop handling display configuration events/requests. void PrepareForExit(); @@ -274,15 +271,6 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator uint32_t protection_mask, const SetProtectionCallback& callback); - // Checks the available color profiles for |display_id| and fills the result - // into |profiles|. - std::vector<ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( - int64_t display_id); - - // Updates the color calibration to |new_profile|. - bool SetColorCalibrationProfile(int64_t display_id, - ColorCalibrationProfile new_profile); - // Returns true if there is at least one display on. bool IsDisplayOn() const; @@ -331,7 +319,6 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator // this is called with the result (|success|) and the updated display state. void OnConfigured(bool success, const std::vector<DisplaySnapshot*>& displays, - const gfx::Size& framebuffer_size, MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state); @@ -381,11 +368,8 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator bool is_panel_fitting_enabled_; // This is detected by the constructor to determine whether or not we should - // be enabled. If we aren't running on Chrome OS, we can't assume that the - // Xrandr X11 extension or the Ozone underlying display hotplug system are - // supported. - // If this flag is set to false, any attempts to change the display - // configuration to immediately fail without changing the state. + // be enabled. If this flag is set to false, any attempts to change the + // display configuration will immediately fail without changing the state. bool configure_display_; // Current configuration state. @@ -431,14 +415,10 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator // configuration changes asynchronously. DisplayStateList cached_displays_; - // Most-recently-used framebuffer size. - gfx::Size framebuffer_size_; - base::ObserverList<Observer> observers_; // The timer to delay configuring displays. This is used to aggregate multiple // display configuration events when they are reported in short time spans. - // See comment for NativeDisplayEventDispatcherX11 for more details. base::OneShotTimer configure_timer_; // Id for next display protection client. diff --git a/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc b/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc index a366630ab85..fd18131ca47 100644 --- a/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc +++ b/chromium/ui/display/manager/chromeos/display_configurator_unittest.cc @@ -22,7 +22,7 @@ namespace test { namespace { -int64_t kDisplayIds[3] = {123, 456, 789}; +constexpr int64_t kDisplayIds[3] = {123, 456, 789}; std::unique_ptr<DisplayMode> MakeDisplayMode(int width, int height, @@ -108,7 +108,7 @@ class TestStateController : public DisplayConfigurator::StateController { // DisplayConfigurator::StateController overrides: MultipleDisplayState GetStateForDisplayIds( - const DisplayConfigurator::DisplayStateList& outputs) const override { + const DisplayConfigurator::DisplayStateList& outputs) override { return state_; } bool GetResolutionForDisplayId(int64_t display_id, @@ -146,7 +146,7 @@ class TestMirroringController // the time it took to complete. class ConfigurationWaiter { public: - ConfigurationWaiter(DisplayConfigurator::TestApi* test_api) + explicit ConfigurationWaiter(DisplayConfigurator::TestApi* test_api) : on_configured_callback_(base::Bind(&ConfigurationWaiter::OnConfigured, base::Unretained(this))), test_api_(test_api), @@ -303,14 +303,12 @@ class DisplayConfiguratorTest : public testing::Test { configurator_.Init(nullptr, false); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); EXPECT_EQ( JoinActions( - kInitXRandR, kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), + kInit, GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -420,21 +418,15 @@ TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) { observer_.Reset(); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED); UpdateOutputs(2, true); - const int kDualHeight = small_mode_.size().height() + - DisplayConfigurator::kVerticalGap + - big_mode_.size().height(); + EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); EXPECT_EQ(1, observer_.num_changes()); @@ -443,12 +435,9 @@ TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) { configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); EXPECT_EQ(1, observer_.num_changes()); @@ -458,11 +447,8 @@ TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) { UpdateOutputs(1, true); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); EXPECT_EQ(1, observer_.num_changes()); @@ -480,43 +466,35 @@ TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) { UpdateOutputs(2, true); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); - const gfx::Size framebuffer_size = configurator_.framebuffer_size(); - DCHECK(!framebuffer_size.IsEmpty()); observer_.Reset(); configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); - EXPECT_EQ(JoinActions(kGrab, kUngrab, nullptr), log_->GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, configurator_.display_state()); EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); - EXPECT_EQ(framebuffer_size.ToString(), - configurator_.framebuffer_size().ToString()); EXPECT_EQ(1, observer_.num_changes()); // Setting MULTIPLE_DISPLAY_STATE_DUAL_MIRROR should try to reconfigure. observer_.Reset(); configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED); - EXPECT_EQ(JoinActions(nullptr), log_->GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); EXPECT_EQ(1, observer_.num_changes()); // Set back to software mirror mode. observer_.Reset(); configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); - EXPECT_EQ(JoinActions(kGrab, kUngrab, nullptr), log_->GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, configurator_.display_state()); EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled()); @@ -527,11 +505,8 @@ TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) { UpdateOutputs(1, true); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); EXPECT_EQ(1, observer_.num_changes()); @@ -545,12 +520,9 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { UpdateOutputs(2, true); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); EXPECT_EQ(1, observer_.num_changes()); @@ -567,12 +539,9 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(big_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); EXPECT_EQ(1, observer_.num_changes()); @@ -586,15 +555,11 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ( - JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ(JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), + nullptr), + log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); EXPECT_EQ(1, observer_.num_changes()); @@ -609,12 +574,9 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); @@ -632,21 +594,15 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); observer_.Reset(); UpdateOutputs(2, true); - const int kDualHeight = small_mode_.size().height() + - DisplayConfigurator::kVerticalGap + - big_mode_.size().height(); + EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, configurator_.display_state()); @@ -665,12 +621,9 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(big_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled()); @@ -687,16 +640,12 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, configurator_.display_state()); @@ -713,16 +662,12 @@ TEST_F(DisplayConfiguratorTest, SetDisplayPower) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, configurator_.display_state()); @@ -736,21 +681,15 @@ TEST_F(DisplayConfiguratorTest, SuspendAndResume) { // 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. - const gfx::Size framebuffer_size = configurator_.framebuffer_size(); - DCHECK(!framebuffer_size.IsEmpty()); config_waiter_.Reset(); configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ(framebuffer_size.ToString(), - configurator_.framebuffer_size().ToString()); - EXPECT_EQ(JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), - outputs_[0].get(), nullptr) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, kSync, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ( + JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + nullptr), + log_->GetActionsAndClear()); // No resume delay in single display mode. config_waiter_.Reset(); @@ -759,11 +698,8 @@ TEST_F(DisplayConfiguratorTest, SuspendAndResume) { EXPECT_EQ(base::TimeDelta::Max(), config_waiter_.Wait()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Now turn the display off before suspending and check that the @@ -774,19 +710,17 @@ TEST_F(DisplayConfiguratorTest, SuspendAndResume) { config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ(JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), - outputs_[0].get(), nullptr) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ( + JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + nullptr), + log_->GetActionsAndClear()); config_waiter_.Reset(); configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ(kSync, log_->GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); config_waiter_.Reset(); configurator_.ResumeDisplays(); @@ -802,23 +736,17 @@ TEST_F(DisplayConfiguratorTest, SuspendAndResume) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); UpdateOutputs(2, true); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); config_waiter_.Reset(); @@ -828,15 +756,11 @@ TEST_F(DisplayConfiguratorTest, SuspendAndResume) { EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); - EXPECT_EQ( - JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ(JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), + nullptr), + log_->GetActionsAndClear()); // No delay in suspend. config_waiter_.Reset(); @@ -846,7 +770,7 @@ TEST_F(DisplayConfiguratorTest, SuspendAndResume) { EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_OFF, configurator_.current_power_state()); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); - EXPECT_EQ(kSync, log_->GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); // If a display is disconnected while suspended, the configurator should // pick up the change and only turn on the internal display. The should be @@ -867,11 +791,8 @@ TEST_F(DisplayConfiguratorTest, SuspendAndResume) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -880,9 +801,8 @@ TEST_F(DisplayConfiguratorTest, Headless) { EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); Init(false); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); - configurator_.ForceInitialConfigure(0); - EXPECT_EQ(JoinActions(kInitXRandR, kGrab, kForceDPMS, kUngrab, nullptr), - log_->GetActionsAndClear()); + configurator_.ForceInitialConfigure(); + EXPECT_EQ(JoinActions(kInit, nullptr), log_->GetActionsAndClear()); // Not much should happen when the display power state is changed while // no displays are connected. @@ -892,15 +812,14 @@ TEST_F(DisplayConfiguratorTest, Headless) { config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ(JoinActions(kGrab, kUngrab, nullptr), log_->GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); config_waiter_.Reset(); configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON, DisplayConfigurator::kSetDisplayPowerNoFlags, config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ(JoinActions(kGrab, kForceDPMS, kUngrab, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); // Connect an external display and check that it's configured correctly. outputs_[0] = FakeDisplaySnapshot::Builder() @@ -915,19 +834,12 @@ TEST_F(DisplayConfiguratorTest, Headless) { UpdateOutputs(1, true); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(big_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &big_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); - const gfx::Size framebuffer_size = configurator_.framebuffer_size(); - DCHECK(!framebuffer_size.IsEmpty()); UpdateOutputs(0, true); - EXPECT_EQ(JoinActions(kGrab, kUngrab, nullptr), log_->GetActionsAndClear()); - EXPECT_EQ(framebuffer_size.ToString(), - configurator_.framebuffer_size().ToString()); + EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); } TEST_F(DisplayConfiguratorTest, StartWithTwoOutputs) { @@ -937,16 +849,13 @@ TEST_F(DisplayConfiguratorTest, StartWithTwoOutputs) { EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); EXPECT_EQ( JoinActions( - kInitXRandR, kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), + kInit, GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -954,7 +863,7 @@ TEST_F(DisplayConfiguratorTest, InvalidMultipleDisplayStates) { UpdateOutputs(0, false); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); observer_.Reset(); configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_HEADLESS); EXPECT_EQ(1, observer_.num_changes()); @@ -995,7 +904,7 @@ TEST_F(DisplayConfiguratorTest, GetMultipleDisplayStateForMirroredDisplays) { UpdateOutputs(2, false); Init(false); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); } @@ -1039,18 +948,14 @@ TEST_F(DisplayConfiguratorTest, PanelFitting) { UpdateOutputs(2, false); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); Init(true /* is_panel_fitting_enabled */); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state()); EXPECT_EQ( JoinActions( - kInitXRandR, kGrab, - GetAddOutputModeAction(*outputs_[0], &small_mode_).c_str(), - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), + kInit, GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Both outputs should be using the small mode. @@ -1069,7 +974,7 @@ TEST_F(DisplayConfiguratorTest, PanelFitting) { TEST_F(DisplayConfiguratorTest, ContentProtection) { Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); EXPECT_NE(kNoActions, log_->GetActionsAndClear()); uint64_t id = configurator_.RegisterContentProtectionClient(); @@ -1145,13 +1050,11 @@ TEST_F(DisplayConfiguratorTest, DoNotConfigureWithSuspendedDisplays) { configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ(JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), - outputs_[0].get(), nullptr) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, kSync, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ( + JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + nullptr), + log_->GetActionsAndClear()); // The configuration timer should not be started when the displays // are suspended. @@ -1176,23 +1079,17 @@ TEST_F(DisplayConfiguratorTest, DoNotConfigureWithSuspendedDisplays) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); UpdateOutputs(2, false); configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // The DisplayConfigurator should do nothing at resume time if there is no @@ -1210,13 +1107,11 @@ TEST_F(DisplayConfiguratorTest, DoNotConfigureWithSuspendedDisplays) { configurator_.SuspendDisplays(config_waiter_.on_configuration_callback()); EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); - EXPECT_EQ(JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), - outputs_[0].get(), nullptr) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, kSync, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ( + JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + nullptr), + log_->GetActionsAndClear()); EXPECT_FALSE(test_api_.TriggerConfigureTimeout()); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); @@ -1226,11 +1121,8 @@ TEST_F(DisplayConfiguratorTest, DoNotConfigureWithSuspendedDisplays) { EXPECT_EQ(base::TimeDelta::Max(), config_waiter_.Wait()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1240,7 +1132,7 @@ TEST_F(DisplayConfiguratorTest, ContentProtectionTwoClients) { EXPECT_NE(client1, client2); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); UpdateOutputs(2, true); EXPECT_NE(kNoActions, log_->GetActionsAndClear()); @@ -1302,7 +1194,7 @@ TEST_F(DisplayConfiguratorTest, ContentProtectionTwoClientsEnable) { EXPECT_NE(client1, client2); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); UpdateOutputs(2, true); log_->GetActionsAndClear(); @@ -1375,13 +1267,10 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(big_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[0], modes[3].get(), gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[0], modes[2].get(), gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); outputs_[1] = FakeDisplaySnapshot::Builder() @@ -1405,9 +1294,6 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(modes[0]->size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(), // Then attempt to configure crtc1 with the first mode. GetCrtcAction(*outputs_[1], modes[0].get(), gfx::Point(0, 0)).c_str(), @@ -1419,12 +1305,6 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { // and the configured modes were different, it // should now try and setup a valid configurable // extended mode. - GetFramebufferAction( - gfx::Size(modes[0]->size().width(), - modes[0]->size().height() + modes[0]->size().height() + - DisplayConfigurator::kVerticalGap), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], modes[0].get(), gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], modes[0].get(), gfx::Point(0, modes[0]->size().height() + @@ -1435,7 +1315,7 @@ TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) { gfx::Point(0, modes[0]->size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1445,7 +1325,7 @@ TEST_F(DisplayConfiguratorTest, SaveDisplayPowerStateOnConfigFailure) { // Start out with two displays in extended mode. state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); log_->GetActionsAndClear(); observer_.Reset(); @@ -1481,11 +1361,8 @@ TEST_F(DisplayConfiguratorTest, SaveDisplayPowerStateOnConfigFailure) { UpdateOutputs(1, true); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1496,7 +1373,7 @@ TEST_F(DisplayConfiguratorTest, DontRestoreStalePowerStateAfterResume) { // Start out with two displays in mirrored mode. state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); log_->GetActionsAndClear(); observer_.Reset(); @@ -1511,12 +1388,9 @@ TEST_F(DisplayConfiguratorTest, DontRestoreStalePowerStateAfterResume) { EXPECT_EQ(0, observer_.num_failures()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(big_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Suspend and resume the system. Resuming should restore the previous power @@ -1527,15 +1401,11 @@ TEST_F(DisplayConfiguratorTest, DontRestoreStalePowerStateAfterResume) { EXPECT_EQ(kNoDelay, config_waiter_.Wait()); EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ(2, observer_.num_changes()); - EXPECT_EQ( - JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, kSync, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ(JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, 0)).c_str(), + nullptr), + log_->GetActionsAndClear()); // Before the task runs, exit docked mode. config_waiter_.Reset(); @@ -1548,12 +1418,9 @@ TEST_F(DisplayConfiguratorTest, DontRestoreStalePowerStateAfterResume) { EXPECT_EQ(0, observer_.num_failures()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Check that the display states are not changed after resuming. @@ -1569,12 +1436,9 @@ TEST_F(DisplayConfiguratorTest, DontRestoreStalePowerStateAfterResume) { EXPECT_EQ(kLongDelay, config_waiter_.Wait()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), outputs_[0].get(), - outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1585,24 +1449,20 @@ TEST_F(DisplayConfiguratorTest, ExternalControl) { base::Bind(&DisplayConfiguratorTest::OnDisplayControlUpdated, base::Unretained(this))); EXPECT_EQ(CALLBACK_SUCCESS, PopDisplayControlResult()); - EXPECT_EQ(JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), - outputs_[0].get(), nullptr) - .c_str(), - GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), - kUngrab, kRelinquishDisplayControl, nullptr), - log_->GetActionsAndClear()); + EXPECT_EQ( + JoinActions( + GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), + kRelinquishDisplayControl, nullptr), + log_->GetActionsAndClear()); configurator_.TakeControl( base::Bind(&DisplayConfiguratorTest::OnDisplayControlUpdated, base::Unretained(this))); EXPECT_EQ(CALLBACK_SUCCESS, PopDisplayControlResult()); EXPECT_EQ( JoinActions( - kTakeDisplayControl, kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), + kTakeDisplayControl, GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1611,7 +1471,7 @@ TEST_F(DisplayConfiguratorTest, // Start out with two displays in extended mode. state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); log_->GetActionsAndClear(); observer_.Reset(); @@ -1633,21 +1493,14 @@ TEST_F(DisplayConfiguratorTest, EXPECT_EQ(1, observer_.num_changes()); EXPECT_EQ(0, observer_.num_failures()); - const int kDualHeight = small_mode_.size().height() + - DisplayConfigurator::kVerticalGap + - big_mode_.size().height(); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); config_waiter_.Reset(); @@ -1662,16 +1515,12 @@ TEST_F(DisplayConfiguratorTest, EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1680,7 +1529,7 @@ TEST_F(DisplayConfiguratorTest, // Start out with two displays in extended mode. state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); log_->GetActionsAndClear(); observer_.Reset(); @@ -1696,22 +1545,14 @@ TEST_F(DisplayConfiguratorTest, EXPECT_EQ(0, observer_.num_changes()); EXPECT_EQ(1, observer_.num_failures()); - const int kDualHeight = small_mode_.size().height() + - DisplayConfigurator::kVerticalGap + - big_mode_.size().height(); - EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // This configuration should trigger a display configuration since the @@ -1726,10 +1567,6 @@ TEST_F(DisplayConfiguratorTest, EXPECT_EQ(2, observer_.num_failures()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + @@ -1739,7 +1576,7 @@ TEST_F(DisplayConfiguratorTest, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Allow configuration to succeed. @@ -1755,16 +1592,12 @@ TEST_F(DisplayConfiguratorTest, EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1772,25 +1605,15 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { // Start out with two displays in extended mode. state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED); Init(false); - configurator_.ForceInitialConfigure(0); + configurator_.ForceInitialConfigure(); log_->GetActionsAndClear(); observer_.Reset(); UpdateOutputs(3, true); state_controller_.set_state(MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED); - const int kDualHeight = small_mode_.size().height() + - DisplayConfigurator::kVerticalGap + - big_mode_.size().height(); - const int kTripleHeight = 2 * small_mode_.size().height() + - 2 * DisplayConfigurator::kVerticalGap + - big_mode_.size().height(); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction( - gfx::Size(big_mode_.size().width(), kTripleHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + @@ -1802,7 +1625,7 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { big_mode_.size().height() + 2 * DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Verify that turning the power off works. @@ -1814,10 +1637,6 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction( - gfx::Size(big_mode_.size().width(), kTripleHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, small_mode_.size().height() + @@ -1829,7 +1648,7 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { big_mode_.size().height() + 2 * DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); config_waiter_.Reset(); @@ -1840,10 +1659,6 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { EXPECT_EQ(CALLBACK_SUCCESS, config_waiter_.callback_result()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction( - gfx::Size(big_mode_.size().width(), kTripleHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + @@ -1855,7 +1670,7 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { big_mode_.size().height() + 2 * DisplayConfigurator::kVerticalGap)) .c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Disconnect the third output. @@ -1864,16 +1679,12 @@ TEST_F(DisplayConfiguratorTest, TestWithThreeDisplays) { UpdateOutputs(2, true); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); } @@ -1889,21 +1700,15 @@ TEST_F(DisplayConfiguratorTest, SuspendResumeWithMultipleDisplays) { EXPECT_EQ(1, observer_.num_changes()); EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, configurator_.current_power_state()); - const int kDualHeight = small_mode_.size().height() + - DisplayConfigurator::kVerticalGap + - big_mode_.size().height(); + EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Suspending displays should result in an immediate configuration without @@ -1918,16 +1723,12 @@ TEST_F(DisplayConfiguratorTest, SuspendResumeWithMultipleDisplays) { configurator_.display_state()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, kSync, nullptr), + nullptr), log_->GetActionsAndClear()); // Resuming from suspend with dual displays. Configuration should be done @@ -1942,16 +1743,12 @@ TEST_F(DisplayConfiguratorTest, SuspendResumeWithMultipleDisplays) { configurator_.display_state()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], &big_mode_, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Suspend displays and disconnect one of them while in suspend. @@ -1965,16 +1762,12 @@ TEST_F(DisplayConfiguratorTest, SuspendResumeWithMultipleDisplays) { configurator_.current_power_state()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight), - outputs_[0].get(), outputs_[1].get()) - .c_str(), GetCrtcAction(*outputs_[0], nullptr, gfx::Point(0, 0)).c_str(), GetCrtcAction(*outputs_[1], nullptr, gfx::Point(0, small_mode_.size().height() + DisplayConfigurator::kVerticalGap)) .c_str(), - kUngrab, kSync, nullptr), + nullptr), log_->GetActionsAndClear()); UpdateOutputs(1, false); EXPECT_EQ(kNoActions, log_->GetActionsAndClear()); @@ -1990,11 +1783,8 @@ TEST_F(DisplayConfiguratorTest, SuspendResumeWithMultipleDisplays) { EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state()); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), outputs_[0].get(), nullptr) - .c_str(), GetCrtcAction(*outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(), - kForceDPMS, kUngrab, nullptr), + nullptr), log_->GetActionsAndClear()); // Verify that the above is the exact same behavior for 3+ displays. diff --git a/chromium/ui/display/manager/chromeos/display_layout_manager.h b/chromium/ui/display/manager/chromeos/display_layout_manager.h index f6cb591007c..c43f2bbb00e 100644 --- a/chromium/ui/display/manager/chromeos/display_layout_manager.h +++ b/chromium/ui/display/manager/chromeos/display_layout_manager.h @@ -34,12 +34,12 @@ class DisplayLayoutManager { // Based on the given |displays|, display state and power state, it will // create display configuration requests which will then be used to // configure the hardware. The requested configuration is stored in - // |requests| and |framebuffer_size|. - virtual bool GetDisplayLayout(const std::vector<DisplaySnapshot*>& displays, - MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - std::vector<DisplayConfigureRequest>* requests, - gfx::Size* framebuffer_size) const = 0; + // |requests|. + virtual bool GetDisplayLayout( + const std::vector<DisplaySnapshot*>& displays, + MultipleDisplayState new_display_state, + chromeos::DisplayPowerState new_power_state, + std::vector<DisplayConfigureRequest>* requests) const = 0; virtual std::vector<DisplaySnapshot*> GetDisplayStates() const = 0; diff --git a/chromium/ui/display/manager/chromeos/mojo/BUILD.gn b/chromium/ui/display/manager/chromeos/mojo/BUILD.gn deleted file mode 100644 index e29b518ebcc..00000000000 --- a/chromium/ui/display/manager/chromeos/mojo/BUILD.gn +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//mojo/public/tools/bindings/mojom.gni") - -mojom("interfaces") { - sources = [ - "touch_device_transform.mojom", - ] - - public_deps = [ - "//ui/gfx/mojo", - ] -} diff --git a/chromium/ui/display/manager/chromeos/mojo/OWNERS b/chromium/ui/display/manager/chromeos/mojo/OWNERS deleted file mode 100644 index e75daf744a0..00000000000 --- a/chromium/ui/display/manager/chromeos/mojo/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -per-file *_struct_traits*.*=set noparent -per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS - -per-file *.mojom=set noparent -per-file *.mojom=file://ipc/SECURITY_OWNERS - -per-file *.typemap=set noparent -per-file *.typemap=file://ipc/SECURITY_OWNERS diff --git a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform.typemap b/chromium/ui/display/manager/chromeos/mojo/touch_device_transform.typemap deleted file mode 100644 index 3a0a96276af..00000000000 --- a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform.typemap +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -mojom = "//ui/display/manager/chromeos/mojo/touch_device_transform.mojom" -public_headers = [ "//ui/display/manager/chromeos/touch_device_transform.h" ] -traits_headers = [ "//ui/display/manager/chromeos/mojo/touch_device_transform_struct_traits.h" ] -deps = [ - "//ui/gfx/mojo:struct_traits", -] -public_deps = [ - "//ui/display/manager", -] -type_mappings = - [ "display.mojom.TouchDeviceTransform=display::TouchDeviceTransform" ] diff --git a/chromium/ui/display/manager/chromeos/mojo/typemaps.gni b/chromium/ui/display/manager/chromeos/mojo/typemaps.gni deleted file mode 100644 index 21a671bb583..00000000000 --- a/chromium/ui/display/manager/chromeos/mojo/typemaps.gni +++ /dev/null @@ -1,6 +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. - -typemaps = - [ "//ui/display/manager/chromeos/mojo/touch_device_transform.typemap" ] diff --git a/chromium/ui/display/manager/chromeos/touch_device_transform.h b/chromium/ui/display/manager/chromeos/touch_device_transform.h deleted file mode 100644 index c9eee51c9b0..00000000000 --- a/chromium/ui/display/manager/chromeos/touch_device_transform.h +++ /dev/null @@ -1,26 +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_DISPLAY_MANAGER_CHROMEOS_TOUCH_DEVICE_TRANSFORM_H_ -#define UI_DISPLAY_MANAGER_CHROMEOS_TOUCH_DEVICE_TRANSFORM_H_ - -#include <stdint.h> - -#include "ui/display/manager/display_manager_export.h" -#include "ui/gfx/transform.h" - -namespace display { - -struct DISPLAY_MANAGER_EXPORT TouchDeviceTransform { - TouchDeviceTransform(); - ~TouchDeviceTransform(); - - int64_t display_id; - int32_t device_id; - gfx::Transform transform; -}; - -} // namespace display - -#endif // UI_DISPLAY_MANAGER_CHROMEOS_TOUCH_DEVICE_TRANSFORM_H_ diff --git a/chromium/ui/display/manager/chromeos/touch_transform_controller.cc b/chromium/ui/display/manager/chromeos/touch_transform_controller.cc index a297d628ef9..3ed5de8929e 100644 --- a/chromium/ui/display/manager/chromeos/touch_transform_controller.cc +++ b/chromium/ui/display/manager/chromeos/touch_transform_controller.cc @@ -10,7 +10,6 @@ #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/display/display_layout.h" #include "ui/display/manager/chromeos/display_configurator.h" -#include "ui/display/manager/chromeos/touch_device_transform.h" #include "ui/display/manager/chromeos/touch_transform_setter.h" #include "ui/display/manager/display_manager.h" #include "ui/display/manager/managed_display_info.h" @@ -18,6 +17,7 @@ #include "ui/display/types/display_constants.h" #include "ui/display/types/display_snapshot.h" #include "ui/events/devices/input_device_manager.h" +#include "ui/events/devices/touch_device_transform.h" namespace display { @@ -197,16 +197,10 @@ double TouchTransformController::GetTouchResolutionScale( gfx::Transform TouchTransformController::GetTouchTransform( const ManagedDisplayInfo& display, const ManagedDisplayInfo& touch_display, - const ui::TouchscreenDevice& touchscreen, - const gfx::Size& framebuffer_size) const { + const ui::TouchscreenDevice& touchscreen) const { auto current_size = gfx::SizeF(display.bounds_in_native().size()); auto touch_native_size = gfx::SizeF(touch_display.GetNativeModeSize()); -#if defined(USE_OZONE) auto touch_area = gfx::SizeF(touchscreen.size); -#elif defined(USE_X11) - // On X11 touches are reported in the framebuffer coordinate space. - auto touch_area = gfx::SizeF(framebuffer_size); -#endif gfx::Transform ctm; @@ -214,14 +208,12 @@ gfx::Transform TouchTransformController::GetTouchTransform( touch_area.IsEmpty() || touchscreen.id == ui::InputDevice::kInvalidId) return ctm; -#if defined(USE_OZONE) // Translate the touch so that it falls within the display bounds. This // should not be performed if the displays are mirrored. if (display.id() == touch_display.id()) { ctm.Translate(display.bounds_in_native().x(), display.bounds_in_native().y()); } -#endif // If the device is currently under calibration, then do not return any // transform as we want to use the raw native touch input data for calibration @@ -300,8 +292,7 @@ TouchTransformController::~TouchTransformController() {} void TouchTransformController::UpdateTouchTransforms() const { UpdateData update_data; UpdateTouchTransforms(&update_data); - setter_->ConfigureTouchDevices(update_data.device_to_scale, - update_data.touch_device_transforms); + setter_->ConfigureTouchDevices(update_data.touch_device_transforms); } void TouchTransformController::UpdateTouchRadius( @@ -319,13 +310,15 @@ void TouchTransformController::UpdateTouchTransform( const ManagedDisplayInfo& touch_display, const ManagedDisplayInfo& target_display, UpdateData* update_data) const { - TouchDeviceTransform touch_device_transform; + ui::TouchDeviceTransform touch_device_transform; touch_device_transform.display_id = target_display_id; - gfx::Size fb_size = display_configurator_->framebuffer_size(); for (const auto& device_id : touch_display.input_devices()) { touch_device_transform.device_id = device_id; touch_device_transform.transform = GetTouchTransform( - target_display, touch_display, FindTouchscreenById(device_id), fb_size); + target_display, touch_display, FindTouchscreenById(device_id)); + auto device_to_scale_iter = update_data->device_to_scale.find(device_id); + if (device_to_scale_iter != update_data->device_to_scale.end()) + touch_device_transform.radius_scale = device_to_scale_iter->second; update_data->touch_device_transforms.push_back(touch_device_transform); } } diff --git a/chromium/ui/display/manager/chromeos/touch_transform_controller.h b/chromium/ui/display/manager/chromeos/touch_transform_controller.h index 69239bc5301..1d089e4e83b 100644 --- a/chromium/ui/display/manager/chromeos/touch_transform_controller.h +++ b/chromium/ui/display/manager/chromeos/touch_transform_controller.h @@ -17,6 +17,7 @@ namespace ui { struct TouchscreenDevice; +struct TouchDeviceTransform; } namespace display { @@ -26,8 +27,6 @@ class DisplayManager; class ManagedDisplayInfo; class TouchTransformSetter; -struct TouchDeviceTransform; - namespace test { class TouchTransformControllerTest; } @@ -59,7 +58,7 @@ class DISPLAY_MANAGER_EXPORT TouchTransformController { ~UpdateData(); std::map<int32_t, double> device_to_scale; - std::vector<TouchDeviceTransform> touch_device_transforms; + std::vector<ui::TouchDeviceTransform> touch_device_transforms; }; void UpdateTouchTransforms(UpdateData* data) const; @@ -69,14 +68,12 @@ class DISPLAY_MANAGER_EXPORT TouchTransformController { // The transform is also responsible for properly scaling the display if the // display supports panel fitting. // - // On X11 events are reported in framebuffer coordinate space, so the - // |framebuffer_size| is used for scaling. // On Ozone events are reported in the touchscreen's resolution, so // |touch_display| is used to determine the size and scale the event. - gfx::Transform GetTouchTransform(const ManagedDisplayInfo& display, - const ManagedDisplayInfo& touch_display, - const ui::TouchscreenDevice& touchscreen, - const gfx::Size& framebuffer_size) const; + gfx::Transform GetTouchTransform( + const ManagedDisplayInfo& display, + const ManagedDisplayInfo& touch_display, + const ui::TouchscreenDevice& touchscreen) const; // Returns the scaling factor for the touch radius such that it scales the // radius from |touch_device|'s coordinate system to the |touch_display|'s 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 473b847b753..2860c085541 100644 --- a/chromium/ui/display/manager/chromeos/touch_transform_controller_unittest.cc +++ b/chromium/ui/display/manager/chromeos/touch_transform_controller_unittest.cc @@ -21,7 +21,9 @@ namespace test { namespace { constexpr int kDisplayId1 = 1; +constexpr int kDisplayId2 = 2; constexpr int kTouchId1 = 5; +constexpr int kTouchId2 = 6; ManagedDisplayInfo CreateDisplayInfo(int64_t id, unsigned int touch_device_id, @@ -39,6 +41,19 @@ ManagedDisplayInfo CreateDisplayInfo(int64_t id, return info; } +ui::TouchDeviceTransform CreateTouchDeviceTransform( + int64_t display_id, + int32_t device_id, + const gfx::Transform& transform, + double radius_scale = 1.0) { + ui::TouchDeviceTransform touch_device_transform; + touch_device_transform.display_id = display_id; + touch_device_transform.device_id = device_id; + touch_device_transform.transform = transform; + touch_device_transform.radius_scale = radius_scale; + return touch_device_transform; +} + ui::TouchscreenDevice CreateTouchscreenDevice(unsigned int id, const gfx::Size& size) { return ui::TouchscreenDevice(id, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, @@ -101,12 +116,12 @@ class TouchTransformControllerTest : public testing::Test { TouchTransformControllerTest() {} ~TouchTransformControllerTest() override {} - gfx::Transform GetTouchTransform(const ManagedDisplayInfo& display, - const ManagedDisplayInfo& touch_display, - const ui::TouchscreenDevice& touchscreen, - const gfx::Size& framebuffer_size) const { + gfx::Transform GetTouchTransform( + const ManagedDisplayInfo& display, + const ManagedDisplayInfo& touch_display, + const ui::TouchscreenDevice& touchscreen) const { return touch_transform_controller_->GetTouchTransform( - display, touch_display, touchscreen, framebuffer_size); + display, touch_display, touchscreen); } double GetTouchResolutionScale( @@ -160,8 +175,7 @@ TEST_F(TouchTransformControllerTest, MirrorModeLetterboxing) { gfx::Size fb_size(1920, 1200); - // Create the touchscreens with the same size as the framebuffer so we can - // share the tests between Ozone & X11. + // TODO(kylechar): Check the TouchscreenDevice size makes sense for Ozone. ui::TouchscreenDevice internal_touchscreen = CreateTouchscreenDevice(10, fb_size); ui::TouchscreenDevice external_touchscreen = @@ -169,15 +183,16 @@ TEST_F(TouchTransformControllerTest, MirrorModeLetterboxing) { ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( internal_display_info.id(), internal_touchscreen.id, GetTouchTransform(internal_display_info, internal_display_info, - internal_touchscreen, fb_size)); - - device_manager->UpdateTouchInfoForDisplay( + internal_touchscreen))); + transforms.push_back(CreateTouchDeviceTransform( internal_display_info.id(), external_touchscreen.id, GetTouchTransform(external_display_info, external_display_info, - external_touchscreen, fb_size)); + external_touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(1, device_manager->GetTargetDisplayForTouchDevice(10)); EXPECT_EQ(1, device_manager->GetTargetDisplayForTouchDevice(11)); @@ -227,24 +242,25 @@ TEST_F(TouchTransformControllerTest, MirrorModePillarboxing) { gfx::Size fb_size(1024, 768); - // Create the touchscreens with the same size as the framebuffer so we can - // share the tests between Ozone & X11. + // TODO(kylechar): Check the TouchscreenDevice size makes sense for Ozone. ui::TouchscreenDevice internal_touchscreen = CreateTouchscreenDevice(10, fb_size); ui::TouchscreenDevice external_touchscreen = CreateTouchscreenDevice(11, fb_size); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); + std::vector<ui::TouchDeviceTransform> transforms; - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( internal_display_info.id(), internal_touchscreen.id, GetTouchTransform(internal_display_info, internal_display_info, - internal_touchscreen, fb_size)); + internal_touchscreen))); - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( internal_display_info.id(), external_touchscreen.id, GetTouchTransform(external_display_info, external_display_info, - external_touchscreen, fb_size)); + external_touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(1, device_manager->GetTargetDisplayForTouchDevice(10)); EXPECT_EQ(1, device_manager->GetTargetDisplayForTouchDevice(11)); @@ -299,24 +315,24 @@ TEST_F(TouchTransformControllerTest, SoftwareMirrorMode) { gfx::Size fb_size(1920, 1990); - // Create the touchscreens with the same size as the framebuffer so we can - // share the tests between Ozone & X11. + // TODO(kylechar): Check the TouchscreenDevice size makes sense for Ozone. ui::TouchscreenDevice display1_touchscreen = CreateTouchscreenDevice(10, fb_size); ui::TouchscreenDevice display2_touchscreen = CreateTouchscreenDevice(11, fb_size); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); + std::vector<ui::TouchDeviceTransform> transforms; - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( display1_info.id(), display1_touchscreen.id, - GetTouchTransform(display1_info, display1_info, display1_touchscreen, - fb_size)); + GetTouchTransform(display1_info, display1_info, display1_touchscreen))); - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( display1_info.id(), display2_touchscreen.id, - GetTouchTransform(display1_info, display2_info, display2_touchscreen, - fb_size)); + GetTouchTransform(display1_info, display2_info, display2_touchscreen))); + + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(1, device_manager->GetTargetDisplayForTouchDevice(10)); EXPECT_EQ(1, device_manager->GetTargetDisplayForTouchDevice(11)); @@ -370,20 +386,20 @@ TEST_F(TouchTransformControllerTest, ExtendedMode) { CreateDisplayInfo(2, 6u, gfx::Rect(0, 828, 2560, 1600)); gfx::Size fb_size(2560, 2428); - // Create the touchscreens with the same size as the framebuffer so we can - // share the tests between Ozone & X11. + // TODO(kylechar): Check the TouchscreenDevice size makes sense for Ozone. ui::TouchscreenDevice touchscreen1 = CreateTouchscreenDevice(5, fb_size); ui::TouchscreenDevice touchscreen2 = CreateTouchscreenDevice(6, fb_size); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); - - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( display1.id(), touchscreen1.id, - GetTouchTransform(display1, display1, touchscreen1, fb_size)); + GetTouchTransform(display1, display1, touchscreen1))); - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( display2.id(), touchscreen2.id, - GetTouchTransform(display2, display2, touchscreen2, fb_size)); + GetTouchTransform(display2, display2, touchscreen2))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(1, device_manager->GetTargetDisplayForTouchDevice(5)); EXPECT_EQ(2, device_manager->GetTargetDisplayForTouchDevice(6)); @@ -407,26 +423,16 @@ TEST_F(TouchTransformControllerTest, ExtendedMode) { x = 0.0; y = 0.0; device_manager->ApplyTouchTransformer(6, &x, &y); -#if defined(USE_OZONE) // On ozone we expect screen coordinates so add display origin. EXPECT_NEAR(0 + 0, x, 0.5); EXPECT_NEAR(0 + 828, y, 0.5); -#else - EXPECT_NEAR(0, x, 0.5); - EXPECT_NEAR(0, y, 0.5); -#endif x = 2559.0; y = 2427.0; device_manager->ApplyTouchTransformer(6, &x, &y); -#if defined(USE_OZONE) // On ozone we expect screen coordinates so add display origin. EXPECT_NEAR(2559 + 0, x, 0.5); EXPECT_NEAR(1599 + 828, y, 0.5); -#else - EXPECT_NEAR(2559, x, 0.5); - EXPECT_NEAR(1599, y, 0.5); -#endif } TEST_F(TouchTransformControllerTest, TouchRadiusScale) { @@ -441,16 +447,12 @@ TEST_F(TouchTransformControllerTest, TouchRadiusScale) { } TEST_F(TouchTransformControllerTest, OzoneTranslation) { -#if defined(USE_OZONE) // The internal display has size 1920 x 1200. The external display has // size 1920x1200. The total frame buffer is 1920x2450, // where 2458 = 1200 + 50 (hidden gap) + 1200 // and the second monitor is translated to Point (0, 1250) in the // framebuffer. - const int kDisplayId2 = 2; - const int kTouchId2 = 6; const gfx::Size kDisplaySize(1920, 1200); - const gfx::Size kTouchSize(1920, 1200); const int kHiddenGap = 50; ManagedDisplayInfo display1 = CreateDisplayInfo( @@ -461,23 +463,23 @@ TEST_F(TouchTransformControllerTest, OzoneTranslation) { gfx::Rect(0, kDisplaySize.height() + kHiddenGap, kDisplaySize.width(), kDisplaySize.height())); - gfx::Size fb_size(1920, 2450); - ui::TouchscreenDevice touchscreen1 = CreateTouchscreenDevice(kTouchId1, kDisplaySize); ui::TouchscreenDevice touchscreen2 = CreateTouchscreenDevice(kTouchId2, kDisplaySize); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); + std::vector<ui::TouchDeviceTransform> transforms; // Mirror displays. Touch screen 2 is associated to display 1. - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( display1.id(), touchscreen1.id, - GetTouchTransform(display1, display1, touchscreen1, kTouchSize)); + GetTouchTransform(display1, display1, touchscreen1))); - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( display1.id(), touchscreen2.id, - GetTouchTransform(display1, display2, touchscreen2, kTouchSize)); + GetTouchTransform(display1, display2, touchscreen2))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(kDisplayId1, device_manager->GetTargetDisplayForTouchDevice(kTouchId1)); @@ -509,9 +511,10 @@ TEST_F(TouchTransformControllerTest, OzoneTranslation) { EXPECT_NEAR(1200, y, 0.5); // Remove mirroring of displays. - device_manager->UpdateTouchInfoForDisplay( + transforms.push_back(CreateTouchDeviceTransform( display2.id(), touchscreen2.id, - GetTouchTransform(display2, display2, touchscreen2, kTouchSize)); + GetTouchTransform(display2, display2, touchscreen2))); + device_manager->ConfigureTouchDevices(transforms); x = 1920.0; y = 1200.0; @@ -524,7 +527,6 @@ TEST_F(TouchTransformControllerTest, OzoneTranslation) { device_manager->ApplyTouchTransformer(kTouchId2, &x, &y); EXPECT_NEAR(1920, x, 0.5); EXPECT_NEAR(1200 + kDisplaySize.height() + kHiddenGap, y, 0.5); -#endif // USE_OZONE } TEST_F(TouchTransformControllerTest, AccurateUserTouchCalibration) { @@ -549,16 +551,15 @@ TEST_F(TouchTransformControllerTest, AccurateUserTouchCalibration) { const std::string msg = GetTouchPointString(user_input); - gfx::Size fb_size(1920, 1200); - ui::TouchscreenDevice touchscreen = CreateTouchscreenDevice(kTouchId1, kTouchSize); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); - - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( display.id(), touchscreen.id, - GetTouchTransform(display, display, touchscreen, kTouchSize)); + GetTouchTransform(display, display, touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(kDisplayId1, device_manager->GetTargetDisplayForTouchDevice(kTouchId1)); @@ -598,10 +599,11 @@ TEST_F(TouchTransformControllerTest, ErrorProneUserTouchCalibration) { CreateTouchscreenDevice(kTouchId1, kTouchSize); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); - - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( display.id(), touchscreen.id, - GetTouchTransform(display, display, touchscreen, kTouchSize)); + GetTouchTransform(display, display, touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(kDisplayId1, device_manager->GetTargetDisplayForTouchDevice(kTouchId1)); @@ -643,10 +645,11 @@ TEST_F(TouchTransformControllerTest, ResolutionChangeUserTouchCalibration) { CreateTouchscreenDevice(kTouchId1, kTouchSize); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); - - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( display.id(), touchscreen.id, - GetTouchTransform(display, display, touchscreen, kTouchSize)); + GetTouchTransform(display, display, touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(kDisplayId1, device_manager->GetTargetDisplayForTouchDevice(kTouchId1)); @@ -684,9 +687,11 @@ TEST_F(TouchTransformControllerTest, DifferentBoundsUserTouchCalibration) { ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( display.id(), touchscreen.id, - GetTouchTransform(display, display, touchscreen, kTouchSize)); + GetTouchTransform(display, display, touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(kDisplayId1, device_manager->GetTargetDisplayForTouchDevice(kTouchId1)); @@ -718,12 +723,8 @@ TEST_F(TouchTransformControllerTest, LetterboxingUserTouchCalibration) { false))); internal_display_info.SetManagedDisplayModes(internal_modes); - gfx::Size fb_size(kDisplaySize); - - // Create the touchscreens with the same size as the framebuffer so we can - // share the tests between Ozone & X11. ui::TouchscreenDevice internal_touchscreen = - CreateTouchscreenDevice(kTouchId1, fb_size); + CreateTouchscreenDevice(kTouchId1, kTouchSize); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); @@ -742,10 +743,12 @@ TEST_F(TouchTransformControllerTest, LetterboxingUserTouchCalibration) { internal_display_info.SetTouchCalibrationData(touch_data); EXPECT_TRUE(internal_display_info.has_touch_calibration_data()); - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( internal_display_info.id(), internal_touchscreen.id, GetTouchTransform(internal_display_info, internal_display_info, - internal_touchscreen, fb_size)); + internal_touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(kDisplayId1, device_manager->GetTargetDisplayForTouchDevice(kTouchId1)); @@ -793,12 +796,8 @@ TEST_F(TouchTransformControllerTest, PillarBoxingUserTouchCalibration) { false))); internal_display_info.SetManagedDisplayModes(internal_modes); - gfx::Size fb_size(kDisplaySize); - - // Create the touchscreens with the same size as the framebuffer so we can - // share the tests between Ozone & X11. ui::TouchscreenDevice internal_touchscreen = - CreateTouchscreenDevice(kTouchId1, fb_size); + CreateTouchscreenDevice(kTouchId1, kDisplaySize); ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance(); @@ -817,10 +816,12 @@ TEST_F(TouchTransformControllerTest, PillarBoxingUserTouchCalibration) { internal_display_info.SetTouchCalibrationData(touch_data); EXPECT_TRUE(internal_display_info.has_touch_calibration_data()); - device_manager->UpdateTouchInfoForDisplay( + std::vector<ui::TouchDeviceTransform> transforms; + transforms.push_back(CreateTouchDeviceTransform( internal_display_info.id(), internal_touchscreen.id, GetTouchTransform(internal_display_info, internal_display_info, - internal_touchscreen, fb_size)); + internal_touchscreen))); + device_manager->ConfigureTouchDevices(transforms); EXPECT_EQ(kDisplayId1, device_manager->GetTargetDisplayForTouchDevice(kTouchId1)); diff --git a/chromium/ui/display/manager/chromeos/touch_transform_setter.h b/chromium/ui/display/manager/chromeos/touch_transform_setter.h index 4e87b079c4c..efbb8e12a6f 100644 --- a/chromium/ui/display/manager/chromeos/touch_transform_setter.h +++ b/chromium/ui/display/manager/chromeos/touch_transform_setter.h @@ -5,15 +5,16 @@ #ifndef UI_DISPLAY_MANAGER_CHROMEOS_TOUCH_TRANSFORM_SETTER_H_ #define UI_DISPLAY_MANAGER_CHROMEOS_TOUCH_TRANSFORM_SETTER_H_ -#include <map> #include <vector> #include "base/macros.h" #include "ui/display/manager/display_manager_export.h" -namespace display { - +namespace ui { struct TouchDeviceTransform; +} + +namespace display { // TouchTransformSetter is used by TouchTransformController to apply the actual // settings. @@ -21,11 +22,9 @@ class DISPLAY_MANAGER_EXPORT TouchTransformSetter { public: virtual ~TouchTransformSetter() {} - // |scales| maps from the touch device id to the touch radius scale and // |transforms| contains the transform for each device and display pair. virtual void ConfigureTouchDevices( - const std::map<int32_t, double>& scales, - const std::vector<TouchDeviceTransform>& transforms) = 0; + const std::vector<ui::TouchDeviceTransform>& transforms) = 0; }; } // namespace display diff --git a/chromium/ui/display/manager/chromeos/update_display_configuration_task.cc b/chromium/ui/display/manager/chromeos/update_display_configuration_task.cc index 581c7c624a8..1e533f9b097 100644 --- a/chromium/ui/display/manager/chromeos/update_display_configuration_task.cc +++ b/chromium/ui/display/manager/chromeos/update_display_configuration_task.cc @@ -18,7 +18,6 @@ UpdateDisplayConfigurationTask::UpdateDisplayConfigurationTask( MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, int power_flags, - uint32_t background_color_argb, bool force_configure, const ResponseCallback& callback) : delegate_(delegate), @@ -26,19 +25,16 @@ UpdateDisplayConfigurationTask::UpdateDisplayConfigurationTask( new_display_state_(new_display_state), new_power_state_(new_power_state), power_flags_(power_flags), - background_color_argb_(background_color_argb), force_configure_(force_configure), callback_(callback), force_dpms_(false), requesting_displays_(false), weak_ptr_factory_(this) { - delegate_->GrabServer(); delegate_->AddObserver(this); } UpdateDisplayConfigurationTask::~UpdateDisplayConfigurationTask() { delegate_->RemoveObserver(this); - delegate_->UngrabServer(); } void UpdateDisplayConfigurationTask::Run() { @@ -65,9 +61,6 @@ void UpdateDisplayConfigurationTask::OnDisplaysUpdated( cached_displays_ = displays; requesting_displays_ = false; - if (cached_displays_.size() > 1 && background_color_argb_) - delegate_->SetBackgroundColor(background_color_argb_); - // If the user hasn't requested a display state, update it using the requested // power state. if (new_display_state_ == MULTIPLE_DISPLAY_STATE_INVALID) @@ -99,14 +92,11 @@ void UpdateDisplayConfigurationTask::EnterState( VLOG(2) << "EnterState"; std::vector<DisplayConfigureRequest> requests; if (!layout_manager_->GetDisplayLayout(cached_displays_, new_display_state_, - new_power_state_, &requests, - &framebuffer_size_)) { + new_power_state_, &requests)) { callback.Run(ConfigureDisplaysTask::ERROR); return; } if (!requests.empty()) { - DCHECK(!framebuffer_size_.IsEmpty()); - delegate_->CreateFrameBuffer(framebuffer_size_); configure_task_.reset( new ConfigureDisplaysTask(delegate_, requests, callback)); configure_task_->Run(); @@ -160,11 +150,8 @@ void UpdateDisplayConfigurationTask::OnEnableSoftwareMirroring( } void UpdateDisplayConfigurationTask::FinishConfiguration(bool success) { - if (success && force_dpms_) - delegate_->ForceDPMSOn(); - - callback_.Run(success, cached_displays_, framebuffer_size_, - new_display_state_, new_power_state_); + callback_.Run(success, cached_displays_, new_display_state_, + new_power_state_); } bool UpdateDisplayConfigurationTask::ShouldForceDpms() const { diff --git a/chromium/ui/display/manager/chromeos/update_display_configuration_task.h b/chromium/ui/display/manager/chromeos/update_display_configuration_task.h index e76ba8cdf79..1538fd0f2e3 100644 --- a/chromium/ui/display/manager/chromeos/update_display_configuration_task.h +++ b/chromium/ui/display/manager/chromeos/update_display_configuration_task.h @@ -24,20 +24,17 @@ class NativeDisplayDelegate; class DISPLAY_MANAGER_EXPORT UpdateDisplayConfigurationTask : public NativeDisplayObserver { public: - typedef base::Callback<void( - bool /* success */, - const std::vector<DisplaySnapshot*>& /* displays */, - const gfx::Size& /* framebuffer_size */, - MultipleDisplayState /* new_display_state */, - chromeos::DisplayPowerState /* new_power_state */)> - ResponseCallback; + using ResponseCallback = + base::Callback<void(bool /* success */, + const std::vector<DisplaySnapshot*>& /* displays */, + MultipleDisplayState /* new_display_state */, + chromeos::DisplayPowerState /* new_power_state */)>; UpdateDisplayConfigurationTask(NativeDisplayDelegate* delegate, DisplayLayoutManager* layout_manager, MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state, int power_flags, - uint32_t background_color_argb, bool force_configure, const ResponseCallback& callback); ~UpdateDisplayConfigurationTask() override; @@ -91,8 +88,6 @@ class DISPLAY_MANAGER_EXPORT UpdateDisplayConfigurationTask // DisplayConfigurator. int power_flags_; - uint32_t background_color_argb_; - bool force_configure_; // Used to signal that the task has finished. @@ -105,8 +100,6 @@ class DISPLAY_MANAGER_EXPORT UpdateDisplayConfigurationTask // List of updated displays. std::vector<DisplaySnapshot*> cached_displays_; - gfx::Size framebuffer_size_; - std::unique_ptr<ConfigureDisplaysTask> configure_task_; base::WeakPtrFactory<UpdateDisplayConfigurationTask> weak_ptr_factory_; 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 72989ca0d73..4115036836e 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 @@ -80,11 +80,11 @@ class TestDisplayLayoutManager : public DisplayLayoutManager { return power_state_; } - bool GetDisplayLayout(const std::vector<DisplaySnapshot*>& displays, - MultipleDisplayState new_display_state, - chromeos::DisplayPowerState new_power_state, - std::vector<DisplayConfigureRequest>* requests, - gfx::Size* framebuffer_size) const override { + bool GetDisplayLayout( + const std::vector<DisplaySnapshot*>& displays, + MultipleDisplayState new_display_state, + chromeos::DisplayPowerState new_power_state, + std::vector<DisplayConfigureRequest>* requests) const override { gfx::Point origin; for (DisplaySnapshot* display : displays) { const DisplayMode* mode = display->native_mode(); @@ -100,12 +100,8 @@ class TestDisplayLayoutManager : public DisplayLayoutManager { requests->push_back(DisplayConfigureRequest(display, nullptr, origin)); } - if (new_display_state != MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) { + if (new_display_state != MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) origin.Offset(0, mode->size().height()); - framebuffer_size->SetToMax(gfx::Size(mode->size().width(), origin.y())); - } else { - *framebuffer_size = mode->size(); - } } return true; @@ -179,7 +175,6 @@ class UpdateDisplayConfigurationTaskTest : public testing::Test { void ResponseCallback(bool success, const std::vector<DisplaySnapshot*>& displays, - const gfx::Size& framebuffer_size, MultipleDisplayState new_display_state, chromeos::DisplayPowerState new_power_state) { configured_ = true; @@ -220,7 +215,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, HeadlessConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_HEADLESS, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -230,7 +225,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, HeadlessConfiguration) { EXPECT_TRUE(configuration_status_); EXPECT_EQ(MULTIPLE_DISPLAY_STATE_HEADLESS, display_state_); EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_); - EXPECT_EQ(JoinActions(kGrab, kUngrab, nullptr), log_.GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_.GetActionsAndClear()); } TEST_F(UpdateDisplayConfigurationTaskTest, SingleConfiguration) { @@ -239,7 +234,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -251,11 +246,8 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleConfiguration) { EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), displays_[0].get(), nullptr) - .c_str(), GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(), - kUngrab, nullptr), + nullptr), log_.GetActionsAndClear()); } @@ -265,7 +257,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, ExtendedConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -277,16 +269,11 @@ TEST_F(UpdateDisplayConfigurationTaskTest, ExtendedConfiguration) { EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(gfx::Size(big_mode_.size().width(), - small_mode_.size().height() + - big_mode_.size().height()), - displays_[0].get(), displays_[1].get()) - .c_str(), GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(), GetCrtcAction(*displays_[1], &big_mode_, gfx::Point(0, small_mode_.size().height())) .c_str(), - kUngrab, nullptr), + nullptr), log_.GetActionsAndClear()); } @@ -296,7 +283,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, MirrorConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -308,12 +295,9 @@ TEST_F(UpdateDisplayConfigurationTaskTest, MirrorConfiguration) { EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(small_mode_.size(), displays_[0].get(), - displays_[1].get()) - .c_str(), GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(), GetCrtcAction(*displays_[1], &small_mode_, gfx::Point()).c_str(), - kUngrab, nullptr), + nullptr), log_.GetActionsAndClear()); } @@ -324,7 +308,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailMirrorConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -332,7 +316,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailMirrorConfiguration) { EXPECT_TRUE(configured_); EXPECT_FALSE(configuration_status_); - EXPECT_EQ(JoinActions(kGrab, kUngrab, nullptr), log_.GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_.GetActionsAndClear()); } TEST_F(UpdateDisplayConfigurationTaskTest, FailExtendedConfiguration) { @@ -342,7 +326,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailExtendedConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -352,11 +336,6 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailExtendedConfiguration) { EXPECT_FALSE(configuration_status_); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(gfx::Size(big_mode_.size().width(), - small_mode_.size().height() + - big_mode_.size().height()), - displays_[0].get(), displays_[1].get()) - .c_str(), GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(), GetCrtcAction(*displays_[1], &big_mode_, gfx::Point(0, small_mode_.size().height())) @@ -364,7 +343,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, FailExtendedConfiguration) { GetCrtcAction(*displays_[1], &small_mode_, gfx::Point(0, small_mode_.size().height())) .c_str(), - kUngrab, nullptr), + nullptr), log_.GetActionsAndClear()); } @@ -374,7 +353,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleChangePowerConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -386,18 +365,15 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleChangePowerConfiguration) { EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_ON, power_state_); EXPECT_EQ( JoinActions( - kGrab, - GetFramebufferAction(small_mode_.size(), displays_[0].get(), nullptr) - .c_str(), GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(), - kUngrab, nullptr), + nullptr), log_.GetActionsAndClear()); // Turn power off { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, - chromeos::DISPLAY_POWER_ALL_OFF, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_OFF, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -407,11 +383,8 @@ TEST_F(UpdateDisplayConfigurationTaskTest, SingleChangePowerConfiguration) { EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, display_state_); EXPECT_EQ(chromeos::DISPLAY_POWER_ALL_OFF, power_state_); EXPECT_EQ( - JoinActions(kGrab, GetFramebufferAction(small_mode_.size(), - displays_[0].get(), nullptr) - .c_str(), - GetCrtcAction(*displays_[0], nullptr, gfx::Point()).c_str(), - kUngrab, nullptr), + JoinActions( + GetCrtcAction(*displays_[0], nullptr, gfx::Point()).c_str(), nullptr), log_.GetActionsAndClear()); } @@ -424,7 +397,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, NoopSoftwareMirrorConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -435,7 +408,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, NoopSoftwareMirrorConfiguration) { { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -445,7 +418,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, NoopSoftwareMirrorConfiguration) { EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, display_state_); EXPECT_TRUE(layout_manager_.GetSoftwareMirroringController() ->SoftwareMirroringEnabled()); - EXPECT_EQ(JoinActions(kGrab, kUngrab, nullptr), log_.GetActionsAndClear()); + EXPECT_EQ(kNoActions, log_.GetActionsAndClear()); } TEST_F(UpdateDisplayConfigurationTaskTest, @@ -458,7 +431,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, false, + chromeos::DISPLAY_POWER_ALL_ON, 0, false, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -469,7 +442,7 @@ TEST_F(UpdateDisplayConfigurationTaskTest, { UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, - chromeos::DISPLAY_POWER_ALL_ON, 0, 0, true /* force_configure */, + chromeos::DISPLAY_POWER_ALL_ON, 0, true /* force_configure */, base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, base::Unretained(this))); task.Run(); @@ -481,16 +454,11 @@ TEST_F(UpdateDisplayConfigurationTaskTest, ->SoftwareMirroringEnabled()); EXPECT_EQ( JoinActions( - kGrab, GetFramebufferAction(gfx::Size(big_mode_.size().width(), - small_mode_.size().height() + - big_mode_.size().height()), - displays_[0].get(), displays_[1].get()) - .c_str(), GetCrtcAction(*displays_[0], &small_mode_, gfx::Point()).c_str(), GetCrtcAction(*displays_[1], &big_mode_, gfx::Point(0, small_mode_.size().height())) .c_str(), - kUngrab, nullptr), + nullptr), log_.GetActionsAndClear()); } diff --git a/chromium/ui/display/manager/chromeos/x11/DEPS b/chromium/ui/display/manager/chromeos/x11/DEPS deleted file mode 100644 index 4c156b26039..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -include_rules = [ - "+ui/events/platform", - "+ui/gfx/x", -] diff --git a/chromium/ui/display/manager/chromeos/x11/display_mode_x11.cc b/chromium/ui/display/manager/chromeos/x11/display_mode_x11.cc deleted file mode 100644 index 5c6722042d4..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/display_mode_x11.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/ptr_util.h" - -#include "ui/display/manager/chromeos/x11/display_mode_x11.h" - -namespace display { - -DisplayModeX11::DisplayModeX11(const gfx::Size& size, - bool interlaced, - float refresh_rate, - RRMode mode_id) - : DisplayMode(size, interlaced, refresh_rate), mode_id_(mode_id) {} - -DisplayModeX11::~DisplayModeX11() {} - -std::unique_ptr<DisplayMode> DisplayModeX11::Clone() const { - return base::WrapUnique( - new DisplayModeX11(size(), is_interlaced(), refresh_rate(), mode_id())); -} - -} // namespace display diff --git a/chromium/ui/display/manager/chromeos/x11/display_mode_x11.h b/chromium/ui/display/manager/chromeos/x11/display_mode_x11.h deleted file mode 100644 index 7c8b63002f1..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/display_mode_x11.h +++ /dev/null @@ -1,37 +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_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_MODE_X11_H_ -#define UI_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_MODE_X11_H_ - -#include "base/macros.h" -#include "ui/display/manager/display_manager_export.h" -#include "ui/display/types/display_mode.h" - -// Forward declare from Xlib and Xrandr. -typedef unsigned long XID; -typedef XID RRMode; - -namespace display { - -class DISPLAY_MANAGER_EXPORT DisplayModeX11 : public DisplayMode { - public: - DisplayModeX11(const gfx::Size& size, - bool interlaced, - float refresh_rate, - RRMode mode_id); - ~DisplayModeX11() override; - std::unique_ptr<DisplayMode> Clone() const override; - - RRMode mode_id() const { return mode_id_; } - - private: - RRMode mode_id_; - - DISALLOW_COPY_AND_ASSIGN(DisplayModeX11); -}; - -} // namespace display - -#endif // UI_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_MODE_X11_H_ diff --git a/chromium/ui/display/manager/chromeos/x11/display_snapshot_x11.cc b/chromium/ui/display/manager/chromeos/x11/display_snapshot_x11.cc deleted file mode 100644 index c58593c3bd7..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/display_snapshot_x11.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/display/manager/chromeos/x11/display_snapshot_x11.h" - -#include "base/strings/stringprintf.h" -#include "ui/display/manager/chromeos/x11/display_mode_x11.h" - -namespace display { - -DisplaySnapshotX11::DisplaySnapshotX11( - int64_t display_id, - const gfx::Point& origin, - const gfx::Size& physical_size, - DisplayConnectionType type, - bool is_aspect_preserving_scaling, - bool has_overscan, - std::string display_name, - std::vector<std::unique_ptr<const DisplayMode>> modes, - const std::vector<uint8_t>& edid, - const DisplayMode* current_mode, - const DisplayMode* native_mode, - RROutput output, - RRCrtc crtc, - int index) - : DisplaySnapshot(display_id, - origin, - physical_size, - type, - is_aspect_preserving_scaling, - has_overscan, - false, - display_name, - // TODO(jdufault): Figure out if we can get the file - // descriptor that maps to the device. - base::FilePath(), - std::move(modes), - edid, - current_mode, - native_mode), - output_(output), - crtc_(crtc), - index_(index) {} - -DisplaySnapshotX11::~DisplaySnapshotX11() {} - -std::string DisplaySnapshotX11::ToString() const { - return base::StringPrintf( - "[type=%d, output=%ld, crtc=%ld, mode=%ld, dim=%dx%d]", - type_, - output_, - crtc_, - current_mode_ - ? static_cast<const DisplayModeX11*>(current_mode_)->mode_id() - : 0, - physical_size_.width(), - physical_size_.height()); -} - -} // namespace display diff --git a/chromium/ui/display/manager/chromeos/x11/display_snapshot_x11.h b/chromium/ui/display/manager/chromeos/x11/display_snapshot_x11.h deleted file mode 100644 index 58d464405c7..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/display_snapshot_x11.h +++ /dev/null @@ -1,62 +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_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_SNAPSHOT_X11_H_ -#define UI_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_SNAPSHOT_X11_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "ui/display/manager/display_manager_export.h" -#include "ui/display/types/display_snapshot.h" - -// Forward declare from Xlib and Xrandr. -typedef unsigned long XID; -typedef XID RROutput; -typedef XID RRCrtc; - -namespace display { - -class DISPLAY_MANAGER_EXPORT DisplaySnapshotX11 : public DisplaySnapshot { - public: - DisplaySnapshotX11(int64_t display_id, - const gfx::Point& origin, - const gfx::Size& physical_size, - DisplayConnectionType type, - bool is_aspect_preserving_scaling, - bool has_overscan, - std::string display_name, - std::vector<std::unique_ptr<const DisplayMode>> modes, - const std::vector<uint8_t>& edid, - const DisplayMode* current_mode, - const DisplayMode* native_mode, - RROutput output, - RRCrtc crtc, - int index); - ~DisplaySnapshotX11() override; - - RROutput output() const { return output_; } - RRCrtc crtc() const { return crtc_; } - int index() const { return index_; } - - // DisplaySnapshot overrides: - std::string ToString() const override; - - private: - RROutput output_; - - // CRTC that should be used for this output. Not necessarily the CRTC - // that XRandR reports is currently being used. - RRCrtc crtc_; - - // This output's index in the array returned by XRandR. Stable even as - // outputs are connected or disconnected. - int index_; - - DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotX11); -}; - -} // namespace display - -#endif // UI_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_SNAPSHOT_X11_H_ diff --git a/chromium/ui/display/manager/chromeos/x11/display_util_x11.cc b/chromium/ui/display/manager/chromeos/x11/display_util_x11.cc deleted file mode 100644 index b658d2038f2..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/display_util_x11.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/display/manager/chromeos/x11/display_util_x11.h" - -#include "base/macros.h" -#include "base/strings/string_util.h" - -namespace display { - -namespace { - -struct DisplayConnectionTypeMapping { - // Prefix of output name. - const char* name; - DisplayConnectionType type; -}; - -const DisplayConnectionTypeMapping kDisplayConnectionTypeMapping[] = { - {"LVDS", DISPLAY_CONNECTION_TYPE_INTERNAL}, - {"eDP", DISPLAY_CONNECTION_TYPE_INTERNAL}, - {"DSI", DISPLAY_CONNECTION_TYPE_INTERNAL}, - {"VGA", DISPLAY_CONNECTION_TYPE_VGA}, - {"HDMI", DISPLAY_CONNECTION_TYPE_HDMI}, - {"DVI", DISPLAY_CONNECTION_TYPE_DVI}, - {"DP", DISPLAY_CONNECTION_TYPE_DISPLAYPORT}}; - -} // namespace - -DisplayConnectionType GetDisplayConnectionTypeFromName( - const std::string& name) { - for (unsigned int i = 0; i < arraysize(kDisplayConnectionTypeMapping); ++i) { - if (base::StartsWith(name, kDisplayConnectionTypeMapping[i].name, - base::CompareCase::SENSITIVE)) { - return kDisplayConnectionTypeMapping[i].type; - } - } - - return DISPLAY_CONNECTION_TYPE_UNKNOWN; -} - -} // namespace display diff --git a/chromium/ui/display/manager/chromeos/x11/display_util_x11.h b/chromium/ui/display/manager/chromeos/x11/display_util_x11.h deleted file mode 100644 index 69327708f0a..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/display_util_x11.h +++ /dev/null @@ -1,25 +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_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_UTIL_X11_H_ -#define UI_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_UTIL_X11_H_ - -#include <string> - -#include "ui/display/manager/display_manager_export.h" -#include "ui/display/types/display_constants.h" - -typedef unsigned long XID; -typedef XID RROutput; - -namespace display { - -// Returns the DisplayConnectionType by matching known type prefixes to |name|. -// Returns DISPLAY_TYPE_UNKNOWN if no valid match. -DISPLAY_MANAGER_EXPORT DisplayConnectionType -GetDisplayConnectionTypeFromName(const std::string& name); - -} // namespace display - -#endif // UI_DISPLAY_MANAGER_CHROMEOS_X11_DISPLAY_UTIL_X11_H_ diff --git a/chromium/ui/display/manager/chromeos/x11/display_util_x11_unittest.cc b/chromium/ui/display/manager/chromeos/x11/display_util_x11_unittest.cc deleted file mode 100644 index c7ddd655fb0..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/display_util_x11_unittest.cc +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/display/manager/chromeos/x11/display_util_x11.h" - -#include <memory> - -#include "testing/gtest/include/gtest/gtest.h" - -namespace display { - -TEST(DisplayUtilX11Test, GetDisplayConnectionTypeFromName) { - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, - GetDisplayConnectionTypeFromName("LVDS")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, - GetDisplayConnectionTypeFromName("eDP")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, - GetDisplayConnectionTypeFromName("DSI")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, - GetDisplayConnectionTypeFromName("LVDSxx")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, - GetDisplayConnectionTypeFromName("eDPzz")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL, - GetDisplayConnectionTypeFromName("DSIyy")); - - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_VGA, - GetDisplayConnectionTypeFromName("VGA")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_VGA, - GetDisplayConnectionTypeFromName("VGAxx")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, - GetDisplayConnectionTypeFromName("HDMI")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI, - GetDisplayConnectionTypeFromName("HDMIyy")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DVI, - GetDisplayConnectionTypeFromName("DVI")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DVI, - GetDisplayConnectionTypeFromName("DVIzz")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DISPLAYPORT, - GetDisplayConnectionTypeFromName("DP")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DISPLAYPORT, - GetDisplayConnectionTypeFromName("DPww")); - - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("xyz")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("abcLVDS")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("cdeeDP")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("abcDSI")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("LVD")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("eD")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("DS")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("VG")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("HDM")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("DV")); - EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN, - GetDisplayConnectionTypeFromName("D")); -} - -} // namespace display diff --git a/chromium/ui/display/manager/chromeos/x11/native_display_delegate_x11.cc b/chromium/ui/display/manager/chromeos/x11/native_display_delegate_x11.cc deleted file mode 100644 index df9af546fa4..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/native_display_delegate_x11.cc +++ /dev/null @@ -1,656 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/display/manager/chromeos/x11/native_display_delegate_x11.h" - -#include <X11/Xatom.h> -#include <X11/Xlib.h> -#include <X11/extensions/dpms.h> -#include <X11/extensions/XInput2.h> -#include <X11/extensions/Xrandr.h> - -#include <algorithm> -#include <utility> - -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "ui/display/manager/chromeos/x11/display_mode_x11.h" -#include "ui/display/manager/chromeos/x11/display_snapshot_x11.h" -#include "ui/display/manager/chromeos/x11/display_util_x11.h" -#include "ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.h" -#include "ui/display/types/native_display_observer.h" -#include "ui/display/util/x11/edid_parser_x11.h" -#include "ui/events/platform/platform_event_source.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/x/x11_atom_cache.h" -#include "ui/gfx/x/x11_error_tracker.h" -#include "ui/gfx/x/x11_types.h" - -namespace display { - -namespace { - -// DPI measurements. -const float kMmInInch = 25.4; -const float kDpi96 = 96.0; -const float kPixelsToMmScale = kMmInInch / kDpi96; - -const char kContentProtectionAtomName[] = "Content Protection"; -const char kProtectionUndesiredAtomName[] = "Undesired"; -const char kProtectionDesiredAtomName[] = "Desired"; -const char kProtectionEnabledAtomName[] = "Enabled"; - -RRMode GetOutputNativeMode(const XRROutputInfo* output_info) { - return output_info->nmode > 0 ? output_info->modes[0] : None; -} - -XRRCrtcGamma* ResampleGammaRamp(XRRCrtcGamma* gamma_ramp, int gamma_ramp_size) { - if (gamma_ramp->size == gamma_ramp_size) - return gamma_ramp; - -#define RESAMPLE(array, i, r) \ - array[i] + (array[i + 1] - array[i]) * r / gamma_ramp_size - - XRRCrtcGamma* resampled = XRRAllocGamma(gamma_ramp_size); - for (int i = 0; i < gamma_ramp_size; ++i) { - int base_index = gamma_ramp->size * i / gamma_ramp_size; - int remaining = gamma_ramp->size * i % gamma_ramp_size; - if (base_index < gamma_ramp->size - 1) { - resampled->red[i] = RESAMPLE(gamma_ramp->red, base_index, remaining); - resampled->green[i] = RESAMPLE(gamma_ramp->green, base_index, remaining); - resampled->blue[i] = RESAMPLE(gamma_ramp->blue, base_index, remaining); - } else { - resampled->red[i] = gamma_ramp->red[gamma_ramp->size - 1]; - resampled->green[i] = gamma_ramp->green[gamma_ramp->size - 1]; - resampled->blue[i] = gamma_ramp->blue[gamma_ramp->size - 1]; - } - } - -#undef RESAMPLE - XRRFreeGamma(gamma_ramp); - return resampled; -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// NativeDisplayDelegateX11::HelperDelegateX11 - -class NativeDisplayDelegateX11::HelperDelegateX11 - : public NativeDisplayDelegateX11::HelperDelegate { - public: - HelperDelegateX11(NativeDisplayDelegateX11* delegate) : delegate_(delegate) {} - ~HelperDelegateX11() override {} - - // NativeDisplayDelegateX11::HelperDelegate overrides: - void UpdateXRandRConfiguration(const base::NativeEvent& event) override { - XRRUpdateConfiguration(event); - } - std::vector<DisplaySnapshot*> GetCachedDisplays() const override { - return delegate_->GetCachedDisplays(); - } - void NotifyDisplayObservers() override { - for (NativeDisplayObserver& observer : delegate_->observers_) - observer.OnConfigurationChanged(); - } - - private: - NativeDisplayDelegateX11* delegate_; - - DISALLOW_COPY_AND_ASSIGN(HelperDelegateX11); -}; - -//////////////////////////////////////////////////////////////////////////////// -// NativeDisplayDelegateX11 implementation: - -NativeDisplayDelegateX11::NativeDisplayDelegateX11() - : display_(gfx::GetXDisplay()), - window_(DefaultRootWindow(display_)), - background_color_argb_(0) {} - -NativeDisplayDelegateX11::~NativeDisplayDelegateX11() { - if (ui::PlatformEventSource::GetInstance() && - platform_event_dispatcher_.get()) { - ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher( - platform_event_dispatcher_.get()); - } -} - -void NativeDisplayDelegateX11::Initialize() { - int error_base_ignored = 0; - int xrandr_event_base = 0; - XRRQueryExtension(display_, &xrandr_event_base, &error_base_ignored); - - helper_delegate_.reset(new HelperDelegateX11(this)); - platform_event_dispatcher_.reset(new NativeDisplayEventDispatcherX11( - helper_delegate_.get(), xrandr_event_base)); - - if (ui::PlatformEventSource::GetInstance()) { - ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher( - platform_event_dispatcher_.get()); - } -} - -void NativeDisplayDelegateX11::GrabServer() { - CHECK(!screen_) << "Server already grabbed"; - XGrabServer(display_); - screen_.reset(XRRGetScreenResources(display_, window_)); - CHECK(screen_); -} - -void NativeDisplayDelegateX11::UngrabServer() { - CHECK(screen_) << "Server not grabbed"; - screen_.reset(); - XUngrabServer(display_); - // crbug.com/366125 - XFlush(display_); -} - -void NativeDisplayDelegateX11::TakeDisplayControl( - const DisplayControlCallback& callback) { - NOTIMPLEMENTED(); -} - -void NativeDisplayDelegateX11::RelinquishDisplayControl( - const DisplayControlCallback& callback) { - NOTIMPLEMENTED(); -} - -void NativeDisplayDelegateX11::SyncWithServer() { - XSync(display_, 0); -} - -void NativeDisplayDelegateX11::SetBackgroundColor(uint32_t color_argb) { - background_color_argb_ = color_argb; -} - -void NativeDisplayDelegateX11::ForceDPMSOn() { - CHECK(DPMSEnable(display_)); - CHECK(DPMSForceLevel(display_, DPMSModeOn)); -} - -void NativeDisplayDelegateX11::GetDisplays( - const GetDisplaysCallback& callback) { - CHECK(screen_) << "Server not grabbed"; - - cached_outputs_.clear(); - std::set<RRCrtc> last_used_crtcs; - - InitModes(); - for (int i = 0; i < screen_->noutput; ++i) { - RROutput output_id = screen_->outputs[i]; - XRROutputInfo* output_info = - XRRGetOutputInfo(display_, screen_.get(), output_id); - if (output_info->connection == RR_Connected) { - cached_outputs_.push_back( - InitDisplaySnapshot(output_id, output_info, &last_used_crtcs, i)); - } - XRRFreeOutputInfo(output_info); - } - - callback.Run(GetCachedDisplays()); -} - -void NativeDisplayDelegateX11::AddMode(const DisplaySnapshot& output, - const DisplayMode* mode) { - CHECK(screen_) << "Server not grabbed"; - CHECK(mode) << "Must add valid mode"; - - const DisplaySnapshotX11& x11_output = - static_cast<const DisplaySnapshotX11&>(output); - RRMode mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id(); - - VLOG(1) << "AddDisplayMode: output=" << x11_output.output() - << " mode=" << mode_id; - XRRAddOutputMode(display_, x11_output.output(), mode_id); -} - -void NativeDisplayDelegateX11::Configure(const DisplaySnapshot& output, - const DisplayMode* mode, - const gfx::Point& origin, - const ConfigureCallback& callback) { - const DisplaySnapshotX11& x11_output = - static_cast<const DisplaySnapshotX11&>(output); - RRMode mode_id = None; - if (mode) - mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id(); - - callback.Run(ConfigureCrtc(x11_output.crtc(), mode_id, x11_output.output(), - origin.x(), origin.y())); -} - -bool NativeDisplayDelegateX11::ConfigureCrtc(RRCrtc crtc, - RRMode mode, - RROutput output, - int x, - int y) { - CHECK(screen_) << "Server not grabbed"; - VLOG(1) << "ConfigureCrtc: crtc=" << crtc << " mode=" << mode - << " output=" << output << " x=" << x << " y=" << y; - // Xrandr.h is full of lies. XRRSetCrtcConfig() is defined as returning a - // Status, which is typically 0 for failure and 1 for success. In - // actuality it returns a RRCONFIGSTATUS, which uses 0 for success. - if (XRRSetCrtcConfig(display_, screen_.get(), crtc, CurrentTime, x, y, mode, - RR_Rotate_0, (output && mode) ? &output : NULL, - (output && mode) ? 1 : 0) != RRSetConfigSuccess) { - LOG(WARNING) << "Unable to configure CRTC " << crtc << ":" - << " mode=" << mode << " output=" << output << " x=" << x - << " y=" << y; - return false; - } - - return true; -} - -void NativeDisplayDelegateX11::CreateFrameBuffer(const gfx::Size& size) { - CHECK(screen_) << "Server not grabbed"; - gfx::Size current_screen_size( - DisplayWidth(display_, DefaultScreen(display_)), - DisplayHeight(display_, DefaultScreen(display_))); - - VLOG(1) << "CreateFrameBuffer: new=" << size.ToString() - << " current=" << current_screen_size.ToString(); - - DestroyUnusedCrtcs(); - - if (size == current_screen_size) - return; - - gfx::Size min_screen_size(current_screen_size); - min_screen_size.SetToMin(size); - UpdateCrtcsForNewFramebuffer(min_screen_size); - - int mm_width = size.width() * kPixelsToMmScale; - int mm_height = size.height() * kPixelsToMmScale; - XRRSetScreenSize( - display_, window_, size.width(), size.height(), mm_width, mm_height); - // We don't wait for root window resize, therefore this end up with drawing - // in the old window size, which we care during the boot. - DrawBackground(); - - // Don't redraw the background upon framebuffer change again. This should - // happen only once after boot. - background_color_argb_ = 0; -} - -void NativeDisplayDelegateX11::AddObserver(NativeDisplayObserver* observer) { - observers_.AddObserver(observer); -} - -void NativeDisplayDelegateX11::RemoveObserver(NativeDisplayObserver* observer) { - observers_.RemoveObserver(observer); -} - -FakeDisplayController* NativeDisplayDelegateX11::GetFakeDisplayController() { - return nullptr; -} - -std::vector<DisplaySnapshot*> NativeDisplayDelegateX11::GetCachedDisplays() - const { - std::vector<DisplaySnapshot*> snapshots(cached_outputs_.size()); - std::transform( - cached_outputs_.cbegin(), cached_outputs_.cend(), snapshots.begin(), - [](const std::unique_ptr<DisplaySnapshot>& item) { return item.get(); }); - return snapshots; -} - -void NativeDisplayDelegateX11::InitModes() { - CHECK(screen_) << "Server not grabbed"; - - modes_.clear(); - - for (int i = 0; i < screen_->nmode; ++i) { - const XRRModeInfo& info = screen_->modes[i]; - float refresh_rate = 0.0f; - if (info.hTotal && info.vTotal) { - refresh_rate = - static_cast<float>(info.dotClock) / - (static_cast<float>(info.hTotal) * static_cast<float>(info.vTotal)); - } - - modes_.insert(std::make_pair( - info.id, base::MakeUnique<DisplayModeX11>( - gfx::Size(info.width, info.height), - info.modeFlags & RR_Interlace, refresh_rate, info.id))); - } -} - -std::unique_ptr<DisplaySnapshotX11> -NativeDisplayDelegateX11::InitDisplaySnapshot(RROutput output, - XRROutputInfo* info, - std::set<RRCrtc>* last_used_crtcs, - int index) { - int64_t display_id = 0; - EDIDParserX11 edid_parser(output); - if (!edid_parser.GetDisplayId(static_cast<uint8_t>(index), &display_id)) - display_id = index; - - bool has_overscan = false; - edid_parser.GetOutputOverscanFlag(&has_overscan); - - DisplayConnectionType type = GetDisplayConnectionTypeFromName(info->name); - if (type == DISPLAY_CONNECTION_TYPE_UNKNOWN) - LOG(ERROR) << "Unknown link type: " << info->name; - - RRMode native_mode_id = GetOutputNativeMode(info); - RRMode current_mode_id = None; - gfx::Point origin; - if (info->crtc) { - XRRCrtcInfo* crtc_info = - XRRGetCrtcInfo(display_, screen_.get(), info->crtc); - current_mode_id = crtc_info->mode; - origin.SetPoint(crtc_info->x, crtc_info->y); - XRRFreeCrtcInfo(crtc_info); - } - - RRCrtc crtc = None; - // Assign a CRTC that isn't already in use. - for (int i = 0; i < info->ncrtc; ++i) { - if (last_used_crtcs->find(info->crtcs[i]) == last_used_crtcs->end()) { - crtc = info->crtcs[i]; - last_used_crtcs->insert(crtc); - break; - } - } - - const DisplayMode* current_mode = NULL; - const DisplayMode* native_mode = NULL; - std::vector<std::unique_ptr<const DisplayMode>> display_modes; - - for (int i = 0; i < info->nmode; ++i) { - const RRMode mode = info->modes[i]; - if (modes_.find(mode) != modes_.end()) { - display_modes.push_back(modes_.at(mode)->Clone()); - - if (mode == current_mode_id) - current_mode = display_modes.back().get(); - if (mode == native_mode_id) - native_mode = display_modes.back().get(); - } else { - LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode; - } - } - - auto display_snapshot = base::MakeUnique<DisplaySnapshotX11>( - display_id, origin, gfx::Size(info->mm_width, info->mm_height), type, - IsOutputAspectPreservingScaling(output), has_overscan, - edid_parser.GetDisplayName(), std::move(display_modes), - edid_parser.edid(), current_mode, native_mode, output, crtc, index); - - VLOG(1) << "Found display " << cached_outputs_.size() << ":" - << " output=" << output << " crtc=" << crtc - << " current_mode=" << current_mode_id; - - return display_snapshot; -} - -void NativeDisplayDelegateX11::GetHDCPState( - const DisplaySnapshot& output, - const GetHDCPStateCallback& callback) { - HDCPState state = HDCP_STATE_UNDESIRED; - bool success = GetHDCPState(output, &state); - callback.Run(success, state); -} - -bool NativeDisplayDelegateX11::GetHDCPState(const DisplaySnapshot& output, - HDCPState* state) { - unsigned char* values = NULL; - int actual_format = 0; - unsigned long nitems = 0; - unsigned long bytes_after = 0; - Atom actual_type = None; - int success = 0; - RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output(); - Atom prop = gfx::GetAtom(kContentProtectionAtomName); - - // TODO(kcwu): Move this to x11_util (similar method calls in this file and - // output_util.cc) - success = XRRGetOutputProperty(display_, - output_id, - prop, - 0, - 100, - False, - False, - AnyPropertyType, - &actual_type, - &actual_format, - &nitems, - &bytes_after, - &values); - gfx::XScopedPtr<unsigned char> scoped_values(values); - if (actual_type == None) { - LOG(ERROR) << "Property '" << kContentProtectionAtomName - << "' does not exist"; - return false; - } - - if (success == Success && actual_type == XA_ATOM && actual_format == 32 && - nitems == 1) { - Atom value = reinterpret_cast<Atom*>(values)[0]; - if (value == gfx::GetAtom(kProtectionUndesiredAtomName)) { - *state = HDCP_STATE_UNDESIRED; - } else if (value == gfx::GetAtom(kProtectionDesiredAtomName)) { - *state = HDCP_STATE_DESIRED; - } else if (value == gfx::GetAtom(kProtectionEnabledAtomName)) { - *state = HDCP_STATE_ENABLED; - } else { - LOG(ERROR) << "Unknown " << kContentProtectionAtomName - << " value: " << value; - return false; - } - } else { - LOG(ERROR) << "XRRGetOutputProperty failed"; - return false; - } - - VLOG(3) << "HDCP state: success," << *state; - return true; -} - -void NativeDisplayDelegateX11::SetHDCPState( - const DisplaySnapshot& output, - HDCPState state, - const SetHDCPStateCallback& callback) { - callback.Run(SetHDCPState(output, state)); -} - -bool NativeDisplayDelegateX11::SetHDCPState(const DisplaySnapshot& output, - HDCPState state) { - Atom name = gfx::GetAtom(kContentProtectionAtomName); - Atom value = None; - switch (state) { - case HDCP_STATE_UNDESIRED: - value = gfx::GetAtom(kProtectionUndesiredAtomName); - break; - case HDCP_STATE_DESIRED: - value = gfx::GetAtom(kProtectionDesiredAtomName); - break; - default: - NOTREACHED() << "Invalid HDCP state: " << state; - return false; - } - gfx::X11ErrorTracker err_tracker; - unsigned char* data = reinterpret_cast<unsigned char*>(&value); - RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output(); - XRRChangeOutputProperty( - display_, output_id, name, XA_ATOM, 32, PropModeReplace, data, 1); - if (err_tracker.FoundNewError()) { - LOG(ERROR) << "XRRChangeOutputProperty failed"; - return false; - } else { - return true; - } -} - -void NativeDisplayDelegateX11::DestroyUnusedCrtcs() { - CHECK(screen_) << "Server not grabbed"; - - for (int i = 0; i < screen_->ncrtc; ++i) { - bool in_use = false; - for (const auto& snapshot : cached_outputs_) { - DisplaySnapshotX11* x11_output = - static_cast<DisplaySnapshotX11*>(snapshot.get()); - if (screen_->crtcs[i] == x11_output->crtc()) { - in_use = true; - break; - } - } - - if (!in_use) - ConfigureCrtc(screen_->crtcs[i], None, None, 0, 0); - } -} - -void NativeDisplayDelegateX11::UpdateCrtcsForNewFramebuffer( - const gfx::Size& min_screen_size) { - CHECK(screen_) << "Server not grabbed"; - // Setting the screen size will fail if any CRTC doesn't fit afterwards. - // At the same time, turning CRTCs off and back on uses up a lot of time. - // This function tries to be smart to avoid too many off/on cycles: - // - We set the new modes on CRTCs, if they fit in both the old and new - // FBs, and park them at (0,0) - // - We disable the CRTCs we will need but don't fit in the old FB. Those - // will be reenabled after the resize. - // We don't worry about the cached state of the outputs here since we are - // not interested in the state we are setting - we just try to get the CRTCs - // out of the way so we can rebuild the frame buffer. - gfx::Rect fb_rect(min_screen_size); - for (const auto& snapshot : cached_outputs_) { - DisplaySnapshotX11* x11_output = - static_cast<DisplaySnapshotX11*>(snapshot.get()); - const DisplayMode* mode_info = x11_output->current_mode(); - RROutput output = x11_output->output(); - RRMode mode = None; - - if (mode_info) { - mode = static_cast<const DisplayModeX11*>(mode_info)->mode_id(); - - if (!fb_rect.Contains(gfx::Rect(mode_info->size()))) { - // In case our CRTC doesn't fit in common area of our current and about - // to be resized framebuffer, disable it. - // It'll get reenabled after we resize the framebuffer. - mode = None; - output = None; - mode_info = NULL; - } - } - - ConfigureCrtc(x11_output->crtc(), mode, output, 0, 0); - } -} - -bool NativeDisplayDelegateX11::IsOutputAspectPreservingScaling(RROutput id) { - Atom scaling_prop = gfx::GetAtom("scaling mode"); - Atom full_aspect_atom = gfx::GetAtom("Full aspect"); - if (scaling_prop == None || full_aspect_atom == None) - return false; - - int nprop = 0; - gfx::XScopedPtr<Atom[]> props(XRRListOutputProperties(display_, id, &nprop)); - for (int j = 0; j < nprop; j++) { - Atom prop = props[j]; - if (scaling_prop == prop) { - unsigned char* values = NULL; - int actual_format; - unsigned long nitems; - unsigned long bytes_after; - Atom actual_type; - int success; - - success = XRRGetOutputProperty(display_, - id, - prop, - 0, - 100, - False, - False, - AnyPropertyType, - &actual_type, - &actual_format, - &nitems, - &bytes_after, - &values); - gfx::XScopedPtr<unsigned char> scoped_value(values); - if (success == Success && actual_type == XA_ATOM && actual_format == 32 && - nitems == 1) { - Atom value = reinterpret_cast<Atom*>(values)[0]; - if (full_aspect_atom == value) - return true; - } - } - } - return false; -} - -std::vector<ColorCalibrationProfile> -NativeDisplayDelegateX11::GetAvailableColorCalibrationProfiles( - const DisplaySnapshot& output) { - // TODO(mukai|marcheu): Checks the system data and fills the result. - // Note that the order would be Dynamic -> Standard -> Movie -> Reading. - return std::vector<ColorCalibrationProfile>(); -} - -bool NativeDisplayDelegateX11::SetColorCalibrationProfile( - const DisplaySnapshot& output, - ColorCalibrationProfile new_profile) { - const DisplaySnapshotX11& x11_output = - static_cast<const DisplaySnapshotX11&>(output); - - XRRCrtcGamma* gamma_ramp = CreateGammaRampForProfile(x11_output, new_profile); - - if (!gamma_ramp) - return false; - - int gamma_ramp_size = XRRGetCrtcGammaSize(display_, x11_output.crtc()); - XRRSetCrtcGamma(display_, x11_output.crtc(), - ResampleGammaRamp(gamma_ramp, gamma_ramp_size)); - XRRFreeGamma(gamma_ramp); - return true; -} - -XRRCrtcGamma* NativeDisplayDelegateX11::CreateGammaRampForProfile( - const DisplaySnapshotX11& x11_output, - ColorCalibrationProfile new_profile) { - // TODO(mukai|marcheu): Creates the appropriate gamma ramp data from the - // profile enum. It would be served by the vendor. - return NULL; -} - -bool NativeDisplayDelegateX11::SetColorCorrection( - const DisplaySnapshot& output, - const std::vector<GammaRampRGBEntry>& degamma_lut, - const std::vector<GammaRampRGBEntry>& gamma_lut, - const std::vector<float>& correction_matrix) { - NOTIMPLEMENTED(); - return false; -} - -void NativeDisplayDelegateX11::DrawBackground() { - if (!background_color_argb_) - return; - // Configuring CRTCs/Framebuffer clears the boot screen image. Paint the - // same background color after updating framebuffer to minimize the - // duration of black screen at boot time. - XColor color; - Colormap colormap = DefaultColormap(display_, 0); - // XColor uses 16 bits per color. - color.red = (background_color_argb_ & 0x00FF0000) >> 8; - color.green = (background_color_argb_ & 0x0000FF00); - color.blue = (background_color_argb_ & 0x000000FF) << 8; - color.flags = DoRed | DoGreen | DoBlue; - XAllocColor(display_, colormap, &color); - - GC gc = XCreateGC(display_, window_, 0, 0); - XSetForeground(display_, gc, color.pixel); - XSetFillStyle(display_, gc, FillSolid); - int width = DisplayWidth(display_, DefaultScreen(display_)); - int height = DisplayHeight(display_, DefaultScreen(display_)); - XFillRectangle(display_, window_, gc, 0, 0, width, height); - XFreeGC(display_, gc); - XFreeColors(display_, colormap, &color.pixel, 1, 0); -} - -} // namespace display diff --git a/chromium/ui/display/manager/chromeos/x11/native_display_delegate_x11.h b/chromium/ui/display/manager/chromeos/x11/native_display_delegate_x11.h deleted file mode 100644 index 3b326fcb243..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/native_display_delegate_x11.h +++ /dev/null @@ -1,182 +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_DISPLAY_MANAGER_CHROMEOS_X11_NATIVE_DISPLAY_DELEGATE_X11_H_ -#define UI_DISPLAY_MANAGER_CHROMEOS_X11_NATIVE_DISPLAY_DELEGATE_X11_H_ - -#include <stdint.h> - -#include <map> -#include <memory> -#include <set> -#include <vector> - -#include "base/compiler_specific.h" -#include "base/event_types.h" -#include "base/macros.h" -#include "base/observer_list.h" -#include "ui/display/manager/display_manager_export.h" -#include "ui/display/types/native_display_delegate.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/x/x11_types.h" - -// Forward declarations for Xlib and Xrandr. -// This is so unused X definitions don't pollute the namespace. -typedef XID RROutput; -typedef XID RRCrtc; -typedef XID RRMode; -typedef XID _Window; - -struct _XRROutputInfo; -typedef _XRROutputInfo XRROutputInfo; -struct _XRRScreenResources; -typedef _XRRScreenResources XRRScreenResources; -struct _XRRCrtcGamma; -typedef _XRRCrtcGamma XRRCrtcGamma; - -extern "C" { -void XRRFreeScreenResources(XRRScreenResources* resources); -} - -namespace display { - -class DisplayModeX11; -class DisplaySnapshotX11; -class NativeDisplayEventDispatcherX11; - -class DISPLAY_MANAGER_EXPORT NativeDisplayDelegateX11 - : public NativeDisplayDelegate { - public: - // Helper class that allows NativeDisplayEventDispatcherX11 and - // NativeDisplayDelegateX11::PlatformEventObserverX11 to interact with this - // class or with mocks in tests. - class HelperDelegate { - public: - virtual ~HelperDelegate() {} - - // Tells XRandR to update its configuration in response to |event|, an - // RRScreenChangeNotify event. - virtual void UpdateXRandRConfiguration(const base::NativeEvent& event) = 0; - - // Returns the list of current outputs. This is used to discard duplicate - // events. - virtual std::vector<DisplaySnapshot*> GetCachedDisplays() const = 0; - - // Notify |observers_| that a change in configuration has occurred. - virtual void NotifyDisplayObservers() = 0; - }; - - NativeDisplayDelegateX11(); - ~NativeDisplayDelegateX11() override; - - // NativeDisplayDelegate overrides: - void Initialize() override; - void GrabServer() override; - void UngrabServer() override; - void TakeDisplayControl(const DisplayControlCallback& callback) override; - void RelinquishDisplayControl( - const DisplayControlCallback& callback) override; - void SyncWithServer() override; - void SetBackgroundColor(uint32_t color_argb) override; - void ForceDPMSOn() override; - void GetDisplays(const GetDisplaysCallback& callback) override; - void AddMode(const DisplaySnapshot& output, const DisplayMode* mode) override; - void Configure(const DisplaySnapshot& output, - const DisplayMode* mode, - const gfx::Point& origin, - const ConfigureCallback& callback) override; - void CreateFrameBuffer(const gfx::Size& size) override; - void GetHDCPState(const DisplaySnapshot& output, - const GetHDCPStateCallback& callback) override; - void SetHDCPState(const DisplaySnapshot& output, - HDCPState state, - const SetHDCPStateCallback& callback) override; - std::vector<ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( - const DisplaySnapshot& output) override; - bool SetColorCalibrationProfile(const DisplaySnapshot& output, - ColorCalibrationProfile new_profile) override; - bool SetColorCorrection(const DisplaySnapshot& output, - const std::vector<GammaRampRGBEntry>& degamma_lut, - const std::vector<GammaRampRGBEntry>& gamma_lut, - const std::vector<float>& correction_matrix) override; - void AddObserver(NativeDisplayObserver* observer) override; - void RemoveObserver(NativeDisplayObserver* observer) override; - FakeDisplayController* GetFakeDisplayController() override; - - std::vector<DisplaySnapshot*> GetCachedDisplays() const; - - private: - class HelperDelegateX11; - - // Parses all the modes made available by |screen_|. - void InitModes(); - - // Helper method for GetOutputs() that returns an OutputSnapshot struct based - // on the passed-in information. - std::unique_ptr<DisplaySnapshotX11> InitDisplaySnapshot( - RROutput id, - XRROutputInfo* info, - std::set<RRCrtc>* last_used_crtcs, - int index); - - // Destroys unused CRTCs. - void DestroyUnusedCrtcs(); - - // Parks used CRTCs in a way which allows a framebuffer resize. This is faster - // than turning them off, resizing, then turning them back on. - // |min_screen_size| represent the smallest size between the current - // framebuffer size and the requested framebuffer size. - void UpdateCrtcsForNewFramebuffer(const gfx::Size& min_screen_size); - - bool ConfigureCrtc(RRCrtc crtc, RRMode mode, RROutput output, int x, int y); - - // Helper functions that perform the actual HDCP requests. - bool GetHDCPState(const DisplaySnapshot& output, HDCPState* state); - bool SetHDCPState(const DisplaySnapshot& output, HDCPState state); - - // Returns whether |id| is configured to preserve aspect when scaling. - bool IsOutputAspectPreservingScaling(RROutput id); - - // Creates the gamma ramp for |new_profile|, or NULL if it doesn't exist. - // The caller should take the ownership. - XRRCrtcGamma* CreateGammaRampForProfile(const DisplaySnapshotX11& x11_output, - ColorCalibrationProfile new_profile); - - void DrawBackground(); - - XDisplay* display_; - _Window window_; - - // Initialized when the server is grabbed and freed when it's ungrabbed. - gfx::XScopedPtr< - XRRScreenResources, - gfx::XObjectDeleter<XRRScreenResources, void, XRRFreeScreenResources>> - screen_; - - std::map<RRMode, std::unique_ptr<DisplayModeX11>> modes_; - - // Every time GetOutputs() is called we cache the updated list of outputs in - // |cached_outputs_| so that we can check for duplicate events rather than - // propagate them. - std::vector<std::unique_ptr<DisplaySnapshot>> cached_outputs_; - - std::unique_ptr<HelperDelegate> helper_delegate_; - - // Processes X11 display events associated with the root window and notifies - // |observers_| when a display change has occurred. - std::unique_ptr<NativeDisplayEventDispatcherX11> platform_event_dispatcher_; - - // List of observers waiting for display configuration change events. - base::ObserverList<NativeDisplayObserver> observers_; - - // A background color used during boot time + multi displays. - uint32_t background_color_argb_; - - DISALLOW_COPY_AND_ASSIGN(NativeDisplayDelegateX11); -}; - -} // namespace display - -#endif // UI_DISPLAY_MANAGER_CHROMEOS_X11_NATIVE_DISPLAY_DELEGATE_X11_H_ diff --git a/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.cc b/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.cc deleted file mode 100644 index a54a1cbbc46..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.cc +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.h" - -#include <X11/extensions/Xrandr.h> -#include <utility> - -#include "base/time/default_tick_clock.h" -#include "ui/display/manager/chromeos/x11/display_mode_x11.h" -#include "ui/display/manager/chromeos/x11/display_snapshot_x11.h" -#include "ui/events/platform/platform_event_source.h" - -namespace display { - -// static -const int NativeDisplayEventDispatcherX11::kUseCacheAfterStartupMs = 7000; - -NativeDisplayEventDispatcherX11::NativeDisplayEventDispatcherX11( - NativeDisplayDelegateX11::HelperDelegate* delegate, - int xrandr_event_base) - : delegate_(delegate), - xrandr_event_base_(xrandr_event_base), - tick_clock_(new base::DefaultTickClock) { - startup_time_ = tick_clock_->NowTicks(); -} - -NativeDisplayEventDispatcherX11::~NativeDisplayEventDispatcherX11() {} - -bool NativeDisplayEventDispatcherX11::CanDispatchEvent( - const ui::PlatformEvent& event) { - return (event->type - xrandr_event_base_ == RRScreenChangeNotify) || - (event->type - xrandr_event_base_ == RRNotify); -} - -uint32_t NativeDisplayEventDispatcherX11::DispatchEvent( - const ui::PlatformEvent& event) { - if (event->type - xrandr_event_base_ == RRScreenChangeNotify) { - VLOG(1) << "Received RRScreenChangeNotify event"; - delegate_->UpdateXRandRConfiguration(event); - return ui::POST_DISPATCH_PERFORM_DEFAULT; - } - - // Bail out early for everything except RRNotify_OutputChange events - // about an output getting connected or disconnected. - if (event->type - xrandr_event_base_ != RRNotify) - return ui::POST_DISPATCH_PERFORM_DEFAULT; - const XRRNotifyEvent* notify_event = reinterpret_cast<XRRNotifyEvent*>(event); - if (notify_event->subtype != RRNotify_OutputChange) - return ui::POST_DISPATCH_PERFORM_DEFAULT; - const XRROutputChangeNotifyEvent* output_change_event = - reinterpret_cast<XRROutputChangeNotifyEvent*>(event); - const int action = output_change_event->connection; - if (action != RR_Connected && action != RR_Disconnected) - return ui::POST_DISPATCH_PERFORM_DEFAULT; - - const bool connected = (action == RR_Connected); - VLOG(1) << "Received RRNotify_OutputChange event:" - << " output=" << output_change_event->output - << " crtc=" << output_change_event->crtc - << " mode=" << output_change_event->mode - << " action=" << (connected ? "connected" : "disconnected"); - - bool check_cache = (tick_clock_->NowTicks() - startup_time_) - .InMilliseconds() <= kUseCacheAfterStartupMs; - - if (check_cache) { - bool found_changed_output = false; - std::vector<DisplaySnapshot*> cached_outputs = - delegate_->GetCachedDisplays(); - for (std::vector<DisplaySnapshot*>::const_iterator it = - cached_outputs.begin(); - it != cached_outputs.end(); - ++it) { - const DisplaySnapshotX11* x11_output = - static_cast<const DisplaySnapshotX11*>(*it); - const DisplayModeX11* x11_mode = - static_cast<const DisplayModeX11*>(x11_output->current_mode()); - RRMode mode_id = x11_mode ? x11_mode->mode_id() : None; - - // Update if we failed to fetch the external display's ID before. - // Internal display's EDID should always be available. - bool display_id_needs_update = - x11_output->type() != DISPLAY_CONNECTION_TYPE_INTERNAL && - !x11_output->display_id(); - - if (x11_output->output() == output_change_event->output) { - if (connected && x11_output->crtc() == output_change_event->crtc && - mode_id == output_change_event->mode && - !display_id_needs_update) { - VLOG(1) << "Ignoring event describing already-cached state"; - return ui::POST_DISPATCH_PERFORM_DEFAULT; - } - found_changed_output = true; - break; - } - } - - if (!connected && !found_changed_output) { - VLOG(1) << "Ignoring event describing already-disconnected output"; - return ui::POST_DISPATCH_PERFORM_DEFAULT; - } - } - - delegate_->NotifyDisplayObservers(); - - return ui::POST_DISPATCH_PERFORM_DEFAULT; -} - -void NativeDisplayEventDispatcherX11::SetTickClockForTest( - std::unique_ptr<base::TickClock> tick_clock) { - tick_clock_ = std::move(tick_clock); - startup_time_ = tick_clock_->NowTicks(); -} - -} // namespace display diff --git a/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.h b/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.h deleted file mode 100644 index a25bfea1286..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.h +++ /dev/null @@ -1,55 +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_DISPLAY_MANAGER_CHROMEOS_X11_NATIVE_DISPLAY_EVENT_DISPATCHER_X11_H_ -#define UI_DISPLAY_MANAGER_CHROMEOS_X11_NATIVE_DISPLAY_EVENT_DISPATCHER_X11_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "base/time/tick_clock.h" -#include "base/time/time.h" -#include "ui/display/manager/chromeos/x11/native_display_delegate_x11.h" -#include "ui/events/platform/platform_event_dispatcher.h" - -namespace display { - -// The implementation is interested in the cases of RRNotify events which -// correspond to output add/remove events. Note that Output add/remove events -// are sent in response to our own reconfiguration operations so spurious events -// are common. Spurious events will have no effect. -class DISPLAY_MANAGER_EXPORT NativeDisplayEventDispatcherX11 - : public ui::PlatformEventDispatcher { - public: - NativeDisplayEventDispatcherX11( - NativeDisplayDelegateX11::HelperDelegate* delegate, - int xrandr_event_base); - ~NativeDisplayEventDispatcherX11() override; - - // ui::PlatformEventDispatcher: - bool CanDispatchEvent(const ui::PlatformEvent& event) override; - uint32_t DispatchEvent(const ui::PlatformEvent& event) override; - - void SetTickClockForTest(std::unique_ptr<base::TickClock> tick_clock); - - // How long the cached output is valid after startup. - static const int kUseCacheAfterStartupMs; - - private: - NativeDisplayDelegateX11::HelperDelegate* delegate_; // Not owned. - - // The base of the event numbers used to represent XRandr events used in - // decoding events regarding output add/remove. - int xrandr_event_base_; - - base::TimeTicks startup_time_; - - std::unique_ptr<base::TickClock> tick_clock_; - - DISALLOW_COPY_AND_ASSIGN(NativeDisplayEventDispatcherX11); -}; - -} // namespace display - -#endif // UI_DISPLAY_MANAGER_CHROMEOS_X11_NATIVE_DISPLAY_EVENT_DISPATCHER_X11_H_ diff --git a/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc b/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc deleted file mode 100644 index 9168ef67534..00000000000 --- a/chromium/ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdint.h> -#include <X11/extensions/Xrandr.h> - -#undef Bool -#undef None - -#include <algorithm> -#include <memory> -#include <vector> - -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/test/simple_test_tick_clock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/display/manager/chromeos/x11/display_mode_x11.h" -#include "ui/display/manager/chromeos/x11/display_snapshot_x11.h" -#include "ui/display/manager/chromeos/x11/native_display_delegate_x11.h" -#include "ui/display/manager/chromeos/x11/native_display_event_dispatcher_x11.h" - -namespace display { - -namespace { - -std::unique_ptr<DisplaySnapshotX11> CreateOutput(int64_t id, - DisplayConnectionType type, - RROutput output, - RRCrtc crtc) { - static const DisplayModeX11 kDefaultDisplayMode(gfx::Size(1, 1), false, 60.0f, - 20); - std::vector<std::unique_ptr<const DisplayMode>> modes; - const DisplayMode* mode; - - modes.push_back(kDefaultDisplayMode.Clone()); - mode = modes.front().get(); - - return base::MakeUnique<DisplaySnapshotX11>( - id, gfx::Point(0, 0), gfx::Size(0, 0), type, false, false, std::string(), - std::move(modes), std::vector<uint8_t>(), mode, nullptr, output, crtc, 0); -} - -std::unique_ptr<DisplaySnapshotX11> CreateExternalOutput(RROutput output, - RRCrtc crtc) { - return CreateOutput(static_cast<int64_t>(output), - DISPLAY_CONNECTION_TYPE_UNKNOWN, output, crtc); -} - -std::unique_ptr<DisplaySnapshotX11> CreateInternalOutput(RROutput output, - RRCrtc crtc) { - return CreateOutput(0, DISPLAY_CONNECTION_TYPE_INTERNAL, output, crtc); -} - -class TestHelperDelegate : public NativeDisplayDelegateX11::HelperDelegate { - public: - TestHelperDelegate(); - ~TestHelperDelegate() override; - - int num_calls_update_xrandr_config() const { - return num_calls_update_xrandr_config_; - } - - int num_calls_notify_observers() const { return num_calls_notify_observers_; } - - void SetCachedOutputs( - const std::vector<std::unique_ptr<DisplaySnapshot>>& outputs) { - cached_outputs_.resize(outputs.size()); - std::transform(outputs.cbegin(), outputs.cend(), cached_outputs_.begin(), - [](const std::unique_ptr<DisplaySnapshot>& item) { - return item.get(); - }); - } - - // NativeDisplayDelegateX11::HelperDelegate overrides: - void UpdateXRandRConfiguration(const base::NativeEvent& event) override; - std::vector<DisplaySnapshot*> GetCachedDisplays() const override; - void NotifyDisplayObservers() override; - - private: - int num_calls_update_xrandr_config_; - int num_calls_notify_observers_; - - std::vector<DisplaySnapshot*> cached_outputs_; - - DISALLOW_COPY_AND_ASSIGN(TestHelperDelegate); -}; - -TestHelperDelegate::TestHelperDelegate() - : num_calls_update_xrandr_config_(0), num_calls_notify_observers_(0) {} - -TestHelperDelegate::~TestHelperDelegate() {} - -void TestHelperDelegate::UpdateXRandRConfiguration( - const base::NativeEvent& event) { - ++num_calls_update_xrandr_config_; -} - -std::vector<DisplaySnapshot*> TestHelperDelegate::GetCachedDisplays() const { - return cached_outputs_; -} - -void TestHelperDelegate::NotifyDisplayObservers() { - ++num_calls_notify_observers_; -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeDisplayEventDispatcherX11Test - -class NativeDisplayEventDispatcherX11Test : public testing::Test { - public: - NativeDisplayEventDispatcherX11Test(); - ~NativeDisplayEventDispatcherX11Test() override; - - protected: - void DispatchScreenChangeEvent(); - void DispatchOutputChangeEvent(RROutput output, - RRCrtc crtc, - RRMode mode, - bool connected); - - int xrandr_event_base_; - std::unique_ptr<TestHelperDelegate> helper_delegate_; - std::unique_ptr<NativeDisplayEventDispatcherX11> dispatcher_; - base::SimpleTestTickClock* test_tick_clock_; // Owned by |dispatcher_|. - - private: - DISALLOW_COPY_AND_ASSIGN(NativeDisplayEventDispatcherX11Test); -}; - -NativeDisplayEventDispatcherX11Test::NativeDisplayEventDispatcherX11Test() - : xrandr_event_base_(10), - helper_delegate_(new TestHelperDelegate()), - dispatcher_(new NativeDisplayEventDispatcherX11(helper_delegate_.get(), - xrandr_event_base_)), - test_tick_clock_(new base::SimpleTestTickClock) { - test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(1)); - dispatcher_->SetTickClockForTest( - std::unique_ptr<base::TickClock>(test_tick_clock_)); -} - -NativeDisplayEventDispatcherX11Test::~NativeDisplayEventDispatcherX11Test() {} - -void NativeDisplayEventDispatcherX11Test::DispatchScreenChangeEvent() { - XRRScreenChangeNotifyEvent event = {0}; - event.type = xrandr_event_base_ + RRScreenChangeNotify; - - dispatcher_->DispatchEvent(reinterpret_cast<const ui::PlatformEvent>(&event)); -} - -void NativeDisplayEventDispatcherX11Test::DispatchOutputChangeEvent( - RROutput output, - RRCrtc crtc, - RRMode mode, - bool connected) { - XRROutputChangeNotifyEvent event = {0}; - event.type = xrandr_event_base_ + RRNotify; - event.subtype = RRNotify_OutputChange; - event.output = output; - event.crtc = crtc; - event.mode = mode; - event.connection = connected ? RR_Connected : RR_Disconnected; - - dispatcher_->DispatchEvent(reinterpret_cast<const ui::PlatformEvent>(&event)); -} - -} // namespace - -TEST_F(NativeDisplayEventDispatcherX11Test, OnScreenChangedEvent) { - DispatchScreenChangeEvent(); - EXPECT_EQ(1, helper_delegate_->num_calls_update_xrandr_config()); - EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnFirstEvent) { - DispatchOutputChangeEvent(1, 10, 20, true); - EXPECT_EQ(0, helper_delegate_->num_calls_update_xrandr_config()); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationAfterSecondEvent) { - DispatchOutputChangeEvent(1, 10, 20, true); - - // Simulate addition of the first output to the cached output list. - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDisconnect) { - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(1, 10, 20, false); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnModeChange) { - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(1, 10, 21, true); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnSecondOutput) { - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDifferentCrtc) { - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(1, 11, 20, true); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, - CheckNotificationOnSecondOutputDisconnect) { - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - outputs.push_back(CreateExternalOutput(2, 11)); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(2, 11, 20, false); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, - AvoidDuplicateNotificationOnSecondOutputDisconnect) { - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - outputs.push_back(CreateExternalOutput(2, 11)); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(2, 11, 20, false); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); - - // Simulate removal of second output from cached output list. - outputs.erase(outputs.begin() + 1); - helper_delegate_->SetCachedOutputs(outputs); - - DispatchOutputChangeEvent(2, 11, 20, false); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, ForceUpdateAfterCacheExpiration) { - // +1 to compenstate a possible rounding error. - const int kHalfOfExpirationMs = - NativeDisplayEventDispatcherX11::kUseCacheAfterStartupMs / 2 + 1; - - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateExternalOutput(1, 10)); - outputs.push_back(CreateExternalOutput(2, 11)); - helper_delegate_->SetCachedOutputs(outputs); - - EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); - - // Duplicated event will be ignored during the startup. - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); - - test_tick_clock_->Advance( - base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs)); - - // Duplicated event will still be ignored. - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); - - // The startup timeout has been elapsed. Duplicated event - // should not be ignored. - test_tick_clock_->Advance( - base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs)); - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); - - // Sending the same event immediately shoudldn't be ignored. - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers()); - - // Advancing time further should not change the behavior. - test_tick_clock_->Advance( - base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs)); - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(3, helper_delegate_->num_calls_notify_observers()); - - test_tick_clock_->Advance( - base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs)); - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(4, helper_delegate_->num_calls_notify_observers()); -} - -TEST_F(NativeDisplayEventDispatcherX11Test, UpdateMissingExternalDisplayId) { - std::vector<std::unique_ptr<DisplaySnapshot>> outputs; - outputs.push_back(CreateInternalOutput(1, 10)); - helper_delegate_->SetCachedOutputs(outputs); - - ASSERT_EQ(0, helper_delegate_->num_calls_notify_observers()); - - // Internal display's ID can be zero and not updated. - DispatchOutputChangeEvent(1, 10, 20, true); - EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers()); - - outputs.clear(); - outputs.push_back(CreateOutput(0, DISPLAY_CONNECTION_TYPE_UNKNOWN, 2, 11)); - helper_delegate_->SetCachedOutputs(outputs); - - // External display should be updated if the id is zero. - DispatchOutputChangeEvent(2, 11, 20, true); - EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers()); -} - -} // namespace display diff --git a/chromium/ui/display/manager/display_layout_store.cc b/chromium/ui/display/manager/display_layout_store.cc index 7e94b343fee..08721f48798 100644 --- a/chromium/ui/display/manager/display_layout_store.cc +++ b/chromium/ui/display/manager/display_layout_store.cc @@ -88,7 +88,7 @@ void DisplayLayoutStore::RegisterLayoutForDisplayIdList( const DisplayLayout& DisplayLayoutStore::GetRegisteredDisplayLayout( const DisplayIdList& list) { - DCHECK_NE(1u, list.size()); + DCHECK_GT(list.size(), 1u); const auto iter = layouts_.find(list); const DisplayLayout* layout = iter != layouts_.end() ? iter->second.get() diff --git a/chromium/ui/display/manager/display_manager.cc b/chromium/ui/display/manager/display_manager.cc index e5e3951608b..7a4b06c669f 100644 --- a/chromium/ui/display/manager/display_manager.cc +++ b/chromium/ui/display/manager/display_manager.cc @@ -207,6 +207,12 @@ void DisplayManager::InitDefaultDisplay() { OnNativeDisplaysChanged(info_list); } +void DisplayManager::UpdateInternalDisplay( + const ManagedDisplayInfo& display_info) { + DCHECK(Display::HasInternalDisplay()); + InsertAndUpdateDisplayInfo(display_info); +} + void DisplayManager::RefreshFontParams() { #if defined(OS_CHROMEOS) // Use the largest device scale factor among currently active displays. Non @@ -290,7 +296,7 @@ void DisplayManager::SetLayoutForCurrentDisplays( } if (delegate_) - delegate_->PostDisplayConfigurationChange(false); + delegate_->PostDisplayConfigurationChange(); } const Display& DisplayManager::GetDisplayForId(int64_t display_id) const { @@ -446,7 +452,6 @@ void DisplayManager::RegisterDisplayProperty( const gfx::Insets* overscan_insets, const gfx::Size& resolution_in_pixels, float device_scale_factor, - ColorCalibrationProfile color_profile, const TouchCalibrationData* touch_calibration_data) { if (display_info_.find(display_id) == display_info_.end()) display_info_[display_id] = @@ -460,7 +465,6 @@ void DisplayManager::RegisterDisplayProperty( Display::ROTATION_SOURCE_USER); display_info_[display_id].SetRotation(rotation, Display::ROTATION_SOURCE_ACTIVE); - display_info_[display_id].SetColorProfile(color_profile); // 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. @@ -513,7 +517,7 @@ void DisplayManager::RegisterDisplayRotationProperties( registered_internal_display_rotation_lock_ = rotation_lock; registered_internal_display_rotation_ = rotation; if (delegate_) - delegate_->PostDisplayConfigurationChange(false); + delegate_->PostDisplayConfigurationChange(); } scoped_refptr<ManagedDisplayMode> DisplayManager::GetSelectedModeForDisplayId( @@ -551,28 +555,6 @@ gfx::Insets DisplayManager::GetOverscanInsets(int64_t display_id) const { : gfx::Insets(); } -void DisplayManager::SetColorCalibrationProfile( - int64_t display_id, - ColorCalibrationProfile profile) { -#if defined(OS_CHROMEOS) - if (!display_info_[display_id].IsColorProfileAvailable(profile)) - return; - - if (delegate_) - delegate_->PreDisplayConfigurationChange(false); - // Just sets color profile if it's not running on ChromeOS (like tests). - if (!configure_displays_ || - delegate_->display_configurator()->SetColorCalibrationProfile(display_id, - profile)) { - display_info_[display_id].SetColorProfile(profile); - UMA_HISTOGRAM_ENUMERATION("ChromeOS.Display.ColorProfile", profile, - NUM_COLOR_PROFILES); - } - if (delegate_) - delegate_->PostDisplayConfigurationChange(false); -#endif -} - void DisplayManager::OnNativeDisplaysChanged( const DisplayInfoList& updated_displays) { if (updated_displays.empty()) { @@ -902,14 +884,8 @@ void DisplayManager::UpdateDisplaysWith( if (delegate_ && primary_metrics) NotifyMetricsChanged(screen_->GetPrimaryDisplay(), primary_metrics); - bool must_clear_window = false; -#if defined(USE_X11) && defined(OS_CHROMEOS) - must_clear_window = - !display_changes.empty() && base::SysInfo::IsRunningOnChromeOS(); -#endif - if (delegate_) - delegate_->PostDisplayConfigurationChange(must_clear_window); + delegate_->PostDisplayConfigurationChange(); // Create the mirroring window asynchronously after all displays // are added so that it can mirror the display newly added. This can @@ -1400,18 +1376,6 @@ void DisplayManager::InsertAndUpdateDisplayInfo( } } display_info_[new_info.id()].UpdateDisplaySize(); - OnDisplayInfoUpdated(display_info_[new_info.id()]); -} - -void DisplayManager::OnDisplayInfoUpdated( - const ManagedDisplayInfo& display_info) { -#if defined(OS_CHROMEOS) - ColorCalibrationProfile color_profile = display_info.color_profile(); - if (color_profile != COLOR_PROFILE_STANDARD) { - delegate_->display_configurator()->SetColorCalibrationProfile( - display_info.id(), color_profile); - } -#endif } Display DisplayManager::CreateDisplayFromDisplayInfoById(int64_t id) { diff --git a/chromium/ui/display/manager/display_manager.h b/chromium/ui/display/manager/display_manager.h index 71d3cc69aba..676558dc08e 100644 --- a/chromium/ui/display/manager/display_manager.h +++ b/chromium/ui/display/manager/display_manager.h @@ -70,7 +70,7 @@ class DISPLAY_MANAGER_EXPORT DisplayManager // |clear_focus| is true, the implementation should deactivate the active // window and set the focus window to NULL. virtual void PreDisplayConfigurationChange(bool clear_focus) = 0; - virtual void PostDisplayConfigurationChange(bool must_clear_window) = 0; + virtual void PostDisplayConfigurationChange() = 0; #if defined(OS_CHROMEOS) // Get the DisplayConfigurator. @@ -127,6 +127,9 @@ class DISPLAY_MANAGER_EXPORT DisplayManager // Initialize default display. void InitDefaultDisplay(); + // Update the internal display's display info. + void UpdateInternalDisplay(const ManagedDisplayInfo& display_info); + // Initializes font related params that depends on display configuration. void RefreshFontParams(); @@ -190,7 +193,6 @@ class DISPLAY_MANAGER_EXPORT DisplayManager const gfx::Insets* overscan_insets, const gfx::Size& resolution_in_pixels, float device_scale_factor, - ColorCalibrationProfile color_profile, const TouchCalibrationData* touch_calibration_data); // Register stored rotation properties for the internal display. @@ -233,10 +235,6 @@ class DISPLAY_MANAGER_EXPORT DisplayManager // display. gfx::Insets GetOverscanInsets(int64_t display_id) const; - // Sets the color calibration of the display to |profile|. - void SetColorCalibrationProfile(int64_t display_id, - ColorCalibrationProfile profile); - // Called when display configuration has changed. The new display // configurations is passed as a vector of Display object, which contains each // display's new infomration. @@ -413,9 +411,6 @@ class DISPLAY_MANAGER_EXPORT DisplayManager // |GetDisplayInfo| to get the correct ManagedDisplayInfo for a display. void InsertAndUpdateDisplayInfo(const ManagedDisplayInfo& new_info); - // Called when the display info is updated through InsertAndUpdateDisplayInfo. - void OnDisplayInfoUpdated(const ManagedDisplayInfo& display_info); - // Creates a display object from the ManagedDisplayInfo for // |display_id|. Display CreateDisplayFromDisplayInfoById(int64_t display_id); diff --git a/chromium/ui/display/manager/forwarding_display_delegate.cc b/chromium/ui/display/manager/forwarding_display_delegate.cc index c95a83cefb3..5f3a7d9fae0 100644 --- a/chromium/ui/display/manager/forwarding_display_delegate.cc +++ b/chromium/ui/display/manager/forwarding_display_delegate.cc @@ -8,7 +8,7 @@ #include "base/bind.h" #include "mojo/public/cpp/bindings/sync_call_restrictions.h" -#include "ui/display/types/display_snapshot_mojo.h" +#include "ui/display/types/display_snapshot.h" namespace display { @@ -31,10 +31,6 @@ void ForwardingDisplayDelegate::Initialize() { delegate_->Initialize(std::move(observer), &snapshots_); } -void ForwardingDisplayDelegate::GrabServer() {} - -void ForwardingDisplayDelegate::UngrabServer() {} - void ForwardingDisplayDelegate::TakeDisplayControl( const DisplayControlCallback& callback) { delegate_->TakeDisplayControl(callback); @@ -45,12 +41,6 @@ void ForwardingDisplayDelegate::RelinquishDisplayControl( delegate_->TakeDisplayControl(callback); } -void ForwardingDisplayDelegate::SyncWithServer() {} - -void ForwardingDisplayDelegate::SetBackgroundColor(uint32_t color_argb) {} - -void ForwardingDisplayDelegate::ForceDPMSOn() {} - void ForwardingDisplayDelegate::GetDisplays( const GetDisplaysCallback& callback) { if (!use_delegate_) { @@ -63,9 +53,6 @@ void ForwardingDisplayDelegate::GetDisplays( base::Unretained(this), callback)); } -void ForwardingDisplayDelegate::AddMode(const DisplaySnapshot& snapshot, - const DisplayMode* mode) {} - void ForwardingDisplayDelegate::Configure(const DisplaySnapshot& snapshot, const DisplayMode* mode, const gfx::Point& origin, @@ -84,8 +71,6 @@ void ForwardingDisplayDelegate::Configure(const DisplaySnapshot& snapshot, callback); } -void ForwardingDisplayDelegate::CreateFrameBuffer(const gfx::Size& size) {} - void ForwardingDisplayDelegate::GetHDCPState( const DisplaySnapshot& snapshot, const GetHDCPStateCallback& callback) { @@ -99,18 +84,6 @@ void ForwardingDisplayDelegate::SetHDCPState( delegate_->SetHDCPState(snapshot.display_id(), state, callback); } -std::vector<ColorCalibrationProfile> -ForwardingDisplayDelegate::GetAvailableColorCalibrationProfiles( - const DisplaySnapshot& output) { - return std::vector<ColorCalibrationProfile>(); -} - -bool ForwardingDisplayDelegate::SetColorCalibrationProfile( - const DisplaySnapshot& output, - ColorCalibrationProfile new_profile) { - return false; -} - bool ForwardingDisplayDelegate::SetColorCorrection( const DisplaySnapshot& output, const std::vector<GammaRampRGBEntry>& degamma_lut, @@ -148,7 +121,7 @@ void ForwardingDisplayDelegate::OnConfigurationChanged() { void ForwardingDisplayDelegate::StoreAndForwardDisplays( const GetDisplaysCallback& callback, - std::vector<std::unique_ptr<DisplaySnapshotMojo>> snapshots) { + std::vector<std::unique_ptr<DisplaySnapshot>> snapshots) { for (auto& observer : observers_) observer.OnDisplaySnapshotsInvalidated(); snapshots_ = std::move(snapshots); diff --git a/chromium/ui/display/manager/forwarding_display_delegate.h b/chromium/ui/display/manager/forwarding_display_delegate.h index 17cded9103b..5e0eb3f352e 100644 --- a/chromium/ui/display/manager/forwarding_display_delegate.h +++ b/chromium/ui/display/manager/forwarding_display_delegate.h @@ -19,44 +19,33 @@ namespace display { -class DisplaySnapshotMojo; +class DisplaySnapshot; // NativeDisplayDelegate implementation that forwards calls to a real // NativeDisplayDelegate in another process. Only forwards the methods // implemented by Ozone DRM, other method won't do anything. class DISPLAY_MANAGER_EXPORT ForwardingDisplayDelegate : public NativeDisplayDelegate, - public NON_EXPORTED_BASE(mojom::NativeDisplayObserver) { + public mojom::NativeDisplayObserver { public: explicit ForwardingDisplayDelegate(mojom::NativeDisplayDelegatePtr delegate); ~ForwardingDisplayDelegate() override; // display::NativeDisplayDelegate: void Initialize() override; - void GrabServer() override; - void UngrabServer() override; void TakeDisplayControl(const DisplayControlCallback& callback) override; void RelinquishDisplayControl( const DisplayControlCallback& callback) override; - void SyncWithServer() override; - void SetBackgroundColor(uint32_t color_argb) override; - void ForceDPMSOn() override; void GetDisplays(const GetDisplaysCallback& callback) override; - void AddMode(const DisplaySnapshot& output, const DisplayMode* mode) override; void Configure(const DisplaySnapshot& output, const DisplayMode* mode, const gfx::Point& origin, const ConfigureCallback& callback) override; - void CreateFrameBuffer(const gfx::Size& size) override; void GetHDCPState(const DisplaySnapshot& output, const GetHDCPStateCallback& callback) override; void SetHDCPState(const DisplaySnapshot& output, HDCPState state, const SetHDCPStateCallback& callback) override; - std::vector<ColorCalibrationProfile> GetAvailableColorCalibrationProfiles( - const DisplaySnapshot& output) override; - bool SetColorCalibrationProfile(const DisplaySnapshot& output, - ColorCalibrationProfile new_profile) override; bool SetColorCorrection(const DisplaySnapshot& output, const std::vector<GammaRampRGBEntry>& degamma_lut, const std::vector<GammaRampRGBEntry>& gamma_lut, @@ -72,7 +61,7 @@ class DISPLAY_MANAGER_EXPORT ForwardingDisplayDelegate // Stores display snapshots and forwards pointers to |callback|. void StoreAndForwardDisplays( const GetDisplaysCallback& callback, - std::vector<std::unique_ptr<DisplaySnapshotMojo>> snapshots); + std::vector<std::unique_ptr<DisplaySnapshot>> snapshots); // Forwards display snapshot pointers to |callback|. void ForwardDisplays(const GetDisplaysCallback& callback); @@ -86,7 +75,7 @@ class DISPLAY_MANAGER_EXPORT ForwardingDisplayDelegate // Display snapshots are owned here but accessed via raw pointers elsewhere. // Call OnDisplaySnapshotsInvalidated() on observers before invalidating them. - std::vector<std::unique_ptr<DisplaySnapshotMojo>> snapshots_; + std::vector<std::unique_ptr<DisplaySnapshot>> snapshots_; base::ObserverList<display::NativeDisplayObserver> observers_; diff --git a/chromium/ui/display/manager/managed_display_info.cc b/chromium/ui/display/manager/managed_display_info.cc index 850aa5468a7..82b1ea3cc21 100644 --- a/chromium/ui/display/manager/managed_display_info.cc +++ b/chromium/ui/display/manager/managed_display_info.cc @@ -304,8 +304,7 @@ ManagedDisplayInfo::ManagedDisplayInfo() configured_ui_scale_(1.0f), native_(false), is_aspect_preserving_scaling_(false), - clear_overscan_insets_(false), - color_profile_(COLOR_PROFILE_STANDARD) {} + clear_overscan_insets_(false) {} ManagedDisplayInfo::ManagedDisplayInfo(int64_t id, const std::string& name, @@ -322,8 +321,7 @@ ManagedDisplayInfo::ManagedDisplayInfo(int64_t id, configured_ui_scale_(1.0f), native_(false), is_aspect_preserving_scaling_(false), - clear_overscan_insets_(false), - color_profile_(COLOR_PROFILE_STANDARD) {} + clear_overscan_insets_(false) {} ManagedDisplayInfo::ManagedDisplayInfo(const ManagedDisplayInfo& other) = default; @@ -363,7 +361,6 @@ void ManagedDisplayInfo::Copy(const ManagedDisplayInfo& native_info) { size_in_pixel_ = native_info.size_in_pixel_; is_aspect_preserving_scaling_ = native_info.is_aspect_preserving_scaling_; display_modes_ = native_info.display_modes_; - available_color_profiles_ = native_info.available_color_profiles_; maximum_cursor_size_ = native_info.maximum_cursor_size_; // Rotation, ui_scale, color_profile and overscan are given by preference, @@ -383,7 +380,6 @@ void ManagedDisplayInfo::Copy(const ManagedDisplayInfo& native_info) { rotations_ = native_info.rotations_; configured_ui_scale_ = native_info.configured_ui_scale_; - color_profile_ = native_info.color_profile(); } } @@ -499,16 +495,6 @@ std::string ManagedDisplayInfo::ToFullString() const { return ToString() + ", display_modes==" + display_modes_str; } -void ManagedDisplayInfo::SetColorProfile(ColorCalibrationProfile profile) { - if (IsColorProfileAvailable(profile)) - color_profile_ = profile; -} - -bool ManagedDisplayInfo::IsColorProfileAvailable( - ColorCalibrationProfile profile) const { - return base::ContainsValue(available_color_profiles_, profile); -} - bool ManagedDisplayInfo::Use125DSFForUIScaling() const { return Display::IsInternalDisplayId(id_); } diff --git a/chromium/ui/display/manager/managed_display_info.h b/chromium/ui/display/manager/managed_display_info.h index e52e05e248c..38c2aab2b36 100644 --- a/chromium/ui/display/manager/managed_display_info.h +++ b/chromium/ui/display/manager/managed_display_info.h @@ -285,24 +285,6 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo { // empty size. gfx::Size GetNativeModeSize() const; - ColorCalibrationProfile color_profile() const { return color_profile_; } - - // Sets the color profile. It will ignore if the specified |profile| is not in - // |available_color_profiles_|. - void SetColorProfile(ColorCalibrationProfile profile); - - // Returns true if |profile| is in |available_color_profiles_|. - bool IsColorProfileAvailable(ColorCalibrationProfile profile) const; - - const std::vector<ColorCalibrationProfile>& available_color_profiles() const { - return available_color_profiles_; - } - - void set_available_color_profiles( - const std::vector<ColorCalibrationProfile>& profiles) { - available_color_profiles_ = profiles; - } - bool is_aspect_preserving_scaling() const { return is_aspect_preserving_scaling_; } @@ -383,12 +365,6 @@ class DISPLAY_MANAGER_EXPORT ManagedDisplayInfo { // The list of modes supported by this display. ManagedDisplayModeList display_modes_; - // The current profile of the color calibration. - ColorCalibrationProfile color_profile_; - - // The list of available variations for the color calibration. - std::vector<ColorCalibrationProfile> available_color_profiles_; - // Maximum cursor size. gfx::Size maximum_cursor_size_; diff --git a/chromium/ui/display/mojo/BUILD.gn b/chromium/ui/display/mojo/BUILD.gn index 7b213c1950c..92baf0cdefb 100644 --- a/chromium/ui/display/mojo/BUILD.gn +++ b/chromium/ui/display/mojo/BUILD.gn @@ -10,7 +10,7 @@ mojom("interfaces") { "display_constants.mojom", "display_layout.mojom", "display_mode.mojom", - "display_snapshot_mojo.mojom", + "display_snapshot.mojom", "gamma_ramp_rgb_entry.mojom", "native_display_delegate.mojom", ] diff --git a/chromium/ui/display/mojo/display_snapshot_mojo.mojom b/chromium/ui/display/mojo/display_snapshot.mojom index 8b7ce1c5075..839a9c07c9c 100644 --- a/chromium/ui/display/mojo/display_snapshot_mojo.mojom +++ b/chromium/ui/display/mojo/display_snapshot.mojom @@ -9,8 +9,8 @@ import "ui/display/mojo/display_constants.mojom"; import "ui/display/mojo/display_mode.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; -// Corresponds to display::DisplaySnapshotMojo. -struct DisplaySnapshotMojo { +// Corresponds to display::DisplaySnapshot. +struct DisplaySnapshot { int64 display_id; gfx.mojom.Point origin; gfx.mojom.Size physical_size; diff --git a/chromium/ui/display/mojo/display_snapshot.typemap b/chromium/ui/display/mojo/display_snapshot.typemap new file mode 100644 index 00000000000..4668070524a --- /dev/null +++ b/chromium/ui/display/mojo/display_snapshot.typemap @@ -0,0 +1,17 @@ +# 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. + +mojom = "//ui/display/mojo/display_snapshot.mojom" +public_headers = [ "//ui/display/types/display_snapshot.h" ] +traits_headers = [ "//ui/display/mojo/display_snapshot_struct_traits.h" ] +sources = [ + "//ui/display/mojo/display_snapshot_struct_traits.cc", +] +public_deps = [ + "//ui/display", +] +deps = [ + "//ui/gfx/geometry", +] +type_mappings = [ "display.mojom.DisplaySnapshot=std::unique_ptr<display::DisplaySnapshot>[move_only]" ] diff --git a/chromium/ui/display/mojo/display_snapshot_mojo.typemap b/chromium/ui/display/mojo/display_snapshot_mojo.typemap deleted file mode 100644 index bd06b0fb742..00000000000 --- a/chromium/ui/display/mojo/display_snapshot_mojo.typemap +++ /dev/null @@ -1,17 +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. - -mojom = "//ui/display/mojo/display_snapshot_mojo.mojom" -public_headers = [ "//ui/display/types/display_snapshot_mojo.h" ] -traits_headers = [ "//ui/display/mojo/display_snapshot_mojo_struct_traits.h" ] -sources = [ - "//ui/display/mojo/display_snapshot_mojo_struct_traits.cc", -] -public_deps = [ - "//ui/display", -] -deps = [ - "//ui/gfx/geometry", -] -type_mappings = [ "display.mojom.DisplaySnapshotMojo=std::unique_ptr<display::DisplaySnapshotMojo>[move_only]" ] diff --git a/chromium/ui/display/mojo/display_snapshot_mojo_struct_traits.cc b/chromium/ui/display/mojo/display_snapshot_struct_traits.cc index 1a370dc87ab..325f52be2d7 100644 --- a/chromium/ui/display/mojo/display_snapshot_mojo_struct_traits.cc +++ b/chromium/ui/display/mojo/display_snapshot_struct_traits.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/display/mojo/display_snapshot_mojo_struct_traits.h" +#include "ui/display/mojo/display_snapshot_struct_traits.h" #include "ui/display/types/display_constants.h" #include "ui/gfx/geometry/size.h" @@ -32,10 +32,9 @@ static uint64_t GetModeIndex( // static std::vector<std::unique_ptr<display::DisplayMode>> -StructTraits<display::mojom::DisplaySnapshotMojoDataView, - std::unique_ptr<display::DisplaySnapshotMojo>>:: - modes( - const std::unique_ptr<display::DisplaySnapshotMojo>& display_snapshot) { +StructTraits<display::mojom::DisplaySnapshotDataView, + std::unique_ptr<display::DisplaySnapshot>>:: + modes(const std::unique_ptr<display::DisplaySnapshot>& display_snapshot) { std::vector<std::unique_ptr<display::DisplayMode>> display_mode_list; for (const auto& display_mode : display_snapshot->modes()) @@ -45,28 +44,28 @@ StructTraits<display::mojom::DisplaySnapshotMojoDataView, } // static -uint64_t StructTraits<display::mojom::DisplaySnapshotMojoDataView, - std::unique_ptr<display::DisplaySnapshotMojo>>:: +uint64_t StructTraits<display::mojom::DisplaySnapshotDataView, + std::unique_ptr<display::DisplaySnapshot>>:: current_mode_index( - const std::unique_ptr<display::DisplaySnapshotMojo>& display_snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& display_snapshot) { return GetModeIndex(display_snapshot->modes(), display_snapshot->current_mode()); } // static -uint64_t StructTraits<display::mojom::DisplaySnapshotMojoDataView, - std::unique_ptr<display::DisplaySnapshotMojo>>:: +uint64_t StructTraits<display::mojom::DisplaySnapshotDataView, + std::unique_ptr<display::DisplaySnapshot>>:: native_mode_index( - const std::unique_ptr<display::DisplaySnapshotMojo>& display_snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& display_snapshot) { return GetModeIndex(display_snapshot->modes(), display_snapshot->native_mode()); } // static -bool StructTraits<display::mojom::DisplaySnapshotMojoDataView, - std::unique_ptr<display::DisplaySnapshotMojo>>:: - Read(display::mojom::DisplaySnapshotMojoDataView data, - std::unique_ptr<display::DisplaySnapshotMojo>* out) { +bool StructTraits<display::mojom::DisplaySnapshotDataView, + std::unique_ptr<display::DisplaySnapshot>>:: + Read(display::mojom::DisplaySnapshotDataView data, + std::unique_ptr<display::DisplaySnapshot>* out) { gfx::Point origin; if (!data.ReadOrigin(&origin)) return false; @@ -123,12 +122,12 @@ bool StructTraits<display::mojom::DisplaySnapshotMojoDataView, if (!data.ReadMaximumCursorSize(&maximum_cursor_size)) return false; - *out = base::MakeUnique<display::DisplaySnapshotMojo>( + *out = base::MakeUnique<display::DisplaySnapshot>( data.display_id(), origin, physical_size, type, data.is_aspect_preserving_scaling(), data.has_overscan(), data.has_color_correction_matrix(), display_name, file_path, - data.product_id(), std::move(modes), std::move(edid), current_mode, - native_mode, maximum_cursor_size); + std::move(modes), std::move(edid), current_mode, native_mode, + data.product_id(), maximum_cursor_size); return true; } diff --git a/chromium/ui/display/mojo/display_snapshot_mojo_struct_traits.h b/chromium/ui/display/mojo/display_snapshot_struct_traits.h index 3c66e1768cf..cffec53f77e 100644 --- a/chromium/ui/display/mojo/display_snapshot_mojo_struct_traits.h +++ b/chromium/ui/display/mojo/display_snapshot_struct_traits.h @@ -8,97 +8,97 @@ #include "ipc/ipc_message_utils.h" #include "ui/display/mojo/display_constants_struct_traits.h" #include "ui/display/mojo/display_mode_struct_traits.h" -#include "ui/display/mojo/display_snapshot_mojo.mojom.h" +#include "ui/display/mojo/display_snapshot.mojom.h" #include "ui/display/types/display_mode.h" -#include "ui/display/types/display_snapshot_mojo.h" +#include "ui/display/types/display_snapshot.h" #include "ui/gfx/geometry/mojo/geometry_struct_traits.h" namespace mojo { template <> -struct StructTraits<display::mojom::DisplaySnapshotMojoDataView, - std::unique_ptr<display::DisplaySnapshotMojo>> { +struct StructTraits<display::mojom::DisplaySnapshotDataView, + std::unique_ptr<display::DisplaySnapshot>> { static int64_t display_id( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->display_id(); } static const gfx::Point& origin( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->origin(); } static const gfx::Size& physical_size( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->physical_size(); } static display::DisplayConnectionType type( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->type(); } static bool is_aspect_preserving_scaling( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->is_aspect_preserving_scaling(); } static bool has_overscan( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->has_overscan(); } static bool has_color_correction_matrix( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->has_color_correction_matrix(); } static std::string display_name( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->display_name(); } static const base::FilePath& sys_path( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->sys_path(); } static std::vector<uint8_t> edid( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->edid(); } static std::vector<std::unique_ptr<display::DisplayMode>> modes( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot); + const std::unique_ptr<display::DisplaySnapshot>& snapshot); static uint64_t current_mode_index( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot); + const std::unique_ptr<display::DisplaySnapshot>& snapshot); static bool has_current_mode( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->current_mode() != nullptr; } static uint64_t native_mode_index( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot); + const std::unique_ptr<display::DisplaySnapshot>& snapshot); static bool has_native_mode( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->native_mode() != nullptr; } static int64_t product_id( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->product_id(); } static const gfx::Size& maximum_cursor_size( - const std::unique_ptr<display::DisplaySnapshotMojo>& snapshot) { + const std::unique_ptr<display::DisplaySnapshot>& snapshot) { return snapshot->maximum_cursor_size(); } - static bool Read(display::mojom::DisplaySnapshotMojoDataView data, - std::unique_ptr<display::DisplaySnapshotMojo>* out); + static bool Read(display::mojom::DisplaySnapshotDataView data, + std::unique_ptr<display::DisplaySnapshot>* out); }; } // namespace mojo diff --git a/chromium/ui/display/mojo/display_struct_traits_unittest.cc b/chromium/ui/display/mojo/display_struct_traits_unittest.cc index 42ae82788ec..cf740387f55 100644 --- a/chromium/ui/display/mojo/display_struct_traits_unittest.cc +++ b/chromium/ui/display/mojo/display_struct_traits_unittest.cc @@ -13,12 +13,12 @@ #include "ui/display/display_layout.h" #include "ui/display/mojo/display_layout_struct_traits.h" #include "ui/display/mojo/display_mode_struct_traits.h" -#include "ui/display/mojo/display_snapshot_mojo_struct_traits.h" +#include "ui/display/mojo/display_snapshot_struct_traits.h" #include "ui/display/mojo/display_struct_traits.h" #include "ui/display/mojo/gamma_ramp_rgb_entry_struct_traits.h" #include "ui/display/types/display_constants.h" #include "ui/display/types/display_mode.h" -#include "ui/display/types/display_snapshot_mojo.h" +#include "ui/display/types/display_snapshot.h" #include "ui/display/types/gamma_ramp_rgb_entry.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" @@ -68,8 +68,8 @@ void CheckDisplayModesEqual(const DisplayMode* input, EXPECT_EQ(input->refresh_rate(), output->refresh_rate()); } -void CheckDisplaySnapShotMojoEqual(const DisplaySnapshotMojo& input, - const DisplaySnapshotMojo& output) { +void CheckDisplaySnapShotMojoEqual(const DisplaySnapshot& input, + const DisplaySnapshot& output) { // We want to test each component individually to make sure each data member // was correctly serialized and deserialized. EXPECT_NE(&input, &output); // Make sure they aren't the same object. @@ -285,16 +285,14 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentAndNativeModesNull) { const DisplayMode* native_mode = nullptr; const std::vector<uint8_t> edid = {1}; - std::unique_ptr<DisplaySnapshotMojo> input = - base::MakeUnique<DisplaySnapshotMojo>( - display_id, origin, physical_size, type, is_aspect_preserving_scaling, - has_overscan, has_color_correction_matrix, display_name, sys_path, - product_id, std::move(modes), edid, current_mode, native_mode, - maximum_cursor_size); + std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>( + display_id, origin, physical_size, type, is_aspect_preserving_scaling, + has_overscan, has_color_correction_matrix, display_name, sys_path, + std::move(modes), edid, current_mode, native_mode, product_id, + maximum_cursor_size); - std::unique_ptr<DisplaySnapshotMojo> output; - SerializeAndDeserialize<mojom::DisplaySnapshotMojo>( - DisplaySnapshotMojo::CreateFrom(*input), &output); + std::unique_ptr<DisplaySnapshot> output; + SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); CheckDisplaySnapShotMojoEqual(*input, *output); } @@ -323,16 +321,14 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotCurrentModeNull) { const DisplayMode* native_mode = modes[0].get(); const std::vector<uint8_t> edid = {1}; - std::unique_ptr<DisplaySnapshotMojo> input = - base::MakeUnique<DisplaySnapshotMojo>( - display_id, origin, physical_size, type, is_aspect_preserving_scaling, - has_overscan, has_color_correction_matrix, display_name, sys_path, - product_id, std::move(modes), edid, current_mode, native_mode, - maximum_cursor_size); + std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>( + display_id, origin, physical_size, type, is_aspect_preserving_scaling, + has_overscan, has_color_correction_matrix, display_name, sys_path, + std::move(modes), edid, current_mode, native_mode, product_id, + maximum_cursor_size); - std::unique_ptr<DisplaySnapshotMojo> output; - SerializeAndDeserialize<mojom::DisplaySnapshotMojo>( - DisplaySnapshotMojo::CreateFrom(*input), &output); + std::unique_ptr<DisplaySnapshot> output; + SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); CheckDisplaySnapShotMojoEqual(*input, *output); } @@ -365,16 +361,14 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotExternal) { const DisplayMode* native_mode = modes[2].get(); const std::vector<uint8_t> edid = {2, 3, 4, 5}; - std::unique_ptr<DisplaySnapshotMojo> input = - base::MakeUnique<DisplaySnapshotMojo>( - display_id, origin, physical_size, type, is_aspect_preserving_scaling, - has_overscan, has_color_correction_matrix, display_name, sys_path, - product_id, std::move(modes), edid, current_mode, native_mode, - maximum_cursor_size); + std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>( + display_id, origin, physical_size, type, is_aspect_preserving_scaling, + has_overscan, has_color_correction_matrix, display_name, sys_path, + std::move(modes), edid, current_mode, native_mode, product_id, + maximum_cursor_size); - std::unique_ptr<DisplaySnapshotMojo> output; - SerializeAndDeserialize<mojom::DisplaySnapshotMojo>( - DisplaySnapshotMojo::CreateFrom(*input), &output); + std::unique_ptr<DisplaySnapshot> output; + SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); CheckDisplaySnapShotMojoEqual(*input, *output); } @@ -402,16 +396,14 @@ TEST(DisplayStructTraitsTest, DisplaySnapshotInternal) { const DisplayMode* native_mode = modes[0].get(); const std::vector<uint8_t> edid = {2, 3}; - std::unique_ptr<DisplaySnapshotMojo> input = - base::MakeUnique<DisplaySnapshotMojo>( - display_id, origin, physical_size, type, is_aspect_preserving_scaling, - has_overscan, has_color_correction_matrix, display_name, sys_path, - product_id, std::move(modes), edid, current_mode, native_mode, - maximum_cursor_size); + std::unique_ptr<DisplaySnapshot> input = base::MakeUnique<DisplaySnapshot>( + display_id, origin, physical_size, type, is_aspect_preserving_scaling, + has_overscan, has_color_correction_matrix, display_name, sys_path, + std::move(modes), edid, current_mode, native_mode, product_id, + maximum_cursor_size); - std::unique_ptr<DisplaySnapshotMojo> output; - SerializeAndDeserialize<mojom::DisplaySnapshotMojo>( - DisplaySnapshotMojo::CreateFrom(*input), &output); + std::unique_ptr<DisplaySnapshot> output; + SerializeAndDeserialize<mojom::DisplaySnapshot>(input->Clone(), &output); CheckDisplaySnapShotMojoEqual(*input, *output); } diff --git a/chromium/ui/display/mojo/native_display_delegate.mojom b/chromium/ui/display/mojo/native_display_delegate.mojom index 9ba1f028888..d0d47a80630 100644 --- a/chromium/ui/display/mojo/native_display_delegate.mojom +++ b/chromium/ui/display/mojo/native_display_delegate.mojom @@ -6,7 +6,7 @@ module display.mojom; import "ui/display/mojo/display_constants.mojom"; import "ui/display/mojo/display_mode.mojom"; -import "ui/display/mojo/display_snapshot_mojo.mojom"; +import "ui/display/mojo/display_snapshot.mojom"; import "ui/display/mojo/gamma_ramp_rgb_entry.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; @@ -21,7 +21,8 @@ interface NativeDisplayDelegate { // Initializes and registers the observer. This is synchronous so that ash // initialization has an initial set of displays to use. [Sync] - Initialize(NativeDisplayObserver observer) => (array<DisplaySnapshotMojo> snapshots); + Initialize(NativeDisplayObserver observer) => + (array<DisplaySnapshot> snapshots); // Take control of the displays from any other controlling process. TakeDisplayControl() => (bool result); @@ -30,7 +31,7 @@ interface NativeDisplayDelegate { RelinquishDisplayControl() => (bool result); // Queries for a list of fresh displays. - GetDisplays() => (array<DisplaySnapshotMojo> snapshots); + GetDisplays() => (array<DisplaySnapshot> snapshots); // Configures the display represented by |display_id| to use |mode| and // positions the display to |origin| in the framebuffer. |mode| can be null, diff --git a/chromium/ui/display/mojo/typemaps.gni b/chromium/ui/display/mojo/typemaps.gni index 268ff25d0c4..e352a04421f 100644 --- a/chromium/ui/display/mojo/typemaps.gni +++ b/chromium/ui/display/mojo/typemaps.gni @@ -7,6 +7,6 @@ typemaps = [ "//ui/display/mojo/display_constants.typemap", "//ui/display/mojo/display_layout.typemap", "//ui/display/mojo/display_mode.typemap", - "//ui/display/mojo/display_snapshot_mojo.typemap", + "//ui/display/mojo/display_snapshot.typemap", "//ui/display/mojo/gamma_ramp_rgb_entry.typemap", ] diff --git a/chromium/ui/display/types/BUILD.gn b/chromium/ui/display/types/BUILD.gn index 9cef99b8f21..b4053c4b031 100644 --- a/chromium/ui/display/types/BUILD.gn +++ b/chromium/ui/display/types/BUILD.gn @@ -10,8 +10,6 @@ component("types") { "display_mode.h", "display_snapshot.cc", "display_snapshot.h", - "display_snapshot_mojo.cc", - "display_snapshot_mojo.h", "display_types_export.h", "fake_display_controller.h", "gamma_ramp_rgb_entry.h", diff --git a/chromium/ui/display/types/display_constants.h b/chromium/ui/display/types/display_constants.h index ff524dd3dda..97cf6214f35 100644 --- a/chromium/ui/display/types/display_constants.h +++ b/chromium/ui/display/types/display_constants.h @@ -55,16 +55,6 @@ enum HDCPState { HDCP_STATE_LAST = HDCP_STATE_ENABLED }; -// Color calibration profiles. Don't change the order, and edit -// tools/metrics/histograms/histograms.xml when a new item is added. -enum ColorCalibrationProfile { - COLOR_PROFILE_STANDARD, - COLOR_PROFILE_DYNAMIC, - COLOR_PROFILE_MOVIE, - COLOR_PROFILE_READING, - NUM_COLOR_PROFILES, -}; - } // namespace display #endif // UI_DISPLAY_TYPES_DISPLAY_CONSTANTS_H_ diff --git a/chromium/ui/display/types/display_snapshot.cc b/chromium/ui/display/types/display_snapshot.cc index e3ecd4b21bc..8e59a1664df 100644 --- a/chromium/ui/display/types/display_snapshot.cc +++ b/chromium/ui/display/types/display_snapshot.cc @@ -4,7 +4,13 @@ #include "ui/display/types/display_snapshot.h" +#include <inttypes.h> + #include <algorithm> +#include <sstream> +#include <utility> + +#include "base/strings/stringprintf.h" namespace display { @@ -13,8 +19,44 @@ namespace { // The display serial number beginning byte position and its length in the // EDID number as defined in the spec. // https://en.wikipedia.org/wiki/Extended_Display_Identification_Data -const size_t kSerialNumberBeginingByte = 12U; -const size_t kSerialNumberLengthInBytes = 4U; +constexpr size_t kSerialNumberBeginingByte = 12U; +constexpr size_t kSerialNumberLengthInBytes = 4U; + +std::string ModeListString( + const std::vector<std::unique_ptr<const DisplayMode>>& modes) { + std::stringstream stream; + bool first = true; + for (auto& mode : modes) { + if (!first) + stream << ", "; + stream << mode->ToString(); + first = false; + } + return stream.str(); +} + +std::string DisplayConnectionTypeString(DisplayConnectionType type) { + switch (type) { + case DISPLAY_CONNECTION_TYPE_NONE: + return "none"; + case DISPLAY_CONNECTION_TYPE_UNKNOWN: + return "unknown"; + case DISPLAY_CONNECTION_TYPE_INTERNAL: + return "internal"; + case DISPLAY_CONNECTION_TYPE_VGA: + return "vga"; + case DISPLAY_CONNECTION_TYPE_HDMI: + return "hdmi"; + case DISPLAY_CONNECTION_TYPE_DVI: + return "dvi"; + case DISPLAY_CONNECTION_TYPE_DISPLAYPORT: + return "dp"; + case DISPLAY_CONNECTION_TYPE_NETWORK: + return "network"; + } + NOTREACHED(); + return ""; +} } // namespace @@ -30,7 +72,9 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id, DisplayModeList modes, const std::vector<uint8_t>& edid, const DisplayMode* current_mode, - const DisplayMode* native_mode) + const DisplayMode* native_mode, + int64_t product_id, + const gfx::Size& maximum_cursor_size) : display_id_(display_id), origin_(origin), physical_size_(physical_size), @@ -44,7 +88,8 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id, edid_(edid), current_mode_(current_mode), native_mode_(native_mode), - product_id_(kInvalidProductID) { + product_id_(product_id), + maximum_cursor_size_(maximum_cursor_size) { // We must explicitly clear out the bytes that represent the serial number. const size_t end = std::min(kSerialNumberBeginingByte + kSerialNumberLengthInBytes, @@ -55,6 +100,42 @@ DisplaySnapshot::DisplaySnapshot(int64_t display_id, DisplaySnapshot::~DisplaySnapshot() {} +std::unique_ptr<DisplaySnapshot> DisplaySnapshot::Clone() { + DisplayModeList clone_modes; + const DisplayMode* cloned_current_mode = nullptr; + const DisplayMode* cloned_native_mode = nullptr; + + // Clone the display modes and find equivalent pointers to the native and + // current mode. + for (auto& mode : modes_) { + clone_modes.push_back(mode->Clone()); + if (mode.get() == current_mode_) + cloned_current_mode = clone_modes.back().get(); + if (mode.get() == native_mode_) + cloned_native_mode = clone_modes.back().get(); + } + + return std::make_unique<DisplaySnapshot>( + display_id_, origin_, physical_size_, type_, + is_aspect_preserving_scaling_, has_overscan_, + has_color_correction_matrix_, display_name_, sys_path_, + std::move(clone_modes), edid_, cloned_current_mode, cloned_native_mode, + product_id_, 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)", + 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()); +} + // static gfx::BufferFormat DisplaySnapshot::PrimaryFormat() { return gfx::BufferFormat::BGRX_8888; diff --git a/chromium/ui/display/types/display_snapshot.h b/chromium/ui/display/types/display_snapshot.h index 3bf0c838857..db0ccbbd7ec 100644 --- a/chromium/ui/display/types/display_snapshot.h +++ b/chromium/ui/display/types/display_snapshot.h @@ -7,6 +7,8 @@ #include <stdint.h> +#include <memory> +#include <string> #include <vector> #include "base/files/file_path.h" @@ -38,7 +40,9 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { DisplayModeList modes, const std::vector<uint8_t>& edid, const DisplayMode* current_mode, - const DisplayMode* native_mode); + const DisplayMode* native_mode, + int64_t product_id, + const gfx::Size& maximum_cursor_size); virtual ~DisplaySnapshot(); const gfx::Point& origin() const { return origin_; } @@ -72,8 +76,11 @@ class DISPLAY_TYPES_EXPORT DisplaySnapshot { return has_color_correction_matrix_; } + // Clones display state. + virtual std::unique_ptr<DisplaySnapshot> Clone(); + // Returns a textual representation of this display state. - virtual std::string ToString() const = 0; + virtual std::string ToString() const; // Used when no product id known. static const int64_t kInvalidProductID = -1; diff --git a/chromium/ui/display/types/display_snapshot_mojo.cc b/chromium/ui/display/types/display_snapshot_mojo.cc deleted file mode 100644 index 09482de3c97..00000000000 --- a/chromium/ui/display/types/display_snapshot_mojo.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/display/types/display_snapshot_mojo.h" - -#include "base/memory/ptr_util.h" -#include "ui/display/types/display_constants.h" - -namespace display { - -// static -std::unique_ptr<DisplaySnapshotMojo> DisplaySnapshotMojo::CreateFrom( - const DisplaySnapshot& other) { - DisplayModeList clone_modes; - const DisplayMode* current_mode = nullptr; - const DisplayMode* native_mode = nullptr; - - // Clone the display modes and find equivalent pointers to the native and - // current mode. - for (auto& mode : other.modes()) { - clone_modes.push_back(mode->Clone()); - if (mode.get() == other.current_mode()) - current_mode = mode.get(); - if (mode.get() == other.native_mode()) - native_mode = mode.get(); - } - - return base::MakeUnique<DisplaySnapshotMojo>( - other.display_id(), other.origin(), other.physical_size(), other.type(), - other.is_aspect_preserving_scaling(), other.has_overscan(), - other.has_color_correction_matrix(), other.display_name(), - other.sys_path(), other.product_id(), std::move(clone_modes), - other.edid(), current_mode, native_mode, other.maximum_cursor_size()); -} - -DisplaySnapshotMojo::DisplaySnapshotMojo(int64_t display_id, - const gfx::Point& origin, - const gfx::Size& physical_size, - DisplayConnectionType type, - bool is_aspect_preserving_scaling, - bool has_overscan, - bool has_color_correction_matrix, - std::string display_name, - const base::FilePath& sys_path, - int64_t product_id, - DisplayModeList modes, - const std::vector<uint8_t>& edid, - const DisplayMode* current_mode, - const DisplayMode* native_mode, - const gfx::Size& maximum_cursor_size) - : DisplaySnapshot(display_id, - origin, - physical_size, - type, - is_aspect_preserving_scaling, - has_overscan, - has_color_correction_matrix, - display_name, - sys_path, - std::move(modes), - edid, - current_mode, - native_mode) { - product_id_ = product_id; - maximum_cursor_size_ = maximum_cursor_size; -} - -DisplaySnapshotMojo::DisplaySnapshotMojo(int64_t display_id, - const gfx::Point& origin, - const gfx::Size& physical_size, - DisplayConnectionType type, - bool is_aspect_preserving_scaling, - bool has_overscan, - bool has_color_correction_matrix, - std::string display_name, - const base::FilePath& sys_path, - DisplayModeList modes, - const std::vector<uint8_t>& edid, - const DisplayMode* current_mode, - const DisplayMode* native_mode, - std::string string_representation) - : DisplaySnapshot(display_id, - origin, - physical_size, - type, - is_aspect_preserving_scaling, - has_overscan, - has_color_correction_matrix, - display_name, - sys_path, - std::move(modes), - edid, - current_mode, - native_mode), - string_representation_(string_representation) {} - -DisplaySnapshotMojo::~DisplaySnapshotMojo() = default; - -std::string DisplaySnapshotMojo::ToString() const { - return string_representation_; -} - -} // namespace display diff --git a/chromium/ui/display/types/display_snapshot_mojo.h b/chromium/ui/display/types/display_snapshot_mojo.h deleted file mode 100644 index d68f93b6c18..00000000000 --- a/chromium/ui/display/types/display_snapshot_mojo.h +++ /dev/null @@ -1,67 +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_DISPLAY_TYPES_DISPLAY_SNAPSHOT_MOJO_H_ -#define UI_DISPLAY_TYPES_DISPLAY_SNAPSHOT_MOJO_H_ - -#include <memory> - -#include "base/macros.h" -#include "ui/display/types/display_snapshot.h" -#include "ui/display/types/display_types_export.h" - -namespace display { - -// DisplaySnapshot implementation that can be used with Mojo IPC. -class DISPLAY_TYPES_EXPORT DisplaySnapshotMojo : public DisplaySnapshot { - public: - // Create a new DisplaySnapshotMojo by copying |other|. - static std::unique_ptr<DisplaySnapshotMojo> CreateFrom( - const DisplaySnapshot& other); - - DisplaySnapshotMojo(int64_t display_id, - const gfx::Point& origin, - const gfx::Size& physical_size, - DisplayConnectionType type, - bool is_aspect_preserving_scaling, - bool has_overscan, - bool has_color_correction_matrix, - std::string display_name, - const base::FilePath& sys_path, - int64_t product_id, - DisplayModeList modes, - const std::vector<uint8_t>& edid, - const DisplayMode* current_mode, - const DisplayMode* native_mode, - const gfx::Size& maximum_cursor_size); - - DisplaySnapshotMojo(int64_t display_id, - const gfx::Point& origin, - const gfx::Size& physical_size, - DisplayConnectionType type, - bool is_aspect_preserving_scaling, - bool has_overscan, - bool has_color_correction_matrix, - std::string display_name, - const base::FilePath& sys_path, - DisplayModeList modes, - const std::vector<uint8_t>& edid, - const DisplayMode* current_mode, - const DisplayMode* native_mode, - std::string string_representation); - - ~DisplaySnapshotMojo() override; - - // display::DisplaySnapshot override: - std::string ToString() const override; - - private: - std::string string_representation_; - - DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotMojo); -}; - -} // namespace display - -#endif // UI_DISPLAY_TYPES_DISPLAY_SNAPSHOT_MOJO_H_ diff --git a/chromium/ui/display/types/native_display_delegate.h b/chromium/ui/display/types/native_display_delegate.h index 4cf7e1333e5..0e226b14302 100644 --- a/chromium/ui/display/types/native_display_delegate.h +++ b/chromium/ui/display/types/native_display_delegate.h @@ -16,7 +16,6 @@ namespace gfx { class Point; -class Size; } namespace display { @@ -45,13 +44,6 @@ class DISPLAY_TYPES_EXPORT NativeDisplayDelegate { virtual void Initialize() = 0; - // Grabs and refreshes any display server related resources. Must be balanced - // by a call to UngrabServer(). - virtual void GrabServer() = 0; - - // Released the display server and any resources allocated by GrabServer(). - virtual void UngrabServer() = 0; - // Take control of the display from any other controlling process. virtual void TakeDisplayControl(const DisplayControlCallback& callback) = 0; @@ -59,23 +51,10 @@ class DISPLAY_TYPES_EXPORT NativeDisplayDelegate { virtual void RelinquishDisplayControl( const DisplayControlCallback& callback) = 0; - // Flushes all pending requests and waits for replies. - virtual void SyncWithServer() = 0; - - // Sets the window's background color to |color_argb|. - virtual void SetBackgroundColor(uint32_t color_argb) = 0; - - // Enables DPMS and forces it to the "on" state. - virtual void ForceDPMSOn() = 0; - // Queries for a list of fresh displays and returns them via |callback|. // Note the query operation may be expensive and take over 60 milliseconds. virtual void GetDisplays(const GetDisplaysCallback& callback) = 0; - // Adds |mode| to |output|. |mode| must be a valid display mode pointer. - virtual void AddMode(const DisplaySnapshot& output, - const DisplayMode* mode) = 0; - // Configures the display represented by |output| to use |mode| and positions // the display to |origin| in the framebuffer. |mode| can be NULL, which // represents disabling the display. The callback will return the status of @@ -85,9 +64,6 @@ class DISPLAY_TYPES_EXPORT NativeDisplayDelegate { const gfx::Point& origin, const ConfigureCallback& callback) = 0; - // Called to set the frame buffer (underlying XRR "screen") size. - virtual void CreateFrameBuffer(const gfx::Size& size) = 0; - // Gets HDCP state of output. virtual void GetHDCPState(const DisplaySnapshot& output, const GetHDCPStateCallback& callback) = 0; @@ -97,15 +73,6 @@ class DISPLAY_TYPES_EXPORT NativeDisplayDelegate { HDCPState state, const SetHDCPStateCallback& callback) = 0; - // Gets the available list of color calibrations. - virtual std::vector<ColorCalibrationProfile> - GetAvailableColorCalibrationProfiles(const DisplaySnapshot& output) = 0; - - // Sets the color calibration of |output| to |new_profile|. - virtual bool SetColorCalibrationProfile( - const DisplaySnapshot& output, - ColorCalibrationProfile new_profile) = 0; - // Set the gamma tables and corection matrix for the display. virtual bool SetColorCorrection( const DisplaySnapshot& output, diff --git a/chromium/ui/display/win/color_profile_reader.cc b/chromium/ui/display/win/color_profile_reader.cc index d1220cfd4a9..2a615919f8b 100644 --- a/chromium/ui/display/win/color_profile_reader.cc +++ b/chromium/ui/display/win/color_profile_reader.cc @@ -110,9 +110,10 @@ void ColorProfileReader::ReadProfilesCompleted( if (profile_data.empty()) { display_id_to_color_space_map_[display_id] = default_color_space_; } else { - display_id_to_color_space_map_[display_id] = - gfx::ICCProfile::FromData(profile_data.data(), profile_data.size()) - .GetColorSpace(); + gfx::ICCProfile icc_profile = + gfx::ICCProfile::FromData(profile_data.data(), profile_data.size()); + icc_profile.HistogramDisplay(display_id); + display_id_to_color_space_map_[display_id] = icc_profile.GetColorSpace(); } } diff --git a/chromium/ui/display/win/screen_win.cc b/chromium/ui/display/win/screen_win.cc index f4e89f29062..df4e5cb17ed 100644 --- a/chromium/ui/display/win/screen_win.cc +++ b/chromium/ui/display/win/screen_win.cc @@ -17,6 +17,7 @@ #include "ui/display/display.h" #include "ui/display/display_layout.h" #include "ui/display/display_layout_builder.h" +#include "ui/display/display_switches.h" #include "ui/display/win/display_info.h" #include "ui/display/win/dpi.h" #include "ui/display/win/scaling_util.h" @@ -81,7 +82,8 @@ std::vector<DisplayInfo> FindAndRemoveTouchingDisplayInfos( } Display CreateDisplayFromDisplayInfo(const DisplayInfo& display_info, - ColorProfileReader* color_profile_reader) { + ColorProfileReader* color_profile_reader, + bool hdr_enabled) { Display display(display_info.id()); float scale_factor = display_info.device_scale_factor(); display.set_device_scale_factor(scale_factor); @@ -92,8 +94,12 @@ Display CreateDisplayFromDisplayInfo(const DisplayInfo& display_info, 1.0f / scale_factor)); display.set_rotation(display_info.rotation()); if (!Display::HasForceColorProfile()) { - display.set_color_space( - color_profile_reader->GetDisplayColorSpace(display_info.id())); + if (hdr_enabled) { + display.SetColorSpaceAndDepth(gfx::ColorSpace::CreateSCRGBLinear()); + } else { + display.SetColorSpaceAndDepth( + color_profile_reader->GetDisplayColorSpace(display_info.id())); + } } return display; } @@ -117,7 +123,8 @@ Display CreateDisplayFromDisplayInfo(const DisplayInfo& display_info, // will take precedence. std::vector<ScreenWinDisplay> DisplayInfosToScreenWinDisplays( const std::vector<DisplayInfo>& display_infos, - ColorProfileReader* color_profile_reader) { + ColorProfileReader* color_profile_reader, + bool hdr_enabled) { // Find and extract the primary display. std::vector<DisplayInfo> display_infos_remaining = display_infos; auto primary_display_iter = std::find_if( @@ -145,9 +152,10 @@ std::vector<ScreenWinDisplay> DisplayInfosToScreenWinDisplays( // Layout and create the ScreenWinDisplays. std::vector<Display> displays; - for (const auto& display_info : display_infos) - displays.push_back( - CreateDisplayFromDisplayInfo(display_info, color_profile_reader)); + for (const auto& display_info : display_infos) { + displays.push_back(CreateDisplayFromDisplayInfo( + display_info, color_profile_reader, hdr_enabled)); + } std::unique_ptr<DisplayLayout> layout(builder.Build()); layout->ApplyToDisplayList(&displays, nullptr, 0); @@ -215,7 +223,8 @@ gfx::Point ScalePointRelative(const gfx::Point& from_origin, ScreenWin::ScreenWin() : ScreenWin(true) {} ScreenWin::ScreenWin(bool initialize) - : color_profile_reader_(new ColorProfileReader(this)) { + : color_profile_reader_(new ColorProfileReader(this)), + hdr_enabled_(base::FeatureList::IsEnabled(features::kHighDynamicRange)) { DCHECK(!g_screen_win_instance); g_screen_win_instance = this; if (initialize) @@ -406,6 +415,8 @@ const std::vector<Display>& ScreenWin::GetAllDisplays() const { } Display ScreenWin::GetDisplayNearestWindow(gfx::NativeWindow window) const { + if (!window) + return GetPrimaryDisplay(); HWND window_hwnd = GetHWNDFromNativeView(window); if (!window_hwnd) { // When |window| isn't rooted to a display, we should just return the @@ -458,7 +469,7 @@ gfx::Rect ScreenWin::DIPToScreenRectInWindow(gfx::NativeView view, void ScreenWin::UpdateFromDisplayInfos( const std::vector<DisplayInfo>& display_infos) { screen_win_displays_ = DisplayInfosToScreenWinDisplays( - display_infos, color_profile_reader_.get()); + display_infos, color_profile_reader_.get(), hdr_enabled_); displays_ = ScreenWinDisplaysToDisplays(screen_win_displays_); } diff --git a/chromium/ui/display/win/screen_win.h b/chromium/ui/display/win/screen_win.h index d6a3497177b..988b7c8a099 100644 --- a/chromium/ui/display/win/screen_win.h +++ b/chromium/ui/display/win/screen_win.h @@ -203,6 +203,11 @@ class DISPLAY_EXPORT ScreenWin : public Screen, // A helper to read color profiles from the filesystem. std::unique_ptr<ColorProfileReader> color_profile_reader_; + // Whether or not HDR mode is enabled. + // TODO(ccameron): Set this via the GPU process when the system "HDR and + // advanced color" setting is enabled. + bool hdr_enabled_ = false; + DISALLOW_COPY_AND_ASSIGN(ScreenWin); }; diff --git a/chromium/ui/display/win/screen_win_unittest.cc b/chromium/ui/display/win/screen_win_unittest.cc index 58f1b602c50..6c44428fa91 100644 --- a/chromium/ui/display/win/screen_win_unittest.cc +++ b/chromium/ui/display/win/screen_win_unittest.cc @@ -143,7 +143,7 @@ class TestScreenWinInitializer { virtual HWND CreateFakeHwnd(const gfx::Rect& bounds) = 0; }; -class TestScreenWinManager : public TestScreenWinInitializer { +class TestScreenWinManager final : public TestScreenWinInitializer { public: TestScreenWinManager() = default; diff --git a/chromium/ui/events/BUILD.gn b/chromium/ui/events/BUILD.gn index ca2f3a9cc3b..570f4e01dfd 100644 --- a/chromium/ui/events/BUILD.gn +++ b/chromium/ui/events/BUILD.gn @@ -361,7 +361,14 @@ if (!is_ios) { test("events_unittests") { sources = [ "android/scroller_unittest.cc", + "blink/blink_event_util_unittest.cc", + "blink/input_handler_proxy_unittest.cc", + "blink/input_scroll_elasticity_controller_unittest.cc", + "blink/web_input_event_traits_unittest.cc", + "blink/web_input_event_unittest.cc", "cocoa/events_mac_unittest.mm", + "devices/mojo/device_struct_traits_unittest.cc", + "devices/mojo/touch_device_transform_struct_traits_unittest.cc", "event_dispatcher_unittest.cc", "event_processor_unittest.cc", "event_rewriter_unittest.cc", @@ -376,10 +383,12 @@ if (!is_ios) { "gesture_detection/snap_scroll_controller_unittest.cc", "gesture_detection/touch_disposition_gesture_filter_unittest.cc", "gesture_detection/velocity_tracker_unittest.cc", + "gestures/blink/web_gesture_curve_impl_unittest.cc", "gestures/fling_curve_unittest.cc", "keycodes/dom/keycode_converter_unittest.cc", "keycodes/keyboard_code_conversion_unittest.cc", "keycodes/platform_key_map_win_unittest.cc", + "mojo/struct_traits_unittest.cc", "platform/platform_event_source_unittest.cc", "scoped_target_handler_unittest.cc", "win/event_utils_win_unittest.cc", @@ -393,41 +402,28 @@ if (!is_ios) { ":test_support", "//base", "//base/test:test_support", + "//cc", + "//ipc:test_support", "//mojo/edk/test:run_all_unittests", + "//mojo/public/cpp/bindings", "//skia", "//testing/gmock", "//testing/gtest", + "//third_party/WebKit/public:blink_headers", + "//ui/display", + "//ui/events/blink", "//ui/events/devices", + "//ui/events/devices/mojo:test_interfaces", + "//ui/events/gestures/blink", + "//ui/events/mojo:test_interfaces", "//ui/events/platform", "//ui/gfx:test_support", + "//ui/gfx/ipc/geometry", ] - if (!is_ios) { - sources += [ - "blink/blink_event_util_unittest.cc", - "blink/input_handler_proxy_unittest.cc", - "blink/input_scroll_elasticity_controller_unittest.cc", - "blink/web_input_event_traits_unittest.cc", - "blink/web_input_event_unittest.cc", - "devices/mojo/device_struct_traits_unittest.cc", - "gestures/blink/web_gesture_curve_impl_unittest.cc", - "mojo/struct_traits_unittest.cc", - ] - deps += [ - "//cc", - "//ipc:test_support", - "//mojo/public/cpp/bindings", - "//third_party/WebKit/public:blink_headers", - "//ui/display", - "//ui/events/blink", - "//ui/events/devices/mojo:test_interfaces", - "//ui/events/gestures/blink", - "//ui/events/mojo:test_interfaces", - "//ui/gfx/ipc/geometry", - ] - } - - if (!is_android && !is_ios) { + if (is_android) { + sources += [ "android/motion_event_android_unittest.cc" ] + } else { data_deps = [ "//third_party/mesa:osmesa", ] @@ -446,6 +442,10 @@ if (!is_ios) { ] } + if (use_x11 || use_ozone) { + sources += [ "devices/device_data_manager_unittest.cc" ] + } + if (use_ozone) { sources += [ "ozone/chromeos/cursor_controller_unittest.cc", @@ -484,14 +484,6 @@ if (!is_ios) { ] } - if (is_android) { - sources += [ "android/motion_event_android_unittest.cc" ] - } - - if (is_ios) { - assert_no_deps = ios_assert_no_deps - } - if (is_win) { sources += [ "blink/web_input_event_builders_win_unittest.cc" ] } diff --git a/chromium/ui/events/android/motion_event_android.cc b/chromium/ui/events/android/motion_event_android.cc index 4d458420196..09f030db9be 100644 --- a/chromium/ui/events/android/motion_event_android.cc +++ b/chromium/ui/events/android/motion_event_android.cc @@ -209,6 +209,7 @@ MotionEventAndroid::MotionEventAndroid(JNIEnv* env, jint android_meta_state, jfloat raw_offset_x_pixels, jfloat raw_offset_y_pixels, + jboolean for_touch_handle, const Pointer* const pointer0, const Pointer* const pointer1) : pix_to_dip_(pix_to_dip), @@ -216,6 +217,7 @@ MotionEventAndroid::MotionEventAndroid(JNIEnv* env, ticks_y_(ticks_y), tick_multiplier_(tick_multiplier), time_sec_(time_ms / 1000), + for_touch_handle_(for_touch_handle), cached_time_(FromAndroidTime(time_ms)), cached_action_(FromAndroidAction(android_action)), cached_pointer_count_(pointer_count), @@ -246,6 +248,7 @@ MotionEventAndroid::MotionEventAndroid(const MotionEventAndroid& e) ticks_y_(e.ticks_y_), tick_multiplier_(e.tick_multiplier_), time_sec_(e.time_sec_), + for_touch_handle_(e.for_touch_handle_), cached_time_(e.cached_time_), cached_action_(e.cached_action_), cached_pointer_count_(e.cached_pointer_count_), @@ -261,6 +264,22 @@ MotionEventAndroid::MotionEventAndroid(const MotionEventAndroid& e) cached_pointers_[1] = e.cached_pointers_[1]; } +// static +int MotionEventAndroid::GetAndroidActionForTesting(int action) { + int android_action = JNI_MotionEvent::ACTION_CANCEL; + switch (action) { + case ui::MotionEvent::ACTION_DOWN: + android_action = JNI_MotionEvent::ACTION_DOWN; + break; + case ui::MotionEvent::ACTION_UP: + android_action = JNI_MotionEvent::ACTION_UP; + break; + default: + NOTIMPLEMENTED() << "Conversion not supported: " << action; + } + return android_action; +} + std::unique_ptr<MotionEventAndroid> MotionEventAndroid::CreateFor( const gfx::PointF& point) const { std::unique_ptr<MotionEventAndroid> event(new MotionEventAndroid(*this)); diff --git a/chromium/ui/events/android/motion_event_android.h b/chromium/ui/events/android/motion_event_android.h index a208366a6b5..e23d0e5c0af 100644 --- a/chromium/ui/events/android/motion_event_android.h +++ b/chromium/ui/events/android/motion_event_android.h @@ -25,6 +25,10 @@ namespace ui { // while all *output* coordinates are in DIPs (as with WebTouchEvent). class EVENTS_EXPORT MotionEventAndroid : public MotionEvent { public: + // Returns the motion event action defined in Java layer for a given + // MotionEvent::Action. + static int GetAndroidActionForTesting(int action); + struct Pointer { Pointer(jint id, jfloat pos_x_pixels, @@ -64,6 +68,7 @@ class EVENTS_EXPORT MotionEventAndroid : public MotionEvent { jint meta_state, jfloat raw_offset_x_pixels, jfloat raw_offset_y_pixels, + jboolean for_touch_handle, const Pointer* const pointer0, const Pointer* const pointer1); ~MotionEventAndroid() override; @@ -110,6 +115,7 @@ class EVENTS_EXPORT MotionEventAndroid : public MotionEvent { float ticks_y() const { return ticks_y_; } float time_sec() const { return time_sec_; } float GetTickMultiplier() const; + bool for_touch_handle() const { return for_touch_handle_; } base::android::ScopedJavaLocalRef<jobject> GetJavaObject() const; @@ -139,6 +145,8 @@ class EVENTS_EXPORT MotionEventAndroid : public MotionEvent { const float tick_multiplier_; const uint64_t time_sec_; + const bool for_touch_handle_; + const base::TimeTicks cached_time_; const Action cached_action_; const size_t cached_pointer_count_; diff --git a/chromium/ui/events/android/motion_event_android_unittest.cc b/chromium/ui/events/android/motion_event_android_unittest.cc index 8c67a9d8674..e71b5790746 100644 --- a/chromium/ui/events/android/motion_event_android_unittest.cc +++ b/chromium/ui/events/android/motion_event_android_unittest.cc @@ -66,7 +66,7 @@ TEST(MotionEventAndroidTest, Constructor) { base::android::AttachCurrentThread(), nullptr, kPixToDip, 0.f, 0.f, 0.f, event_time_ms, kAndroidActionDown, pointer_count, history_size, action_index, kAndroidActionButton, kAndroidButtonPrimary, - kAndroidAltKeyDown, raw_offset, -raw_offset, &p0, &p1); + kAndroidAltKeyDown, raw_offset, -raw_offset, false, &p0, &p1); EXPECT_EQ(MotionEvent::ACTION_DOWN, event.GetAction()); EXPECT_EQ(event_time, event.GetEventTime()); @@ -106,7 +106,8 @@ TEST(MotionEventAndroidTest, Clone) { 1, 13.7f, -7.13f, 5.3f, 1.2f, 0.1f, 0.2f, kAndroidToolTypeFinger); MotionEventAndroid event(base::android::AttachCurrentThread(), nullptr, kPixToDip, 0, 0, 0, 0, kAndroidActionDown, - pointer_count, 0, 0, 0, 0, 0, 0, 0, &p0, nullptr); + pointer_count, 0, 0, 0, 0, 0, 0, 0, false, &p0, + nullptr); std::unique_ptr<MotionEvent> clone = event.Clone(); EXPECT_EQ(ui::test::ToString(event), ui::test::ToString(*clone)); @@ -120,7 +121,7 @@ TEST(MotionEventAndroidTest, Cancel) { MotionEventAndroid event(base::android::AttachCurrentThread(), nullptr, kPixToDip, 0, 0, 0, event_time_ms, kAndroidActionDown, pointer_count, 0, 0, 0, 0, 0, 0, - 0, &p0, nullptr); + 0, false, &p0, nullptr); std::unique_ptr<MotionEvent> cancel_event = event.Cancel(); EXPECT_EQ(MotionEvent::ACTION_CANCEL, cancel_event->GetAction()); @@ -142,7 +143,7 @@ TEST(MotionEventAndroidTest, InvalidOrientationsSanitized) { MotionEventAndroid::Pointer p1(1, 0, 0, 0, 0, orientation1, 0, 0); MotionEventAndroid event(base::android::AttachCurrentThread(), nullptr, kPixToDip, 0, 0, 0, 0, kAndroidActionDown, - pointer_count, 0, 0, 0, 0, 0, 0, 0, &p0, &p1); + pointer_count, 0, 0, 0, 0, 0, 0, 0, false, &p0, &p1); EXPECT_EQ(0.f, event.GetOrientation(0)); EXPECT_EQ(0.f, event.GetOrientation(1)); @@ -154,8 +155,8 @@ TEST(MotionEventAndroidTest, NonEmptyHistoryForNonMoveEventsSanitized) { MotionEventAndroid::Pointer p0(0, 0, 0, 0, 0, 0, 0, 0); MotionEventAndroid event(base::android::AttachCurrentThread(), nullptr, kPixToDip, 0, 0, 0, 0, kAndroidActionDown, - pointer_count, history_size, 0, 0, 0, 0, 0, 0, &p0, - nullptr); + pointer_count, history_size, 0, 0, 0, 0, 0, 0, false, + &p0, nullptr); EXPECT_EQ(0U, event.GetHistorySize()); } @@ -171,7 +172,7 @@ TEST(MotionEventAndroidTest, ActionIndexForPointerDown) { MotionEventAndroid event(base::android::AttachCurrentThread(), nullptr, kPixToDip, 0, 0, 0, 0, kAndroidActionPointerDown, pointer_count, history_size, action_index, 0, 0, 0, - 0, 0, &p0, &p1); + 0, 0, false, &p0, &p1); EXPECT_EQ(MotionEvent::ACTION_POINTER_DOWN, event.GetAction()); EXPECT_EQ(action_index, event.GetActionIndex()); diff --git a/chromium/ui/events/android/scroller.cc b/chromium/ui/events/android/scroller.cc index 1375f3f02f5..32835f140a1 100644 --- a/chromium/ui/events/android/scroller.cc +++ b/chromium/ui/events/android/scroller.cc @@ -227,7 +227,7 @@ void Scroller::StartScroll(float start_x, float dy, base::TimeTicks start_time, base::TimeDelta duration) { - DCHECK_GT(duration.ToInternalValue(), 0); + DCHECK_GT(duration, base::TimeDelta()); mode_ = SCROLL_MODE; finished_ = false; duration_ = duration; @@ -270,7 +270,7 @@ void Scroller::Fling(float start_x, velocity_ = velocity; duration_ = GetSplineFlingDuration(velocity); - DCHECK_GT(duration_.ToInternalValue(), 0); + DCHECK_GT(duration_, base::TimeDelta()); duration_seconds_reciprocal_ = 1.0 / duration_.InSecondsF(); start_time_ = start_time; curr_time_ = start_time_; diff --git a/chromium/ui/events/blink/DEPS b/chromium/ui/events/blink/DEPS index 38bfc42275f..1c63c5bab93 100644 --- a/chromium/ui/events/blink/DEPS +++ b/chromium/ui/events/blink/DEPS @@ -1,6 +1,7 @@ include_rules = [ "+cc/input/input_handler.h", "+cc/input/main_thread_scrolling_reason.h", + "+cc/input/scroll_boundary_behavior.h", "+cc/input/scroll_elasticity_helper.h", "+cc/trees/swap_promise_monitor.h", diff --git a/chromium/ui/events/blink/did_overscroll_params.h b/chromium/ui/events/blink/did_overscroll_params.h index 46eeb15d1a5..9c3ea4b186d 100644 --- a/chromium/ui/events/blink/did_overscroll_params.h +++ b/chromium/ui/events/blink/did_overscroll_params.h @@ -5,6 +5,7 @@ #ifndef UI_EVENTS_BLINK_DID_OVERSCROLL_PARAMS_H_ #define UI_EVENTS_BLINK_DID_OVERSCROLL_PARAMS_H_ +#include "cc/input/scroll_boundary_behavior.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/vector2d_f.h" @@ -17,6 +18,7 @@ struct DidOverscrollParams { gfx::Vector2dF latest_overscroll_delta; gfx::Vector2dF current_fling_velocity; gfx::PointF causal_event_viewport_point; + cc::ScrollBoundaryBehavior scroll_boundary_behavior; }; } // namespace ui diff --git a/chromium/ui/events/blink/event_with_callback.cc b/chromium/ui/events/blink/event_with_callback.cc index 39793f4e67e..132e0819b0f 100644 --- a/chromium/ui/events/blink/event_with_callback.cc +++ b/chromium/ui/events/blink/event_with_callback.cc @@ -72,15 +72,28 @@ void EventWithCallback::RunCallbacks( InputHandlerProxy::EventDisposition disposition, const LatencyInfo& latency, std::unique_ptr<DidOverscrollParams> did_overscroll_params) { - for (auto& original_event : original_events_) { - std::unique_ptr<DidOverscrollParams> did_overscroll_params_copy; - if (did_overscroll_params) { - did_overscroll_params_copy = - base::MakeUnique<DidOverscrollParams>(*did_overscroll_params); - } - std::move(original_event.callback_) - .Run(disposition, std::move(original_event.event_), latency, - std::move(did_overscroll_params)); + // |original_events_| could be empty if this is the scroll event extracted + // from the matrix multiplication. + if (original_events_.size() == 0) + return; + + // Ack the oldest event with original latency. + std::move(original_events_.front().callback_) + .Run(disposition, std::move(original_events_.front().event_), latency, + did_overscroll_params + ? base::MakeUnique<DidOverscrollParams>(*did_overscroll_params) + : nullptr); + original_events_.pop_front(); + + // Ack other events with coalesced latency to avoid redundant tracking. + LatencyInfo coalesced_latency = latency; + coalesced_latency.set_coalesced(); + for (auto& coalesced_event : original_events_) { + std::move(coalesced_event.callback_) + .Run(disposition, std::move(coalesced_event.event_), coalesced_latency, + did_overscroll_params + ? base::MakeUnique<DidOverscrollParams>(*did_overscroll_params) + : nullptr); } } diff --git a/chromium/ui/events/blink/input_handler_proxy.cc b/chromium/ui/events/blink/input_handler_proxy.cc index d5c3e54b49a..e79247fd1c2 100644 --- a/chromium/ui/events/blink/input_handler_proxy.cc +++ b/chromium/ui/events/blink/input_handler_proxy.cc @@ -175,59 +175,6 @@ cc::ScrollState CreateScrollStateForGesture(const WebGestureEvent& event) { return cc::ScrollState(scroll_state_data); } -void ReportInputEventLatencyUma(const WebInputEvent& event, - const ui::LatencyInfo& latency_info) { - if (!(event.GetType() == WebInputEvent::kGestureScrollBegin || - event.GetType() == WebInputEvent::kGestureScrollUpdate || - event.GetType() == WebInputEvent::kGesturePinchBegin || - event.GetType() == WebInputEvent::kGesturePinchUpdate || - event.GetType() == WebInputEvent::kGestureFlingStart)) { - return; - } - - ui::LatencyInfo::LatencyMap::const_iterator it = - latency_info.latency_components().find(std::make_pair( - ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0)); - - if (it == latency_info.latency_components().end()) - return; - - base::TimeDelta delta = base::TimeTicks::Now() - it->second.event_time; - for (size_t i = 0; i < it->second.event_count; ++i) { - switch (event.GetType()) { - case blink::WebInputEvent::kGestureScrollBegin: - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Event.Latency.RendererImpl.GestureScrollBegin", - delta.InMicroseconds(), 1, 1000000, 100); - break; - case blink::WebInputEvent::kGestureScrollUpdate: - UMA_HISTOGRAM_CUSTOM_COUNTS( - // So named for historical reasons. - "Event.Latency.RendererImpl.GestureScroll2", - delta.InMicroseconds(), 1, 1000000, 100); - break; - case blink::WebInputEvent::kGesturePinchBegin: - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Event.Latency.RendererImpl.GesturePinchBegin", - delta.InMicroseconds(), 1, 1000000, 100); - break; - case blink::WebInputEvent::kGesturePinchUpdate: - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Event.Latency.RendererImpl.GesturePinchUpdate", - delta.InMicroseconds(), 1, 1000000, 100); - break; - case blink::WebInputEvent::kGestureFlingStart: - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Event.Latency.RendererImpl.GestureFlingStart", - delta.InMicroseconds(), 1, 1000000, 100); - break; - default: - NOTREACHED(); - break; - } - } -} - cc::InputHandler::ScrollInputType GestureScrollInputType( blink::WebGestureDevice device) { return device == blink::kWebGestureDeviceTouchpad @@ -266,7 +213,6 @@ InputHandlerProxy::InputHandlerProxy( disallow_vertical_fling_scroll_(false), has_fling_animation_started_(false), smooth_scroll_enabled_(false), - uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()), touchpad_and_wheel_scroll_latching_enabled_( touchpad_and_wheel_scroll_latching_enabled), touch_result_(kEventDispositionUndefined), @@ -303,9 +249,6 @@ void InputHandlerProxy::HandleInputEventWithLatencyInfo( EventDispositionCallback callback) { DCHECK(input_handler_); - if (uma_latency_reporting_enabled_) - ReportInputEventLatencyUma(*event, latency_info); - TRACE_EVENT_WITH_FLOW1("input,benchmark", "LatencyInfo.Flow", TRACE_ID_DONT_MANGLE(latency_info.trace_id()), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, @@ -889,7 +832,10 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin( "InputHandlerProxy::handle_input gesture scroll", TRACE_EVENT_SCOPE_THREAD); gesture_scroll_on_impl_thread_ = true; - result = DID_HANDLE; + if (scroll_status.bubble) + result = DID_HANDLE_SHOULD_BUBBLE; + else + result = DID_HANDLE; break; case cc::InputHandler::SCROLL_UNKNOWN: case cc::InputHandler::SCROLL_ON_MAIN_THREAD: @@ -1043,7 +989,6 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart( const float vx = gesture_event.data.fling_start.velocity_x; const float vy = gesture_event.data.fling_start.velocity_y; current_fling_velocity_ = gfx::Vector2dF(vx, vy); - DCHECK(!current_fling_velocity_.IsZero()); fling_curve_ = client_->CreateFlingAnimationCurve( gesture_event.source_device, WebFloatPoint(vx, vy), blink::WebSize()); disallow_horizontal_fling_scroll_ = !vx; @@ -1171,7 +1116,6 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart( cc::TouchAction white_listed_touch_action = cc::kTouchActionAuto; EventDisposition result = HitTestTouchEvent( touch_event, &is_touching_scrolling_layer, &white_listed_touch_action); - client_->SetWhiteListedTouchAction(white_listed_touch_action); // If |result| is still DROP_EVENT look at the touch end handler as // we may not want to discard the entire touch sequence. Note this @@ -1189,6 +1133,9 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart( if (is_flinging_on_impl && is_touching_scrolling_layer) result = DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING; + client_->SetWhiteListedTouchAction(white_listed_touch_action, + touch_event.unique_touch_event_id, result); + return result; } @@ -1202,7 +1149,8 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchMove( cc::TouchAction white_listed_touch_action = cc::kTouchActionAuto; EventDisposition result = HitTestTouchEvent( touch_event, &is_touching_scrolling_layer, &white_listed_touch_action); - client_->SetWhiteListedTouchAction(white_listed_touch_action); + client_->SetWhiteListedTouchAction( + white_listed_touch_action, touch_event.unique_touch_event_id, result); return result; } return static_cast<EventDisposition>(touch_result_); @@ -1513,13 +1461,16 @@ void InputHandlerProxy::HandleOverscroll( ToClientScrollIncrement(current_fling_velocity_); current_overscroll_params_->causal_event_viewport_point = gfx::PointF(causal_event_viewport_point); + current_overscroll_params_->scroll_boundary_behavior = + scroll_result.scroll_boundary_behavior; return; } client_->DidOverscroll(scroll_result.accumulated_root_overscroll, scroll_result.unused_scroll_delta, ToClientScrollIncrement(current_fling_velocity_), - gfx::PointF(causal_event_viewport_point)); + gfx::PointF(causal_event_viewport_point), + scroll_result.scroll_boundary_behavior); } bool InputHandlerProxy::CancelCurrentFling() { @@ -1645,6 +1596,7 @@ bool InputHandlerProxy::TouchpadFlingScroll( CancelCurrentFlingWithoutNotifyingClient(); break; case DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING: + case DID_HANDLE_SHOULD_BUBBLE: NOTREACHED(); return false; } diff --git a/chromium/ui/events/blink/input_handler_proxy.h b/chromium/ui/events/blink/input_handler_proxy.h index 2d3a93fea35..c8a0f0972d0 100644 --- a/chromium/ui/events/blink/input_handler_proxy.h +++ b/chromium/ui/events/blink/input_handler_proxy.h @@ -48,10 +48,9 @@ struct DidOverscrollParams; // the compositor's input handling logic. InputHandlerProxy instances live // entirely on the compositor thread. Each InputHandler instance handles input // events intended for a specific WebWidget. -class InputHandlerProxy - : public cc::InputHandlerClient, - public SynchronousInputHandlerProxy, - public NON_EXPORTED_BASE(blink::WebGestureCurveTarget) { +class InputHandlerProxy : public cc::InputHandlerClient, + public SynchronousInputHandlerProxy, + public blink::WebGestureCurveTarget { public: InputHandlerProxy(cc::InputHandler* input_handler, InputHandlerProxyClient* client, @@ -69,7 +68,12 @@ class InputHandlerProxy DID_NOT_HANDLE, DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING, DID_HANDLE_NON_BLOCKING, - DROP_EVENT + DROP_EVENT, + // The compositor did handle the scroll event (so it wouldn't forward the + // event to the main thread.) but it didn't consume the scroll so it should + // pass it to the next consumer (either overscrolling or bubbling the event + // to the next renderer). + DID_HANDLE_SHOULD_BUBBLE, }; using EventDispositionCallback = base::OnceCallback<void(EventDisposition, @@ -247,7 +251,6 @@ class InputHandlerProxy scroll_elasticity_controller_; bool smooth_scroll_enabled_; - bool uma_latency_reporting_enabled_; const bool touchpad_and_wheel_scroll_latching_enabled_; // The merged result of the last touch event with previous touch events. diff --git a/chromium/ui/events/blink/input_handler_proxy_client.h b/chromium/ui/events/blink/input_handler_proxy_client.h index ee17a9f546e..6cefb93c4ce 100644 --- a/chromium/ui/events/blink/input_handler_proxy_client.h +++ b/chromium/ui/events/blink/input_handler_proxy_client.h @@ -49,7 +49,8 @@ class InputHandlerProxyClient { const gfx::Vector2dF& accumulated_overscroll, const gfx::Vector2dF& latest_overscroll_delta, const gfx::Vector2dF& current_fling_velocity, - const gfx::PointF& causal_event_viewport_point) = 0; + const gfx::PointF& causal_event_viewport_point, + const cc::ScrollBoundaryBehavior& scroll_boundary_behavior) = 0; virtual void DidStopFlinging() = 0; @@ -60,7 +61,10 @@ class InputHandlerProxyClient { virtual void GenerateScrollBeginAndSendToMainThread( const blink::WebGestureEvent& update_event) = 0; - virtual void SetWhiteListedTouchAction(cc::TouchAction touch_action) = 0; + virtual void SetWhiteListedTouchAction( + cc::TouchAction touch_action, + uint32_t unique_touch_event_id, + InputHandlerProxy::EventDisposition event_disposition) = 0; protected: virtual ~InputHandlerProxyClient() {} diff --git a/chromium/ui/events/blink/input_handler_proxy_unittest.cc b/chromium/ui/events/blink/input_handler_proxy_unittest.cc index 7de08a08f45..8ccb5e56461 100644 --- a/chromium/ui/events/blink/input_handler_proxy_unittest.cc +++ b/chromium/ui/events/blink/input_handler_proxy_unittest.cc @@ -312,14 +312,19 @@ class MockInputHandlerProxyClient blink::WebFloatSize(cumulative_scroll.width, cumulative_scroll.height)); } - MOCK_METHOD4(DidOverscroll, - void(const gfx::Vector2dF& accumulated_overscroll, - const gfx::Vector2dF& latest_overscroll_delta, - const gfx::Vector2dF& current_fling_velocity, - const gfx::PointF& causal_event_viewport_point)); + MOCK_METHOD5( + DidOverscroll, + void(const gfx::Vector2dF& accumulated_overscroll, + const gfx::Vector2dF& latest_overscroll_delta, + const gfx::Vector2dF& current_fling_velocity, + const gfx::PointF& causal_event_viewport_point, + const cc::ScrollBoundaryBehavior& scroll_boundary_behavior)); void DidStopFlinging() override {} void DidAnimateForInput() override {} - MOCK_METHOD1(SetWhiteListedTouchAction, void(cc::TouchAction touch_action)); + MOCK_METHOD3(SetWhiteListedTouchAction, + void(cc::TouchAction touch_action, + uint32_t unique_touch_event_id, + InputHandlerProxy::EventDisposition event_disposition)); private: DISALLOW_COPY_AND_ASSIGN(MockInputHandlerProxyClient); @@ -538,6 +543,7 @@ class InputHandlerProxyEventQueueTest : public testing::TestWithParam<bool> { void SetUp() override { bool wheel_scroll_latching_enabled = GetParam(); event_disposition_recorder_.clear(); + latency_info_recorder_.clear(); input_handler_proxy_ = base::MakeUnique<TestInputHandlerProxy>( &mock_input_handler_, &mock_client_, wheel_scroll_latching_enabled); if (input_handler_proxy_->compositor_event_queue_) @@ -600,6 +606,7 @@ class InputHandlerProxyEventQueueTest : public testing::TestWithParam<bool> { const ui::LatencyInfo& latency_info, std::unique_ptr<ui::DidOverscrollParams> overscroll_params) { event_disposition_recorder_.push_back(event_disposition); + latency_info_recorder_.push_back(latency_info); } std::deque<std::unique_ptr<EventWithCallback>>& event_queue() { @@ -617,6 +624,7 @@ class InputHandlerProxyEventQueueTest : public testing::TestWithParam<bool> { std::unique_ptr<TestInputHandlerProxy> input_handler_proxy_; testing::StrictMock<MockInputHandlerProxyClient> mock_client_; std::vector<InputHandlerProxy::EventDisposition> event_disposition_recorder_; + std::vector<ui::LatencyInfo> latency_info_recorder_; base::MessageLoop loop_; base::WeakPtrFactory<InputHandlerProxyEventQueueTest> weak_ptr_factory_; @@ -773,6 +781,8 @@ TEST_P(InputHandlerProxyTest, GestureScrollIgnored) { gesture_.SetType(WebInputEvent::kGestureScrollBegin); EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + VERIFY_AND_RESET_MOCKS(); + // GSB is dropped and not sent to the main thread, GSE shouldn't get sent to // the main thread, either. expected_disposition_ = InputHandlerProxy::DROP_EVENT; @@ -782,6 +792,12 @@ TEST_P(InputHandlerProxyTest, GestureScrollIgnored) { EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); VERIFY_AND_RESET_MOCKS(); + + expected_disposition_ = InputHandlerProxy::DROP_EVENT; + gesture_.SetType(WebInputEvent::kGestureFlingStart); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); } TEST_P(InputHandlerProxyTest, GestureScrollByPage) { @@ -2225,11 +2241,10 @@ void InputHandlerProxyTest::GestureFlingStopsAtContentEdge() { .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::_)); + DidOverscroll(overscroll.accumulated_root_overscroll, + overscroll.unused_scroll_delta, + testing::Property(&gfx::Vector2dF::y, testing::Lt(0)), + testing::_, overscroll.scroll_boundary_behavior)); if (!touchpad_and_wheel_scroll_latching_enabled_) EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); @@ -2390,11 +2405,10 @@ TEST_P(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { .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::_)); + DidOverscroll(overscroll.accumulated_root_overscroll, + overscroll.unused_scroll_delta, + testing::Property(&gfx::Vector2dF::y, testing::Lt(0)), + testing::_, overscroll.scroll_boundary_behavior)); EXPECT_SET_NEEDS_ANIMATE_INPUT(1); time += base::TimeDelta::FromMilliseconds(10); Animate(time); @@ -2419,11 +2433,10 @@ TEST_P(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { .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::_)); + DidOverscroll(overscroll.accumulated_root_overscroll, + overscroll.unused_scroll_delta, + testing::Property(&gfx::Vector2dF::x, testing::Lt(0)), + testing::_, overscroll.scroll_boundary_behavior)); EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); time += base::TimeDelta::FromMilliseconds(10); Animate(time); @@ -2550,12 +2563,15 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestNegative) { *touch_action = cc::kTouchActionPanUp; return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; })); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(cc::kTouchActionPanUp)) + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(cc::kTouchActionPanUp, 1, + InputHandlerProxy::DROP_EVENT)) .WillOnce(testing::Return()); WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, WebInputEvent::kTimeStampForTesting); + touch.unique_touch_event_id = 1; touch.touches_length = 3; touch.touch_start_or_first_touch_move = true; touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStateStationary, 0, 0); @@ -2591,7 +2607,9 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPositive) { return cc::InputHandler::TouchStartOrMoveEventListenerType:: HANDLER_ON_SCROLLING_LAYER; })); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(cc::kTouchActionPanY)) + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(cc::kTouchActionPanY, 1, + InputHandlerProxy::DID_NOT_HANDLE)) .WillOnce(testing::Return()); // Since the second touch point hits a touch-region, there should be no // hit-testing for the third touch point. @@ -2599,6 +2617,7 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPositive) { WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, WebInputEvent::kTimeStampForTesting); + touch.unique_touch_event_id = 1; touch.touches_length = 3; touch.touch_start_or_first_touch_move = true; touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); @@ -2632,12 +2651,15 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPassivePositive) { *touch_action = cc::kTouchActionPanX; return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; })); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(cc::kTouchActionPanRight)) + EXPECT_CALL(mock_client_, SetWhiteListedTouchAction( + cc::kTouchActionPanRight, 1, + InputHandlerProxy::DID_HANDLE_NON_BLOCKING)) .WillOnce(testing::Return()); WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, WebInputEvent::kTimeStampForTesting); + touch.unique_touch_event_id = 1; touch.touches_length = 3; touch.touch_start_or_first_touch_move = true; touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); @@ -2670,11 +2692,14 @@ TEST_P(InputHandlerProxyTest, TouchStartPassiveAndTouchEndBlocking) { *touch_action = cc::kTouchActionNone; return cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER; })); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(cc::kTouchActionNone)) + EXPECT_CALL(mock_client_, SetWhiteListedTouchAction( + cc::kTouchActionNone, 1, + InputHandlerProxy::DID_HANDLE_NON_BLOCKING)) .WillOnce(testing::Return()); WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, WebInputEvent::kTimeStampForTesting); + touch.unique_touch_event_id = 1; touch.touches_length = 1; touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::kStatePressed, 0, 0); touch.touch_start_or_first_touch_move = true; @@ -2703,7 +2728,8 @@ TEST_P(InputHandlerProxyTest, TouchMoveBlockingAddedAfterPassiveTouchStart) { EventListenerTypeForTouchStartOrMoveAt(testing::_, testing::_)) .WillOnce(testing::Return( cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER)); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(testing::_)) + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) .WillOnce(testing::Return()); WebTouchEvent touch(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, @@ -2718,7 +2744,8 @@ TEST_P(InputHandlerProxyTest, TouchMoveBlockingAddedAfterPassiveTouchStart) { EventListenerTypeForTouchStartOrMoveAt(testing::_, testing::_)) .WillOnce(testing::Return( cc::InputHandler::TouchStartOrMoveEventListenerType::HANDLER)); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(testing::_)) + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) .WillOnce(testing::Return()); touch.SetType(WebInputEvent::kTouchMove); @@ -3518,7 +3545,8 @@ TEST_P(InputHandlerProxyTest, GestureScrollingThreadStatusHistogram) { mock_input_handler_, GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(testing::_)) + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) .WillOnce(testing::Return()); expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; @@ -3550,7 +3578,8 @@ TEST_P(InputHandlerProxyTest, GestureScrollingThreadStatusHistogram) { .WillOnce( testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType:: HANDLER_ON_SCROLLING_LAYER)); - EXPECT_CALL(mock_client_, SetWhiteListedTouchAction(testing::_)) + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) .WillOnce(testing::Return()); expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; @@ -4103,6 +4132,38 @@ TEST_P(InputHandlerProxyEventQueueTest, TouchpadGestureScrollEndFlushQueue) { input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing()); } +TEST_P(InputHandlerProxyEventQueueTest, CoalescedLatencyInfo) { + // 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::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1); + EXPECT_CALL( + mock_input_handler_, + ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)))) + .WillOnce(testing::Return(scroll_result_did_scroll_)); + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)); + + HandleGestureEvent(WebInputEvent::kGestureScrollBegin); + HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -20); + HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -40); + HandleGestureEvent(WebInputEvent::kGestureScrollUpdate, -30); + HandleGestureEvent(WebInputEvent::kGestureScrollEnd); + input_handler_proxy_->DeliverInputForBeginFrame(); + + EXPECT_EQ(0ul, event_queue().size()); + // Should run callbacks for every original events. + EXPECT_EQ(5ul, event_disposition_recorder_.size()); + EXPECT_EQ(5ul, latency_info_recorder_.size()); + EXPECT_EQ(false, latency_info_recorder_[1].coalesced()); + // Coalesced events should have latency set to coalesced. + EXPECT_EQ(true, latency_info_recorder_[2].coalesced()); + EXPECT_EQ(true, latency_info_recorder_[3].coalesced()); + testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); +} + INSTANTIATE_TEST_CASE_P(AnimateInput, InputHandlerProxyTest, testing::ValuesIn(test_types)); 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 6f239d2790f..df552c39d22 100644 --- a/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc +++ b/chromium/ui/events/blink/input_scroll_elasticity_controller_unittest.cc @@ -83,7 +83,8 @@ class ScrollElasticityControllerTest : public testing::Test { ScrollElasticityControllerTest() : controller_(&helper_), input_event_count_(0), - current_time_(base::TimeTicks::FromInternalValue(100000000ull)) {} + current_time_(base::TimeTicks() + + base::TimeDelta::FromMicroseconds(INT64_C(100000000))) {} ~ScrollElasticityControllerTest() override {} diff --git a/chromium/ui/events/devices/BUILD.gn b/chromium/ui/events/devices/BUILD.gn index ca8c4e80cb2..5913ef1acb0 100644 --- a/chromium/ui/events/devices/BUILD.gn +++ b/chromium/ui/events/devices/BUILD.gn @@ -25,6 +25,8 @@ component("devices") { "input_device_observer_win.cc", "input_device_observer_win.h", "stylus_state.h", + "touch_device_transform.cc", + "touch_device_transform.h", "touchscreen_device.cc", "touchscreen_device.h", ] @@ -32,6 +34,7 @@ component("devices") { defines = [ "EVENTS_DEVICES_IMPLEMENTATION" ] public_deps = [ + "//ui/display/types", "//ui/gfx", ] @@ -39,7 +42,7 @@ component("devices") { "//base", "//base/third_party/dynamic_annotations", "//skia", - "//ui/display/types", + "//ui/gfx:geometry_skia", "//ui/gfx/geometry", ] diff --git a/chromium/ui/events/devices/device_data_manager.cc b/chromium/ui/events/devices/device_data_manager.cc index d66744f3b13..86b8ebd0dad 100644 --- a/chromium/ui/events/devices/device_data_manager.cc +++ b/chromium/ui/events/devices/device_data_manager.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "ui/display/types/display_constants.h" #include "ui/events/devices/input_device_event_observer.h" +#include "ui/events/devices/touch_device_transform.h" #include "ui/gfx/geometry/point3_f.h" // This macro provides the implementation for the observer notification methods. @@ -28,16 +29,6 @@ bool InputDeviceEquals(const ui::InputDevice& a, const ui::InputDevice& b) { } // namespace -DeviceDataManager::TouchscreenInfo::TouchscreenInfo() { - Reset(); -} - -void DeviceDataManager::TouchscreenInfo::Reset() { - radius_scale = 1.0; - target_display = display::kInvalidDisplayId; - device_transform = gfx::Transform(); -} - // static DeviceDataManager* DeviceDataManager::instance_ = nullptr; @@ -50,9 +41,6 @@ DeviceDataManager::~DeviceDataManager() { } // static -DeviceDataManager* DeviceDataManager::instance() { return instance_; } - -// static void DeviceDataManager::set_instance(DeviceDataManager* instance) { DCHECK(instance) << "Must reset the DeviceDataManager using DeleteInstance()."; @@ -62,7 +50,7 @@ void DeviceDataManager::set_instance(DeviceDataManager* instance) { // static void DeviceDataManager::CreateInstance() { - if (instance()) + if (instance_) return; set_instance(new DeviceDataManager()); @@ -90,30 +78,40 @@ bool DeviceDataManager::HasInstance() { return instance_ != nullptr; } -void DeviceDataManager::ClearTouchDeviceAssociations() { - for (auto& touch_info : touch_map_) - touch_info.Reset(); +void DeviceDataManager::ConfigureTouchDevices( + const std::vector<ui::TouchDeviceTransform>& transforms) { + ClearTouchDeviceAssociations(); + for (const TouchDeviceTransform& transform : transforms) + UpdateTouchInfoFromTransform(transform); + are_touchscreen_target_displays_valid_ = true; + for (InputDeviceEventObserver& observer : observers_) + observer.OnTouchDeviceAssociationChanged(); } -bool DeviceDataManager::IsTouchDeviceIdValid( - int touch_device_id) const { - return (touch_device_id > 0 && touch_device_id < kMaxDeviceNum); +void DeviceDataManager::ClearTouchDeviceAssociations() { + for (size_t i = 0; i < touch_map_.size(); ++i) + touch_map_[i] = TouchDeviceTransform(); + for (TouchscreenDevice& touchscreen_device : touchscreen_devices_) + touchscreen_device.target_display_id = display::kInvalidDisplayId; } -void DeviceDataManager::UpdateTouchInfoForDisplay( - int64_t target_display_id, - int touch_device_id, - const gfx::Transform& touch_transformer) { - if (IsTouchDeviceIdValid(touch_device_id)) { - touch_map_[touch_device_id].target_display = target_display_id; - touch_map_[touch_device_id].device_transform = touch_transformer; +void DeviceDataManager::UpdateTouchInfoFromTransform( + const ui::TouchDeviceTransform& touch_device_transform) { + if (!IsTouchDeviceIdValid(touch_device_transform.device_id)) + return; + + touch_map_[touch_device_transform.device_id] = touch_device_transform; + + for (TouchscreenDevice& touchscreen_device : touchscreen_devices_) { + if (touchscreen_device.id == touch_device_transform.device_id) { + touchscreen_device.target_display_id = touch_device_transform.display_id; + return; + } } } -void DeviceDataManager::UpdateTouchRadiusScale(int touch_device_id, - double scale) { - if (IsTouchDeviceIdValid(touch_device_id)) - touch_map_[touch_device_id].radius_scale = scale; +bool DeviceDataManager::IsTouchDeviceIdValid(int touch_device_id) const { + return (touch_device_id > 0 && touch_device_id < kMaxDeviceNum); } void DeviceDataManager::ApplyTouchRadiusScale(int touch_device_id, @@ -127,7 +125,7 @@ void DeviceDataManager::ApplyTouchTransformer(int touch_device_id, float* y) { if (IsTouchDeviceIdValid(touch_device_id)) { gfx::Point3F point(*x, *y, 0.0); - const gfx::Transform& trans = touch_map_[touch_device_id].device_transform; + const gfx::Transform& trans = touch_map_[touch_device_id].transform; trans.TransformPoint(&point); *x = point.x(); *y = point.y(); @@ -158,7 +156,7 @@ bool DeviceDataManager::AreDeviceListsComplete() const { int64_t DeviceDataManager::GetTargetDisplayForTouchDevice( int touch_device_id) const { if (IsTouchDeviceIdValid(touch_device_id)) - return touch_map_[touch_device_id].target_display; + return touch_map_[touch_device_id].display_id; return display::kInvalidDisplayId; } @@ -171,7 +169,12 @@ void DeviceDataManager::OnTouchscreenDevicesUpdated( InputDeviceEquals)) { return; } + are_touchscreen_target_displays_valid_ = false; touchscreen_devices_ = devices; + for (TouchscreenDevice& touchscreen_device : touchscreen_devices_) { + touchscreen_device.target_display_id = + GetTargetDisplayForTouchDevice(touchscreen_device.id); + } NotifyObserversTouchscreenDeviceConfigurationChanged(); } @@ -258,4 +261,8 @@ bool DeviceDataManager::AreTouchscreensEnabled() const { return touch_screens_enabled_; } +bool DeviceDataManager::AreTouchscreenTargetDisplaysValid() const { + return are_touchscreen_target_displays_valid_; +} + } // namespace ui diff --git a/chromium/ui/events/devices/device_data_manager.h b/chromium/ui/events/devices/device_data_manager.h index 01e62aa98a7..7409c225002 100644 --- a/chromium/ui/events/devices/device_data_manager.h +++ b/chromium/ui/events/devices/device_data_manager.h @@ -16,8 +16,8 @@ #include "ui/events/devices/device_hotplug_event_observer.h" #include "ui/events/devices/events_devices_export.h" #include "ui/events/devices/input_device_manager.h" +#include "ui/events/devices/touch_device_transform.h" #include "ui/events/devices/touchscreen_device.h" -#include "ui/gfx/transform.h" namespace ui { @@ -40,16 +40,16 @@ class EVENTS_DEVICES_EXPORT DeviceDataManager static DeviceDataManager* GetInstance(); static bool HasInstance(); - void ClearTouchDeviceAssociations(); - void UpdateTouchInfoForDisplay(int64_t target_display_id, - int touch_device_id, - const gfx::Transform& touch_transformer); + // Configures the touch devices. |transforms| contains the transform for each + // device and display pair. + void ConfigureTouchDevices( + const std::vector<ui::TouchDeviceTransform>& transforms); + void ApplyTouchTransformer(int touch_device_id, float* x, float* y); // Gets the display that touches from |touch_device_id| should be sent to. int64_t GetTargetDisplayForTouchDevice(int touch_device_id) const; - void UpdateTouchRadiusScale(int touch_device_id, double scale); void ApplyTouchRadiusScale(int touch_device_id, double* radius); void SetTouchscreensEnabled(bool enabled); @@ -61,6 +61,7 @@ class EVENTS_DEVICES_EXPORT DeviceDataManager const std::vector<InputDevice>& GetTouchpadDevices() const override; bool AreDeviceListsComplete() const override; bool AreTouchscreensEnabled() const override; + bool AreTouchscreenTargetDisplaysValid() const override; void AddObserver(InputDeviceEventObserver* observer) override; void RemoveObserver(InputDeviceEventObserver* observer) override; @@ -68,8 +69,6 @@ class EVENTS_DEVICES_EXPORT DeviceDataManager protected: DeviceDataManager(); - static DeviceDataManager* instance(); - static void set_instance(DeviceDataManager* instance); // DeviceHotplugEventObserver: @@ -85,20 +84,11 @@ class EVENTS_DEVICES_EXPORT DeviceDataManager void OnStylusStateChanged(StylusState state) override; private: - // Information related to a touchscreen device. - struct TouchscreenInfo { - TouchscreenInfo(); - void Reset(); - - double radius_scale; - int64_t target_display; - gfx::Transform device_transform; - }; - friend class test::DeviceDataManagerTestAPI; - static DeviceDataManager* instance_; - + void ClearTouchDeviceAssociations(); + void UpdateTouchInfoFromTransform( + const ui::TouchDeviceTransform& touch_device_transform); bool IsTouchDeviceIdValid(int touch_device_id) const; void NotifyObserversTouchscreenDeviceConfigurationChanged(); @@ -108,6 +98,8 @@ class EVENTS_DEVICES_EXPORT DeviceDataManager void NotifyObserversDeviceListsComplete(); void NotifyObserversStylusStateChanged(StylusState stylus_state); + static DeviceDataManager* instance_; + std::vector<TouchscreenDevice> touchscreen_devices_; std::vector<InputDevice> keyboard_devices_; std::vector<InputDevice> mouse_devices_; @@ -118,10 +110,13 @@ class EVENTS_DEVICES_EXPORT DeviceDataManager bool touch_screens_enabled_ = true; + // Set to true when ConfigureTouchDevices() is called. + bool are_touchscreen_target_displays_valid_ = false; + // Contains touchscreen device info for each device mapped by device ID. Will // have default values if the device with corresponding ID isn't a touchscreen // or doesn't exist. - std::array<TouchscreenInfo, kMaxDeviceNum> touch_map_; + std::array<TouchDeviceTransform, kMaxDeviceNum> touch_map_; DISALLOW_COPY_AND_ASSIGN(DeviceDataManager); }; diff --git a/chromium/ui/events/devices/device_data_manager_unittest.cc b/chromium/ui/events/devices/device_data_manager_unittest.cc new file mode 100644 index 00000000000..8a17653a7ca --- /dev/null +++ b/chromium/ui/events/devices/device_data_manager_unittest.cc @@ -0,0 +1,109 @@ +// 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/events/devices/device_data_manager.h" + +#include "base/scoped_observer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/types/display_constants.h" +#include "ui/events/devices/device_hotplug_event_observer.h" +#include "ui/events/devices/input_device.h" +#include "ui/events/devices/input_device_event_observer.h" +#include "ui/events/devices/touch_device_transform.h" +#include "ui/events/devices/touchscreen_device.h" +#include "ui/events/test/device_data_manager_test_api.h" +#include "ui/gfx/transform.h" + +namespace ui { +namespace { + +class DeviceDataManagerTest : public testing::Test { + public: + DeviceDataManagerTest() {} + ~DeviceDataManagerTest() override {} + + // testing::Test: + void SetUp() override { DeviceDataManager::CreateInstance(); } + void TearDown() override { DeviceDataManager::DeleteInstance(); } + + private: + DISALLOW_COPY_AND_ASSIGN(DeviceDataManagerTest); +}; + +} // namespace + +TEST_F(DeviceDataManagerTest, DisplayIdUpdated) { + DeviceDataManager* device_data_manager = DeviceDataManager::GetInstance(); + std::vector<TouchscreenDevice> touchscreen_devices(1); + // Default id is invalid, need something other than 0 (0 is invalid). + constexpr int kTouchId = 1; + touchscreen_devices[0].id = kTouchId; + static_cast<DeviceHotplugEventObserver*>(device_data_manager) + ->OnTouchscreenDevicesUpdated(touchscreen_devices); + ASSERT_EQ(1u, device_data_manager->GetTouchscreenDevices().size()); + EXPECT_EQ(display::kInvalidDisplayId, + device_data_manager->GetTouchscreenDevices()[0].target_display_id); + + constexpr int64_t kDisplayId = 2; + std::vector<TouchDeviceTransform> touch_device_transforms(1); + touch_device_transforms[0].display_id = kDisplayId; + touch_device_transforms[0].device_id = kTouchId; + device_data_manager->ConfigureTouchDevices(touch_device_transforms); + ASSERT_EQ(1u, device_data_manager->GetTouchscreenDevices().size()); + EXPECT_EQ(kDisplayId, + device_data_manager->GetTouchscreenDevices()[0].target_display_id); +} + +namespace { + +class TestInputDeviceEventObserver : public InputDeviceEventObserver { + public: + TestInputDeviceEventObserver() = default; + + int on_touch_device_associations_changed_call_count() const { + return on_touch_device_associations_changed_call_count_; + } + + // InputDeviceEventObserver: + void OnTouchDeviceAssociationChanged() override { + on_touch_device_associations_changed_call_count_++; + } + + private: + int on_touch_device_associations_changed_call_count_ = 0; + + DISALLOW_COPY_AND_ASSIGN(TestInputDeviceEventObserver); +}; + +} // namespace + +TEST_F(DeviceDataManagerTest, AreTouchscreenTargetDisplaysValid) { + DeviceDataManager* device_data_manager = DeviceDataManager::GetInstance(); + EXPECT_FALSE(device_data_manager->AreTouchscreenTargetDisplaysValid()); + TestInputDeviceEventObserver observer; + ScopedObserver<DeviceDataManager, InputDeviceEventObserver> scoped_observer( + &observer); + scoped_observer.Add(device_data_manager); + test::DeviceDataManagerTestAPI().OnDeviceListsComplete(); + EXPECT_FALSE(device_data_manager->AreTouchscreenTargetDisplaysValid()); + EXPECT_EQ(0, observer.on_touch_device_associations_changed_call_count()); + + device_data_manager->ConfigureTouchDevices({}); + EXPECT_EQ(1, observer.on_touch_device_associations_changed_call_count()); + EXPECT_TRUE(device_data_manager->AreTouchscreenTargetDisplaysValid()); + + std::vector<TouchscreenDevice> touchscreen_devices(1); + // Default id is invalid, need something other than 0 (0 is invalid). + constexpr int kTouchId = 1; + touchscreen_devices[0].id = kTouchId; + static_cast<DeviceHotplugEventObserver*>(device_data_manager) + ->OnTouchscreenDevicesUpdated(touchscreen_devices); + EXPECT_EQ(1, observer.on_touch_device_associations_changed_call_count()); + EXPECT_FALSE(device_data_manager->AreTouchscreenTargetDisplaysValid()); + device_data_manager->ConfigureTouchDevices({}); + EXPECT_EQ(2, observer.on_touch_device_associations_changed_call_count()); + EXPECT_TRUE(device_data_manager->AreTouchscreenTargetDisplaysValid()); +} + +} // namespace ui diff --git a/chromium/ui/events/devices/input_device_event_observer.h b/chromium/ui/events/devices/input_device_event_observer.h index 4ea7d46e040..c2507b93999 100644 --- a/chromium/ui/events/devices/input_device_event_observer.h +++ b/chromium/ui/events/devices/input_device_event_observer.h @@ -23,6 +23,10 @@ class EVENTS_DEVICES_EXPORT InputDeviceEventObserver { virtual void OnDeviceListsComplete() {} virtual void OnStylusStateChanged(StylusState state) {} + // Called when ConfigureTouchDevices() is called. This indicates the + // transform, scale and/or device<->display mapping has changed. + virtual void OnTouchDeviceAssociationChanged() {} + protected: InputDeviceEventObserver() {} }; diff --git a/chromium/ui/events/devices/input_device_manager.h b/chromium/ui/events/devices/input_device_manager.h index 59526ea357d..dcbd3167b80 100644 --- a/chromium/ui/events/devices/input_device_manager.h +++ b/chromium/ui/events/devices/input_device_manager.h @@ -35,6 +35,10 @@ class EVENTS_DEVICES_EXPORT InputDeviceManager { virtual bool AreDeviceListsComplete() const = 0; virtual bool AreTouchscreensEnabled() const = 0; + // Returns true if the |target_display_id| of the TouchscreenDevices returned + // from GetTouchscreenDevices() is valid. + virtual bool AreTouchscreenTargetDisplaysValid() const = 0; + virtual void AddObserver(InputDeviceEventObserver* observer) = 0; virtual void RemoveObserver(InputDeviceEventObserver* observer) = 0; diff --git a/chromium/ui/events/devices/input_device_observer_android.cc b/chromium/ui/events/devices/input_device_observer_android.cc index 3313b7fea43..509234ebed5 100644 --- a/chromium/ui/events/devices/input_device_observer_android.cc +++ b/chromium/ui/events/devices/input_device_observer_android.cc @@ -19,10 +19,6 @@ using base::android::JavaParamRef; namespace ui { -bool InputDeviceObserverAndroid::RegisterInputDeviceObserver(JNIEnv* env) { - return RegisterNativesImpl(env); -} - InputDeviceObserverAndroid::InputDeviceObserverAndroid() {} InputDeviceObserverAndroid::~InputDeviceObserverAndroid() {} @@ -64,4 +60,4 @@ NOTIFY_OBSERVERS(NotifyObserversTouchpadDeviceConfigurationChanged(), NOTIFY_OBSERVERS(NotifyObserversKeyboardDeviceConfigurationChanged(), OnKeyboardDeviceConfigurationChanged()); -} // namespace ui
\ No newline at end of file +} // namespace ui diff --git a/chromium/ui/events/devices/input_device_observer_android.h b/chromium/ui/events/devices/input_device_observer_android.h index 311dfa821d0..b4d1be637b5 100644 --- a/chromium/ui/events/devices/input_device_observer_android.h +++ b/chromium/ui/events/devices/input_device_observer_android.h @@ -25,8 +25,6 @@ class EVENTS_DEVICES_EXPORT InputDeviceObserverAndroid { static InputDeviceObserverAndroid* GetInstance(); ~InputDeviceObserverAndroid(); - static bool RegisterInputDeviceObserver(JNIEnv* env); - void AddObserver(ui::InputDeviceEventObserver* observer); void RemoveObserver(ui::InputDeviceEventObserver* observer); diff --git a/chromium/ui/events/devices/mojo/BUILD.gn b/chromium/ui/events/devices/mojo/BUILD.gn index fe65d7d1973..ee7bc482818 100644 --- a/chromium/ui/events/devices/mojo/BUILD.gn +++ b/chromium/ui/events/devices/mojo/BUILD.gn @@ -7,10 +7,12 @@ import("//mojo/public/tools/bindings/mojom.gni") mojom("mojo") { sources = [ "input_devices.mojom", + "touch_device_transform.mojom", ] public_deps = [ "//ui/gfx/geometry/mojo", + "//ui/gfx/mojo", ] } diff --git a/chromium/ui/events/devices/mojo/OWNERS b/chromium/ui/events/devices/mojo/OWNERS index 1841ff6198d..e75daf744a0 100644 --- a/chromium/ui/events/devices/mojo/OWNERS +++ b/chromium/ui/events/devices/mojo/OWNERS @@ -3,3 +3,6 @@ per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS per-file *.mojom=set noparent per-file *.mojom=file://ipc/SECURITY_OWNERS + +per-file *.typemap=set noparent +per-file *.typemap=file://ipc/SECURITY_OWNERS diff --git a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform.mojom b/chromium/ui/events/devices/mojo/touch_device_transform.mojom index 3daa9913337..5b8e9c23f5d 100644 --- a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform.mojom +++ b/chromium/ui/events/devices/mojo/touch_device_transform.mojom @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -module display.mojom; +module ui.mojom; import "ui/gfx/mojo/transform.mojom"; @@ -10,4 +10,5 @@ struct TouchDeviceTransform { int64 display_id; int32 device_id; gfx.mojom.Transform transform; + double radius_scale; }; diff --git a/chromium/ui/events/devices/mojo/touch_device_transform.typemap b/chromium/ui/events/devices/mojo/touch_device_transform.typemap new file mode 100644 index 00000000000..689eb3b1e3b --- /dev/null +++ b/chromium/ui/events/devices/mojo/touch_device_transform.typemap @@ -0,0 +1,14 @@ +# 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. + +mojom = "//ui/events/devices/mojo/touch_device_transform.mojom" +public_headers = [ "//ui/events/devices/touch_device_transform.h" ] +traits_headers = + [ "//ui/events/devices/mojo/touch_device_transform_struct_traits.h" ] +deps = [] +public_deps = [ + "//ui/gfx", + "//ui/gfx/mojo", +] +type_mappings = [ "ui.mojom.TouchDeviceTransform=ui::TouchDeviceTransform" ] diff --git a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform_struct_traits.h b/chromium/ui/events/devices/mojo/touch_device_transform_struct_traits.h index 296ed5092b4..b9dd4506cbf 100644 --- a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform_struct_traits.h +++ b/chromium/ui/events/devices/mojo/touch_device_transform_struct_traits.h @@ -7,33 +7,36 @@ #include <stdint.h> -#include "ui/display/manager/chromeos/mojo/touch_device_transform.mojom.h" -#include "ui/display/manager/chromeos/touch_device_transform.h" +#include "ui/events/devices/mojo/touch_device_transform.mojom.h" +#include "ui/events/devices/touch_device_transform.h" #include "ui/gfx/mojo/transform_struct_traits.h" namespace mojo { template <> -struct StructTraits<display::mojom::TouchDeviceTransformDataView, - display::TouchDeviceTransform> { +struct StructTraits<ui::mojom::TouchDeviceTransformDataView, + ui::TouchDeviceTransform> { public: - static int64_t display_id(const display::TouchDeviceTransform& r) { + static int64_t display_id(const ui::TouchDeviceTransform& r) { return r.display_id; } - static int32_t device_id(const display::TouchDeviceTransform& r) { + static int32_t device_id(const ui::TouchDeviceTransform& r) { return r.device_id; } - static const gfx::Transform& transform( - const display::TouchDeviceTransform& r) { + static const gfx::Transform& transform(const ui::TouchDeviceTransform& r) { return r.transform; } + static double radius_scale(const ui::TouchDeviceTransform& r) { + return r.radius_scale; + } - static bool Read(display::mojom::TouchDeviceTransformDataView data, - display::TouchDeviceTransform* out) { + static bool Read(ui::mojom::TouchDeviceTransformDataView data, + ui::TouchDeviceTransform* out) { out->display_id = data.display_id(); out->device_id = data.device_id(); if (!data.ReadTransform(&(out->transform))) return false; + out->radius_scale = data.radius_scale(); return true; } }; diff --git a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform_struct_traits_unittest.cc b/chromium/ui/events/devices/mojo/touch_device_transform_struct_traits_unittest.cc index 42d18dafca9..c9453d5cd83 100644 --- a/chromium/ui/display/manager/chromeos/mojo/touch_device_transform_struct_traits_unittest.cc +++ b/chromium/ui/events/devices/mojo/touch_device_transform_struct_traits_unittest.cc @@ -2,13 +2,13 @@ // 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/mojo/touch_device_transform_struct_traits.h" +#include "ui/events/devices/mojo/touch_device_transform_struct_traits.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/display/manager/chromeos/mojo/touch_device_transform.mojom.h" -#include "ui/display/manager/chromeos/touch_device_transform.h" +#include "ui/events/devices/mojo/touch_device_transform.mojom.h" +#include "ui/events/devices/touch_device_transform.h" -namespace display { +namespace ui { TEST(TouchDeviceTransformStructTraitsTest, SerializeAndDeserialize) { TouchDeviceTransform touch_device_transform; @@ -16,6 +16,7 @@ TEST(TouchDeviceTransformStructTraitsTest, SerializeAndDeserialize) { touch_device_transform.device_id = 202; touch_device_transform.transform = gfx::Transform(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + touch_device_transform.radius_scale = 4; TouchDeviceTransform deserialized; ASSERT_TRUE(mojom::TouchDeviceTransform::Deserialize( mojom::TouchDeviceTransform::Serialize(&touch_device_transform), @@ -23,6 +24,7 @@ TEST(TouchDeviceTransformStructTraitsTest, SerializeAndDeserialize) { EXPECT_EQ(touch_device_transform.display_id, deserialized.display_id); EXPECT_EQ(touch_device_transform.device_id, deserialized.device_id); EXPECT_EQ(touch_device_transform.transform, deserialized.transform); + EXPECT_EQ(touch_device_transform.radius_scale, deserialized.radius_scale); } -} // namespace display +} // namespace ui diff --git a/chromium/ui/events/devices/mojo/typemaps.gni b/chromium/ui/events/devices/mojo/typemaps.gni index 7ce719ad827..727e53fcf73 100644 --- a/chromium/ui/events/devices/mojo/typemaps.gni +++ b/chromium/ui/events/devices/mojo/typemaps.gni @@ -2,4 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -typemaps = [ "//ui/events/devices/mojo/input_device.typemap" ] +typemaps = [ + "//ui/events/devices/mojo/input_device.typemap", + "//ui/events/devices/mojo/touch_device_transform.typemap", +] diff --git a/chromium/ui/display/manager/chromeos/touch_device_transform.cc b/chromium/ui/events/devices/touch_device_transform.cc index 50e8c20e920..dd8c410215d 100644 --- a/chromium/ui/display/manager/chromeos/touch_device_transform.cc +++ b/chromium/ui/events/devices/touch_device_transform.cc @@ -2,12 +2,12 @@ // 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/touch_device_transform.h" +#include "ui/events/devices/touch_device_transform.h" -namespace display { +namespace ui { TouchDeviceTransform::TouchDeviceTransform() = default; TouchDeviceTransform::~TouchDeviceTransform() = default; -} // namespace display +} // namespace ui diff --git a/chromium/ui/events/devices/touch_device_transform.h b/chromium/ui/events/devices/touch_device_transform.h new file mode 100644 index 00000000000..767fe50b110 --- /dev/null +++ b/chromium/ui/events/devices/touch_device_transform.h @@ -0,0 +1,30 @@ +// 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_EVENTS_DEVICES_TOUCH_DEVICE_TRANSFORM_H_ +#define UI_EVENTS_DEVICES_TOUCH_DEVICE_TRANSFORM_H_ + +#include <stdint.h> + +#include "ui/display/types/display_constants.h" +#include "ui/events/devices/events_devices_export.h" +#include "ui/events/devices/input_device.h" +#include "ui/gfx/transform.h" + +namespace ui { + +struct EVENTS_DEVICES_EXPORT TouchDeviceTransform { + TouchDeviceTransform(); + ~TouchDeviceTransform(); + + int64_t display_id = display::kInvalidDisplayId; + int32_t device_id = InputDevice::kInvalidId; + gfx::Transform transform; + // Amount to scale the touch radius by. + double radius_scale = 1; +}; + +} // namespace ui + +#endif // UI_EVENTS_DEVICES_TOUCH_DEVICE_TRANSFORM_H_ diff --git a/chromium/ui/events/devices/touchscreen_device.cc b/chromium/ui/events/devices/touchscreen_device.cc index b840b9681d1..3c2a4a646fa 100644 --- a/chromium/ui/events/devices/touchscreen_device.cc +++ b/chromium/ui/events/devices/touchscreen_device.cc @@ -10,27 +10,19 @@ namespace ui { -TouchscreenDevice::TouchscreenDevice() : touch_points(0), is_stylus(false) {} +TouchscreenDevice::TouchscreenDevice() : touch_points(0) {} TouchscreenDevice::TouchscreenDevice(int id, InputDeviceType type, const std::string& name, const gfx::Size& size, int touch_points) - : InputDevice(id, type, name), - size(size), - touch_points(touch_points), - is_stylus(false) { -} + : InputDevice(id, type, name), size(size), touch_points(touch_points) {} TouchscreenDevice::TouchscreenDevice(const InputDevice& input_device, const gfx::Size& size, int touch_points) - : InputDevice(input_device), - size(size), - touch_points(touch_points), - is_stylus(false) { -} + : InputDevice(input_device), size(size), touch_points(touch_points) {} TouchscreenDevice::~TouchscreenDevice() {} diff --git a/chromium/ui/events/devices/touchscreen_device.h b/chromium/ui/events/devices/touchscreen_device.h index ee024a0f860..958151a4487 100644 --- a/chromium/ui/events/devices/touchscreen_device.h +++ b/chromium/ui/events/devices/touchscreen_device.h @@ -5,8 +5,11 @@ #ifndef UI_EVENTS_DEVICES_TOUCHSCREEN_DEVICE_H_ #define UI_EVENTS_DEVICES_TOUCHSCREEN_DEVICE_H_ +#include <stdint.h> + #include <string> +#include "ui/display/types/display_constants.h" #include "ui/events/devices/events_devices_export.h" #include "ui/events/devices/input_device.h" #include "ui/gfx/geometry/size.h" @@ -33,8 +36,13 @@ struct EVENTS_DEVICES_EXPORT TouchscreenDevice : public InputDevice { gfx::Size size; // Size of the touch screen area. int touch_points; // The number of touch points this device supports (0 if // unknown). - bool is_stylus; // True if the specified touchscreen device is stylus - // capable. + // True if the specified touchscreen device is stylus capable. + bool is_stylus = false; + // Id of the display the touch device targets. + // NOTE: when obtaining TouchscreenDevice from InputDeviceManager this value + // may not have been updated. See + // InputDeviceManager::AreTouchscreenTargetDisplaysValid() for details. + int64_t target_display_id = display::kInvalidDisplayId; }; } // namespace ui diff --git a/chromium/ui/events/devices/x11/device_data_manager_x11.cc b/chromium/ui/events/devices/x11/device_data_manager_x11.cc index f2182752b36..45ae658eb67 100644 --- a/chromium/ui/events/devices/x11/device_data_manager_x11.cc +++ b/chromium/ui/events/devices/x11/device_data_manager_x11.cc @@ -146,7 +146,7 @@ bool DeviceDataManagerX11::IsTouchDataType(const int type) { // static void DeviceDataManagerX11::CreateInstance() { - if (instance()) + if (HasInstance()) return; DeviceDataManagerX11* device_data_manager = new DeviceDataManagerX11(); diff --git a/chromium/ui/events/devices/x11/touch_factory_x11.cc b/chromium/ui/events/devices/x11/touch_factory_x11.cc index 7df258655ee..ab63a07ae36 100644 --- a/chromium/ui/events/devices/x11/touch_factory_x11.cc +++ b/chromium/ui/events/devices/x11/touch_factory_x11.cc @@ -272,7 +272,7 @@ int TouchFactory::GetSlotForTrackingID(uint32_t tracking_id) { } void TouchFactory::ReleaseSlotForTrackingID(uint32_t tracking_id) { - id_generator_.ReleaseNumber(tracking_id); + id_generator_.MaybeReleaseNumber(tracking_id); } bool TouchFactory::IsTouchDevicePresent() { diff --git a/chromium/ui/events/event.cc b/chromium/ui/events/event.cc index 7cb7a32d90f..c487f8f1f3c 100644 --- a/chromium/ui/events/event.cc +++ b/chromium/ui/events/event.cc @@ -409,25 +409,10 @@ Event::Event(const base::NativeEvent& native_event, phase_(EP_PREDISPATCH), result_(ER_UNHANDLED), source_device_id_(ED_UNKNOWN_DEVICE) { - base::TimeDelta delta = EventTimeForNow() - time_stamp_; if (type_ < ET_LAST) latency()->set_source_event_type(EventTypeToLatencySourceEventType(type)); - base::HistogramBase::Sample delta_sample = - static_cast<base::HistogramBase::Sample>(delta.InMicroseconds()); - UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.Browser", delta_sample, 1, 1000000, - 100); ComputeEventLatencyOS(native_event); - // Though it seems inefficient to generate the string twice, the first - // instance will be used only for DCHECK builds and the second won't be - // executed at all if the histogram was previously accessed here. - STATIC_HISTOGRAM_POINTER_GROUP( - base::StringPrintf("Event.Latency.Browser.%s", GetName()), type_, ET_LAST, - Add(delta_sample), - base::Histogram::FactoryGet( - base::StringPrintf("Event.Latency.Browser.%s", GetName()), 1, 1000000, - 100, base::HistogramBase::kUmaTargetedHistogramFlag)); - #if defined(USE_X11) if (native_event->type == GenericEvent) { XIDeviceEvent* xiev = @@ -495,12 +480,22 @@ LocatedEvent::LocatedEvent(EventType type, root_location_(root_location) {} void LocatedEvent::UpdateForRootTransform( - const gfx::Transform& reversed_root_transform) { - // Transform has to be done at root level. - gfx::Point3F p(location_); - reversed_root_transform.TransformPoint(&p); - location_ = p.AsPointF(); - root_location_ = location_; + const gfx::Transform& reversed_root_transform, + const gfx::Transform& reversed_local_transform) { + if (target()) { + gfx::Point3F transformed_location(location_); + reversed_local_transform.TransformPoint(&transformed_location); + location_ = transformed_location.AsPointF(); + + gfx::Point3F transformed_root_location(root_location_); + reversed_root_transform.TransformPoint(&transformed_root_location); + root_location_ = transformed_root_location.AsPointF(); + } else { + // This mirrors what the code previously did. + gfx::Point3F transformed_location(location_); + reversed_root_transform.TransformPoint(&transformed_location); + root_location_ = location_ = transformed_location.AsPointF(); + } } //////////////////////////////////////////////////////////////////////////////// @@ -572,8 +567,7 @@ MouseEvent::MouseEvent(const base::NativeEvent& native_event) changed_button_flags_(GetChangedMouseButtonFlagsFromNative(native_event)), pointer_details_(GetMousePointerDetailsFromNative(native_event)) { latency()->AddLatencyNumberWithTimestamp( - INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, - base::TimeTicks::FromInternalValue(time_stamp().ToInternalValue()), 1); + INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, time_stamp(), 1); latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0); if (type() == ET_MOUSE_PRESSED || type() == ET_MOUSE_RELEASED) SetClickCount(GetRepeatCount(*this)); @@ -840,8 +834,7 @@ TouchEvent::TouchEvent(const base::NativeEvent& native_event) should_remove_native_touch_id_mapping_(false), pointer_details_(GetTouchPointerDetailsFromNative(native_event)) { latency()->AddLatencyNumberWithTimestamp( - INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, - base::TimeTicks::FromInternalValue(time_stamp().ToInternalValue()), 1); + INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, time_stamp(), 1); latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0); FixRotationAngle(); @@ -923,8 +916,10 @@ TouchEvent::~TouchEvent() { } void TouchEvent::UpdateForRootTransform( - const gfx::Transform& inverted_root_transform) { - LocatedEvent::UpdateForRootTransform(inverted_root_transform); + const gfx::Transform& inverted_root_transform, + const gfx::Transform& inverted_local_transform) { + LocatedEvent::UpdateForRootTransform(inverted_root_transform, + inverted_local_transform); gfx::DecomposedTransform decomp; bool success = gfx::DecomposeTransform(&decomp, inverted_root_transform); DCHECK(success); @@ -1149,8 +1144,7 @@ KeyEvent::KeyEvent(const base::NativeEvent& native_event, int event_flags) code_(CodeFromNative(native_event)), is_char_(IsCharFromNative(native_event)) { latency()->AddLatencyNumberWithTimestamp( - INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, - base::TimeTicks::FromInternalValue(time_stamp().ToInternalValue()), 1); + INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, time_stamp(), 1); latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0); if (IsRepeated(*this)) diff --git a/chromium/ui/events/event.h b/chromium/ui/events/event.h index 48b652ed0f0..6b5ccbd3096 100644 --- a/chromium/ui/events/event.h +++ b/chromium/ui/events/event.h @@ -114,6 +114,8 @@ class EVENTS_EXPORT Event { bool IsAltGrDown() const { return (flags_ & EF_ALTGR_DOWN) != 0; } bool IsCapsLockOn() const { return (flags_ & EF_CAPS_LOCK_ON) != 0; } + bool IsSynthesized() const { return (flags_ & EF_IS_SYNTHESIZED) != 0; } + bool IsCancelModeEvent() const { return type_ == ET_CANCEL_MODE; } @@ -363,10 +365,12 @@ class EVENTS_EXPORT LocatedEvent : public Event { return root_location_; } - // Transform the locations using |inverted_root_transform|. - // This is applied to both |location_| and |root_location_|. + // Transform the locations using |inverted_root_transform| and + // |inverted_local_transform|. |inverted_local_transform| is only used if + // the event has a target. virtual void UpdateForRootTransform( - const gfx::Transform& inverted_root_transform); + const gfx::Transform& inverted_root_transform, + const gfx::Transform& inverted_local_transform); template <class T> void ConvertLocationToTarget(T* source, T* target) { if (!target || target == source) @@ -698,7 +702,8 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { // Overridden from LocatedEvent. void UpdateForRootTransform( - const gfx::Transform& inverted_root_transform) override; + const gfx::Transform& inverted_root_transform, + const gfx::Transform& inverted_local_transform) override; // Marks the event as not participating in synchronous gesture recognition. void DisableSynchronousHandling(); diff --git a/chromium/ui/events/event_constants.h b/chromium/ui/events/event_constants.h index 063058912fc..79ca132182b 100644 --- a/chromium/ui/events/event_constants.h +++ b/chromium/ui/events/event_constants.h @@ -136,9 +136,7 @@ enum MouseEventFlags { // from an unconsumed touch/gesture event. EF_TOUCH_ACCESSIBILITY = 1 << 19, // Indicates this event was generated from // touch accessibility mode. - EF_DIRECT_INPUT = 1 << 20, // Mouse event coming from direct, - // on-screen input. - EF_CURSOR_HIDE = 1 << 21, // Indicates this mouse event is generated + 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. }; diff --git a/chromium/ui/events/event_unittest.cc b/chromium/ui/events/event_unittest.cc index 29e5048a0e4..d72bb325be3 100644 --- a/chromium/ui/events/event_unittest.cc +++ b/chromium/ui/events/event_unittest.cc @@ -17,6 +17,8 @@ #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/test/events_test_utils.h" +#include "ui/events/test/test_event_target.h" +#include "ui/gfx/transform.h" #if defined(USE_X11) #include <X11/Xlib.h> @@ -1098,4 +1100,47 @@ TEST(EventTest, EventLatencyOSMouseWheelHistogram) { #endif } +TEST(EventTest, UpdateForRootTransformation) { + gfx::Transform identity_transform; + const gfx::Point location(10, 10); + const gfx::Point root_location(20, 20); + + // A mouse event that is untargeted should reset the root location when + // transformed. Though the events start out with different locations and + // root_locations, they should be equal afterwards. + ui::MouseEvent untargeted(ET_MOUSE_PRESSED, location, root_location, + EventTimeForNow(), 0, 0); + untargeted.UpdateForRootTransform(identity_transform, identity_transform); + EXPECT_EQ(location, untargeted.location()); + EXPECT_EQ(location, untargeted.root_location()); + + ui::test::TestEventTarget target; + + // A mouse event that is targeted should not set the root location to the + // local location. They start with different locations and should stay + // unequal after a transform is applied. + { + ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location, + EventTimeForNow(), 0, 0); + Event::DispatcherApi(&targeted).set_target(&target); + targeted.UpdateForRootTransform(identity_transform, identity_transform); + EXPECT_EQ(location, targeted.location()); + EXPECT_EQ(root_location, targeted.root_location()); + } + + { + // Targeted event with 2x and 3x scales. + gfx::Transform transform2x; + transform2x.Scale(2, 2); + gfx::Transform transform3x; + transform3x.Scale(3, 3); + ui::MouseEvent targeted(ET_MOUSE_PRESSED, location, root_location, + EventTimeForNow(), 0, 0); + Event::DispatcherApi(&targeted).set_target(&target); + targeted.UpdateForRootTransform(transform2x, transform3x); + EXPECT_EQ(gfx::Point(30, 30), targeted.location()); + EXPECT_EQ(gfx::Point(40, 40), targeted.root_location()); + } +} + } // namespace ui diff --git a/chromium/ui/events/gesture_detection/gesture_detector.cc b/chromium/ui/events/gesture_detection/gesture_detector.cc index a3f73861fe4..b7453add2d4 100644 --- a/chromium/ui/events/gesture_detection/gesture_detector.cc +++ b/chromium/ui/events/gesture_detection/gesture_detector.cc @@ -13,6 +13,7 @@ #include <cmath> #include "base/timer/timer.h" +#include "build/build_config.h" #include "ui/events/gesture_detection/gesture_listeners.h" #include "ui/events/gesture_detection/motion_event.h" @@ -59,7 +60,8 @@ GestureDetector::Config::Config() two_finger_tap_max_separation(300), two_finger_tap_timeout(base::TimeDelta::FromMilliseconds(700)), single_tap_repeat_interval(1), - velocity_tracker_strategy(VelocityTracker::Strategy::STRATEGY_DEFAULT) {} + velocity_tracker_strategy(VelocityTracker::Strategy::STRATEGY_DEFAULT) { +} GestureDetector::Config::Config(const Config& other) = default; @@ -291,8 +293,8 @@ bool GestureDetector::OnTouchEvent(const MotionEvent& ev) { two_finger_tap_allowed_for_gesture_ = two_finger_tap_enabled_; maximum_pointer_count_ = 1; - // Always start the SHOW_PRESS timer before the LONG_PRESS timer to ensure - // proper timeout ordering. + // Always start the SHOW_PRESS timer before the LONG_PRESS timer to + // ensure proper timeout ordering. if (showpress_enabled_) timeout_handler_->StartTimeout(SHOW_PRESS); if (longpress_enabled_) diff --git a/chromium/ui/events/gesture_detection/gesture_provider.cc b/chromium/ui/events/gesture_detection/gesture_provider.cc index 2c0c9bf5dc7..39b8a2d1504 100644 --- a/chromium/ui/events/gesture_detection/gesture_provider.cc +++ b/chromium/ui/events/gesture_detection/gesture_provider.cc @@ -323,8 +323,8 @@ class GestureProvider::GestureListenerImpl : public ScaleGestureListener, if (!scroll_event_sent_) { // Note that scroll start hints are in distance traveled, where // scroll deltas are in the opposite direction. - GestureEventDetails scroll_details( - ET_GESTURE_SCROLL_BEGIN, -raw_distance_x, -raw_distance_y); + GestureEventDetails scroll_details(ET_GESTURE_SCROLL_BEGIN, -distance_x, + -distance_y); scroll_details.set_device_type(GestureDeviceType::DEVICE_TOUCHSCREEN); // Scroll focus point always starts with the first touch down point. diff --git a/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc b/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc index ad6d1c9863e..50390db57e1 100644 --- a/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc +++ b/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc @@ -917,8 +917,11 @@ TEST_F(GestureProviderTest, FractionalScroll) { // Generate a scroll gesture and verify that the resulting scroll begin event // has the expected hint values. TEST_F(GestureProviderTest, ScrollBeginValues) { - const float delta_x = 13; - const float delta_y = 89; + const float delta_x = 14; + const float delta_y = 48; + // These are the deltas after subtracting slop region and railing. + const float delta_x_hint = 0; + const float delta_y_hint = 40.32f; const base::TimeTicks event_time = TimeTicks::Now(); @@ -944,8 +947,8 @@ TEST_F(GestureProviderTest, ScrollBeginValues) { const GestureEventData* scroll_begin_gesture = GetActiveScrollBeginEvent(); ASSERT_TRUE(scroll_begin_gesture); - EXPECT_EQ(delta_x, scroll_begin_gesture->details.scroll_x_hint()); - EXPECT_EQ(delta_y, scroll_begin_gesture->details.scroll_y_hint()); + EXPECT_EQ(delta_x_hint, scroll_begin_gesture->details.scroll_x_hint()); + EXPECT_EQ(delta_y_hint, scroll_begin_gesture->details.scroll_y_hint()); } // The following three tests verify that slop regions are checked for diff --git a/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc b/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc index 5d997692ad0..b8090b2a82c 100644 --- a/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc +++ b/chromium/ui/events/gesture_detection/motion_event_buffer_unittest.cc @@ -202,7 +202,7 @@ class MotionEventBufferTest : public testing::Test, base::TimeDelta dt = current_flushed_event->GetEventTime() - last_flushed_event->GetEventTime(); - EXPECT_GE(dt.ToInternalValue(), 0); + EXPECT_GE(dt, base::TimeDelta()); // A time delta of 0 is possible if the flush rate is greater than the // event rate, in which case we can simply skip forward. if (dt.is_zero()) diff --git a/chromium/ui/events/gesture_detection/motion_event_generic.cc b/chromium/ui/events/gesture_detection/motion_event_generic.cc index ad4d7286d36..35a5b885585 100644 --- a/chromium/ui/events/gesture_detection/motion_event_generic.cc +++ b/chromium/ui/events/gesture_detection/motion_event_generic.cc @@ -268,8 +268,7 @@ void MotionEventGeneric::PushHistoricalEvent( DCHECK_EQ(event->GetAction(), ACTION_MOVE); DCHECK_EQ(event->GetPointerCount(), GetPointerCount()); DCHECK_EQ(event->GetAction(), GetAction()); - DCHECK_LE(event->GetEventTime().ToInternalValue(), - GetEventTime().ToInternalValue()); + DCHECK_LE(event->GetEventTime(), GetEventTime()); historical_events_.push_back(std::move(event)); } 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 2f2997e8e47..a9901956a9e 100644 --- a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc +++ b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.cc @@ -32,7 +32,6 @@ namespace { std::unique_ptr<GestureCurve> CreateDefaultPlatformCurve( blink::WebGestureDevice device_source, const gfx::Vector2dF& initial_velocity) { - DCHECK(!initial_velocity.IsZero()); if (device_source == blink::kWebGestureDeviceSyntheticAutoscroll) { return base::MakeUnique<FixedVelocityCurve>(initial_velocity, base::TimeTicks()); @@ -83,37 +82,11 @@ WebGestureCurveImpl::WebGestureCurveImpl(std::unique_ptr<GestureCurve> curve, ThreadType animating_thread_type) : curve_(std::move(curve)), last_offset_(initial_offset), - animating_thread_type_(animating_thread_type), ticks_since_first_animate_(0), first_animate_time_(0), last_animate_time_(0) {} -WebGestureCurveImpl::~WebGestureCurveImpl() { - if (ticks_since_first_animate_ <= 1) - return; - - if (last_animate_time_ <= first_animate_time_) - return; - - switch (animating_thread_type_) { - case ThreadType::MAIN: - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Event.Frequency.Renderer.FlingAnimate", - gfx::ToRoundedInt(ticks_since_first_animate_ / - (last_animate_time_ - first_animate_time_)), - 1, 240, 120); - break; - case ThreadType::IMPL: - UMA_HISTOGRAM_CUSTOM_COUNTS( - "Event.Frequency.RendererImpl.FlingAnimate", - gfx::ToRoundedInt(ticks_since_first_animate_ / - (last_animate_time_ - first_animate_time_)), - 1, 240, 120); - break; - case ThreadType::TEST: - break; - } -} +WebGestureCurveImpl::~WebGestureCurveImpl() {} bool WebGestureCurveImpl::Apply(double time, blink::WebGestureCurveTarget* target) { 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 076f0169d53..ecadeeb2e5f 100644 --- a/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h +++ b/chromium/ui/events/gestures/blink/web_gesture_curve_impl.h @@ -22,7 +22,7 @@ class WebGestureCurveTarget; namespace ui { class GestureCurve; -class WebGestureCurveImpl : public NON_EXPORTED_BASE(blink::WebGestureCurve) { +class WebGestureCurveImpl : public blink::WebGestureCurve { public: static std::unique_ptr<blink::WebGestureCurve> CreateFromDefaultPlatformCurve( blink::WebGestureDevice device_source, @@ -53,7 +53,6 @@ class WebGestureCurveImpl : public NON_EXPORTED_BASE(blink::WebGestureCurve) { gfx::Vector2dF last_offset_; - ThreadType animating_thread_type_; int64_t ticks_since_first_animate_; double first_animate_time_; double last_animate_time_; diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl.h b/chromium/ui/events/gestures/gesture_recognizer_impl.h index f06cf6dc613..52901ce251c 100644 --- a/chromium/ui/events/gestures/gesture_recognizer_impl.h +++ b/chromium/ui/events/gestures/gesture_recognizer_impl.h @@ -59,6 +59,10 @@ class EVENTS_EXPORT GestureRecognizerImpl : public GestureRecognizer, virtual GestureProviderAura* GetGestureProviderForConsumer( GestureConsumer* c); + // Overridden from GestureRecognizer + bool ProcessTouchEventPreDispatch(TouchEvent* event, + GestureConsumer* consumer) override; + private: // Sets up the target consumer for gestures based on the touch-event. void SetupTargets(const TouchEvent& event, GestureConsumer* consumer); @@ -66,10 +70,6 @@ class EVENTS_EXPORT GestureRecognizerImpl : public GestureRecognizer, void DispatchGestureEvent(GestureConsumer* raw_input_consumer, GestureEvent* event); - // Overridden from GestureRecognizer - bool ProcessTouchEventPreDispatch(TouchEvent* event, - GestureConsumer* consumer) override; - Gestures AckTouchEvent(uint32_t unique_event_id, ui::EventResult result, bool is_source_touch_event_set_non_blocking, diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm index 143f9b1f831..d6427700c41 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm @@ -18,6 +18,12 @@ namespace ui { namespace { +bool IsUnicodeControl(unichar c) { + // C0 control characters: http://unicode.org/charts/PDF/U0000.pdf + // C1 control characters: http://unicode.org/charts/PDF/U0080.pdf + return c <= 0x1F || (c >= 0x7F && c <= 0x9F); +} + // This value is not defined but shows up as 0x36. const int kVK_RightCommand = 0x36; // Context menu is not defined but shows up as 0x6E. @@ -850,18 +856,10 @@ DomKey DomKeyFromNSEvent(NSEvent* event) { // e.g. On French keyboard [+a will produce "^q", DomKey should be 'q'. unichar dom_key_char = [characters characterAtIndex:[characters length] - 1]; - const bool is_ctrl_down = ([event modifierFlags] & NSControlKeyMask) && - !([event modifierFlags] & NSAlternateKeyMask); - const bool is_command_down = [event modifierFlags] & NSCommandKeyMask; - // On Mac Blink won't insert ASCII character if either Ctrl or Command, or - // both, are down. - // See EditingBehavior::shouldInsertCharacter() - if (std::iscntrl(dom_key_char) || - (dom_key_char < 0x80 && (is_ctrl_down || is_command_down))) { - // According to spec if the key combination produces a non-printable - // character, the key value should be the character without modifiers - // except Shift and AltGr. - // See https://w3c.github.io/uievents/#keys-guidelines + if (IsUnicodeControl(dom_key_char)) { + // Filter non-glyph modifiers if the generated characters are part of + // Unicode 'Other, Control' General Category. + // https://w3c.github.io/uievents-key/#selecting-key-attribute-values bool unused_is_dead_key; const int kAllowedModifiersMask = NSShiftKeyMask | NSAlphaShiftKeyMask | NSAlternateKeyMask; @@ -870,7 +868,10 @@ DomKey DomKeyFromNSEvent(NSEvent* event) { [event keyCode], [event modifierFlags] & kAllowedModifiersMask, &unused_is_dead_key); } - if (!std::iscntrl(dom_key_char)) + + // We need to check again because keys like ESC will produce control + // characters even without any modifiers. + if (!IsUnicodeControl(dom_key_char)) return DomKeyFromCharCode(dom_key_char); } } diff --git a/chromium/ui/events/mojo/BUILD.gn b/chromium/ui/events/mojo/BUILD.gn index 34b89eb6643..49165b33496 100644 --- a/chromium/ui/events/mojo/BUILD.gn +++ b/chromium/ui/events/mojo/BUILD.gn @@ -12,6 +12,7 @@ mojom("interfaces") { ] public_deps = [ + "//mojo/common:common_custom_types", "//ui/gfx/mojo", "//ui/latency/mojo:interfaces", ] diff --git a/chromium/ui/events/mojo/event.mojom b/chromium/ui/events/mojo/event.mojom index 48c7cd55c5e..04312539cca 100644 --- a/chromium/ui/events/mojo/event.mojom +++ b/chromium/ui/events/mojo/event.mojom @@ -4,6 +4,7 @@ module ui.mojom; +import "mojo/common/time.mojom"; import "ui/events/mojo/event_constants.mojom"; import "ui/events/mojo/keyboard_codes.mojom"; import "ui/latency/mojo/latency_info.mojom"; @@ -115,10 +116,10 @@ struct Event { // // TODO(sky): parts of this should move to PointerData. int32 flags; - // Time in microseconds from when the platform was started. - // This value accurately orders events w.r.t. to each other but - // does not position them at an absolute time. - int64 time_stamp; + // 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; LatencyInfo latency; KeyData? key_data; PointerData? pointer_data; diff --git a/chromium/ui/events/mojo/event_struct_traits.cc b/chromium/ui/events/mojo/event_struct_traits.cc index d2a66cb7837..0b4e196f3a3 100644 --- a/chromium/ui/events/mojo/event_struct_traits.cc +++ b/chromium/ui/events/mojo/event_struct_traits.cc @@ -5,7 +5,6 @@ #include "ui/events/mojo/event_struct_traits.h" #include "ui/events/event.h" -#include "ui/events/event_utils.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/mojo/event_constants.mojom.h" #include "ui/latency/mojo/latency_info_struct_traits.h" @@ -133,9 +132,10 @@ int32_t StructTraits<ui::mojom::EventDataView, EventUniquePtr>::flags( return event->flags(); } -int64_t StructTraits<ui::mojom::EventDataView, EventUniquePtr>::time_stamp( +base::TimeTicks +StructTraits<ui::mojom::EventDataView, EventUniquePtr>::time_stamp( const EventUniquePtr& event) { - return event->time_stamp().ToInternalValue(); + return event->time_stamp(); } const ui::LatencyInfo& @@ -242,6 +242,10 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( EventUniquePtr* out) { DCHECK(!out->get()); + base::TimeTicks time_stamp; + if (!event.ReadTimeStamp(&time_stamp)) + return false; + switch (event.action()) { case ui::mojom::EventType::KEY_PRESSED: case ui::mojom::EventType::KEY_RELEASED: { @@ -250,18 +254,17 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( return false; if (key_data->is_char) { - out->reset(new ui::KeyEvent( - static_cast<base::char16>(key_data->character), - static_cast<ui::KeyboardCode>(key_data->key_code), event.flags(), - base::TimeTicks::FromInternalValue(event.time_stamp()))); - + out->reset( + new ui::KeyEvent(static_cast<base::char16>(key_data->character), + static_cast<ui::KeyboardCode>(key_data->key_code), + event.flags(), time_stamp)); } else { - out->reset(new ui::KeyEvent( - event.action() == ui::mojom::EventType::KEY_PRESSED - ? ui::ET_KEY_PRESSED - : ui::ET_KEY_RELEASED, - static_cast<ui::KeyboardCode>(key_data->key_code), event.flags(), - base::TimeTicks::FromInternalValue(event.time_stamp()))); + out->reset( + new ui::KeyEvent(event.action() == ui::mojom::EventType::KEY_PRESSED + ? ui::ET_KEY_PRESSED + : ui::ET_KEY_RELEASED, + static_cast<ui::KeyboardCode>(key_data->key_code), + event.flags(), time_stamp)); } if (key_data->properties) (*out)->AsKeyEvent()->SetProperties(*key_data->properties); @@ -298,7 +301,7 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( ui::MouseEvent::kMousePointerId) : ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, ui::MouseEvent::kMousePointerId), - ui::EventTimeForNow())); + time_stamp)); break; } case ui::mojom::PointerKind::TOUCH: { @@ -313,7 +316,7 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( pointer_data->brush_data->pressure, pointer_data->brush_data->tilt_x, pointer_data->brush_data->tilt_y), - ui::EventTimeForNow())); + time_stamp)); break; } case ui::mojom::PointerKind::PEN: diff --git a/chromium/ui/events/mojo/event_struct_traits.h b/chromium/ui/events/mojo/event_struct_traits.h index f8d0e0ebafd..247d4753143 100644 --- a/chromium/ui/events/mojo/event_struct_traits.h +++ b/chromium/ui/events/mojo/event_struct_traits.h @@ -20,7 +20,7 @@ template <> struct StructTraits<ui::mojom::EventDataView, EventUniquePtr> { static ui::mojom::EventType action(const EventUniquePtr& event); static int32_t flags(const EventUniquePtr& event); - static int64_t time_stamp(const EventUniquePtr& event); + static base::TimeTicks time_stamp(const EventUniquePtr& event); static const ui::LatencyInfo& latency(const EventUniquePtr& event); static ui::mojom::KeyDataPtr key_data(const EventUniquePtr& event); static ui::mojom::PointerDataPtr pointer_data(const EventUniquePtr& event); diff --git a/chromium/ui/events/mojo/struct_traits_unittest.cc b/chromium/ui/events/mojo/struct_traits_unittest.cc index 4f7c4a90a1d..154f603ec58 100644 --- a/chromium/ui/events/mojo/struct_traits_unittest.cc +++ b/chromium/ui/events/mojo/struct_traits_unittest.cc @@ -56,8 +56,9 @@ TEST_F(StructTraitsTest, KeyEvent) { {'Z', VKEY_Z, EF_CAPS_LOCK_ON}, {'z', VKEY_Z, EF_NONE}, {ET_KEY_PRESSED, VKEY_Z, EF_NONE, - base::TimeTicks::FromInternalValue(101)}, - {'Z', VKEY_Z, EF_NONE, base::TimeTicks::FromInternalValue(102)}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(101)}, + {'Z', VKEY_Z, EF_NONE, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(102)}, }; mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); @@ -88,22 +89,22 @@ TEST_F(StructTraitsTest, PointerEvent) { {ET_POINTER_DOWN, gfx::Point(10, 10), gfx::Point(20, 30), EF_NONE, 0, PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, MouseEvent::kMousePointerId), - base::TimeTicks()}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(201)}, {ET_POINTER_MOVED, gfx::Point(1, 5), gfx::Point(5, 1), EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON, PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, MouseEvent::kMousePointerId), - base::TimeTicks()}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(202)}, {ET_POINTER_UP, gfx::Point(411, 130), gfx::Point(20, 30), EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON, PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, MouseEvent::kMousePointerId), - base::TimeTicks()}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(203)}, {ET_POINTER_EXITED, gfx::Point(10, 10), gfx::Point(20, 30), EF_BACK_MOUSE_BUTTON, 0, PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, MouseEvent::kMousePointerId), - base::TimeTicks()}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(204)}, // Touch pointer events: {ET_POINTER_DOWN, gfx::Point(10, 10), gfx::Point(20, 30), EF_NONE, 0, @@ -114,7 +115,7 @@ TEST_F(StructTraitsTest, PointerEvent) { /* force */ 3.0f, /* tilt_x */ 4.0f, /* tilt_y */ 5.0f), - base::TimeTicks()}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(205)}, {ET_POINTER_CANCELLED, gfx::Point(120, 120), gfx::Point(2, 3), EF_NONE, 0, PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 2, @@ -123,7 +124,7 @@ TEST_F(StructTraitsTest, PointerEvent) { /* force */ 3.5f, /* tilt_x */ 2.5f, /* tilt_y */ 0.5f), - base::TimeTicks()}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(206)}, }; mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); @@ -143,18 +144,22 @@ TEST_F(StructTraitsTest, PointerEvent) { output_ptr_event->changed_button_flags()); EXPECT_EQ(kTestData[i].pointer_details(), output_ptr_event->pointer_details()); + EXPECT_EQ(kTestData[i].time_stamp(), output_ptr_event->time_stamp()); } } TEST_F(StructTraitsTest, PointerWheelEvent) { MouseWheelEvent kTestData[] = { {gfx::Vector2d(11, 15), gfx::Point(3, 4), gfx::Point(40, 30), - base::TimeTicks(), EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(301), + EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON}, {gfx::Vector2d(-5, 3), gfx::Point(40, 3), gfx::Point(4, 0), - base::TimeTicks(), EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(302), + EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON}, {gfx::Vector2d(1, 0), gfx::Point(3, 4), gfx::Point(40, 30), - base::TimeTicks(), EF_NONE, EF_NONE}, + base::TimeTicks() + base::TimeDelta::FromMicroseconds(303), EF_NONE, + EF_NONE}, }; mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); @@ -171,6 +176,7 @@ TEST_F(StructTraitsTest, PointerWheelEvent) { output_pointer_event->root_location()); EXPECT_EQ(kTestData[i].offset(), output_pointer_event->pointer_details().offset); + EXPECT_EQ(kTestData[i].time_stamp(), output_pointer_event->time_stamp()); } } diff --git a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc index 6d5aeb6c90c..9aa24612043 100644 --- a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc @@ -224,12 +224,6 @@ void EventFactoryEvdev::DispatchMouseMoveEvent( gfx::PointF location = params.location; PointerDetails details = params.pointer_details; - if (params.flags & EF_DIRECT_INPUT) { - details = GetTransformedEventPointerDetails(params); - cursor_->MoveCursorTo(GetTransformedEventLocation(params)); - location = cursor_->GetLocation(); - } - MouseEvent event(ui::ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), params.timestamp, modifiers_.GetModifierFlags() | params.flags, @@ -248,12 +242,6 @@ void EventFactoryEvdev::DispatchMouseButtonEvent( gfx::PointF location = params.location; PointerDetails details = params.pointer_details; - if (params.flags & EF_DIRECT_INPUT) { - details = GetTransformedEventPointerDetails(params); - cursor_->MoveCursorTo(GetTransformedEventLocation(params)); - location = cursor_->GetLocation(); - } - // Mouse buttons can be remapped, touchpad taps & clicks cannot. unsigned int button = params.button; if (params.allow_remap) diff --git a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc index aac3e2a02a5..d31d107e45d 100644 --- a/chromium/ui/events/ozone/evdev/input_controller_evdev.cc +++ b/chromium/ui/events/ozone/evdev/input_controller_evdev.cc @@ -154,6 +154,11 @@ void InputControllerEvdev::SetPrimaryButtonRight(bool right) { button_map_->UpdateButtonMap(BTN_RIGHT, right ? BTN_LEFT : BTN_RIGHT); } +void InputControllerEvdev::SetMouseReverseScroll(bool enabled) { + input_device_settings_.mouse_reverse_scroll_enabled = enabled; + ScheduleUpdateDeviceSettings(); +} + void InputControllerEvdev::SetTapToClickPaused(bool state) { input_device_settings_.tap_to_click_paused = state; ScheduleUpdateDeviceSettings(); diff --git a/chromium/ui/events/ozone/evdev/input_controller_evdev.h b/chromium/ui/events/ozone/evdev/input_controller_evdev.h index 6d1adcf6282..947e84cbed0 100644 --- a/chromium/ui/events/ozone/evdev/input_controller_evdev.h +++ b/chromium/ui/events/ozone/evdev/input_controller_evdev.h @@ -57,6 +57,7 @@ class EVENTS_OZONE_EVDEV_EXPORT InputControllerEvdev : public InputController { void SetNaturalScroll(bool enabled) override; void SetMouseSensitivity(int value) override; void SetPrimaryButtonRight(bool right) override; + void SetMouseReverseScroll(bool enabled) override; void SetTapToClickPaused(bool state) override; void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) override; void GetTouchEventLog(const base::FilePath& out_dir, 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 ab3ec281a36..895c76615c7 100644 --- a/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/input_device_factory_evdev.cc @@ -312,6 +312,9 @@ void InputDeviceFactoryEvdev::ApplyInputDeviceSettings() { input_device_settings_.mouse_sensitivity); SetIntPropertyForOneType(DT_MOUSE, "Scroll Sensitivity", input_device_settings_.mouse_sensitivity); + SetBoolPropertyForOneType( + DT_MOUSE, "Mouse Reverse Scrolling", + input_device_settings_.mouse_reverse_scroll_enabled); SetBoolPropertyForOneType(DT_TOUCHPAD, "Tap Paused", input_device_settings_.tap_to_click_paused); diff --git a/chromium/ui/events/ozone/evdev/input_device_settings_evdev.h b/chromium/ui/events/ozone/evdev/input_device_settings_evdev.h index 1be7637e799..8b3c9a07c60 100644 --- a/chromium/ui/events/ozone/evdev/input_device_settings_evdev.h +++ b/chromium/ui/events/ozone/evdev/input_device_settings_evdev.h @@ -26,6 +26,7 @@ struct InputDeviceSettingsEvdev { bool natural_scroll_enabled = false; bool tap_to_click_paused = false; bool touch_event_logging_enabled = true; + bool mouse_reverse_scroll_enabled = false; int touchpad_sensitivity = kDefaultSensitivity; int mouse_sensitivity = kDefaultSensitivity; diff --git a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc index 7b4e229e785..4deb82017c5 100644 --- a/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc +++ b/chromium/ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc @@ -37,6 +37,11 @@ namespace { const char kTestDevicePath[] = "/dev/input/test-device"; +// Returns a fake TimeTicks based on the given microsecond offset. +base::TimeTicks ToTestTimeTicks(int64_t micros) { + return base::TimeTicks() + base::TimeDelta::FromMicroseconds(micros); +} + void InitPixelTouchscreen(TouchEventConverterEvdev* device) { EventDeviceInfo devinfo; EXPECT_TRUE(CapabilitiesToDeviceInfo(kLinkTouchscreen, &devinfo)); @@ -330,8 +335,7 @@ TEST_F(TouchEventConverterEvdevTest, TouchMove) { EXPECT_EQ(1u, size()); ui::TouchEventParams event = dispatched_touch_event(0); EXPECT_EQ(ui::ET_TOUCH_PRESSED, event.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(1427323282019203), - event.timestamp); + EXPECT_EQ(ToTestTimeTicks(1427323282019203), event.timestamp); EXPECT_EQ(295, event.location.x()); EXPECT_EQ(421, event.location.y()); EXPECT_EQ(0, event.slot); @@ -347,8 +351,7 @@ TEST_F(TouchEventConverterEvdevTest, TouchMove) { EXPECT_EQ(2u, size()); event = dispatched_touch_event(1); EXPECT_EQ(ui::ET_TOUCH_MOVED, event.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(1427323282034693), - event.timestamp); + EXPECT_EQ(ToTestTimeTicks(1427323282034693), event.timestamp); EXPECT_EQ(312, event.location.x()); EXPECT_EQ(432, event.location.y()); EXPECT_EQ(0, event.slot); @@ -364,8 +367,7 @@ TEST_F(TouchEventConverterEvdevTest, TouchMove) { EXPECT_EQ(3u, size()); event = dispatched_touch_event(2); EXPECT_EQ(ui::ET_TOUCH_RELEASED, event.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(1427323282144540), - event.timestamp); + EXPECT_EQ(ToTestTimeTicks(1427323282144540), event.timestamp); EXPECT_EQ(312, event.location.x()); EXPECT_EQ(432, event.location.y()); EXPECT_EQ(0, event.slot); @@ -417,7 +419,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { // Move EXPECT_EQ(ui::ET_TOUCH_MOVED, ev0.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(0), ev0.timestamp); + EXPECT_EQ(ToTestTimeTicks(0), ev0.timestamp); EXPECT_EQ(40, ev0.location.x()); EXPECT_EQ(51, ev0.location.y()); EXPECT_EQ(0, ev0.slot); @@ -425,7 +427,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { // Press EXPECT_EQ(ui::ET_TOUCH_PRESSED, ev1.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(0), ev1.timestamp); + EXPECT_EQ(ToTestTimeTicks(0), ev1.timestamp); EXPECT_EQ(101, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); @@ -441,7 +443,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { ev1 = dispatched_touch_event(4); EXPECT_EQ(ui::ET_TOUCH_MOVED, ev1.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(0), ev1.timestamp); + EXPECT_EQ(ToTestTimeTicks(0), ev1.timestamp); EXPECT_EQ(40, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); @@ -458,7 +460,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { ev0 = dispatched_touch_event(5); EXPECT_EQ(ui::ET_TOUCH_MOVED, ev0.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(0), ev0.timestamp); + EXPECT_EQ(ToTestTimeTicks(0), ev0.timestamp); EXPECT_EQ(39, ev0.location.x()); EXPECT_EQ(51, ev0.location.y()); EXPECT_EQ(0, ev0.slot); @@ -476,14 +478,14 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { ev1 = dispatched_touch_event(7); EXPECT_EQ(ui::ET_TOUCH_RELEASED, ev0.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(0), ev0.timestamp); + EXPECT_EQ(ToTestTimeTicks(0), ev0.timestamp); EXPECT_EQ(39, ev0.location.x()); EXPECT_EQ(51, ev0.location.y()); EXPECT_EQ(0, ev0.slot); EXPECT_FLOAT_EQ(0.17647059f, ev0.pointer_details.force); EXPECT_EQ(ui::ET_TOUCH_MOVED, ev1.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(0), ev1.timestamp); + EXPECT_EQ(ToTestTimeTicks(0), ev1.timestamp); EXPECT_EQ(38, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); @@ -499,7 +501,7 @@ TEST_F(TouchEventConverterEvdevTest, TwoFingerGesture) { ev1 = dispatched_touch_event(8); EXPECT_EQ(ui::ET_TOUCH_RELEASED, ev1.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(0), ev1.timestamp); + EXPECT_EQ(ToTestTimeTicks(0), ev1.timestamp); EXPECT_EQ(38, ev1.location.x()); EXPECT_EQ(102, ev1.location.y()); EXPECT_EQ(1, ev1.slot); @@ -844,8 +846,7 @@ TEST_F(TouchEventConverterEvdevTest, ShouldUseLeftButtonIfNoTouchButton) { EXPECT_EQ(1u, size()); ui::TouchEventParams event = dispatched_touch_event(0); EXPECT_EQ(ui::ET_TOUCH_PRESSED, event.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(1433965490837958), - event.timestamp); + EXPECT_EQ(ToTestTimeTicks(1433965490837958), event.timestamp); EXPECT_EQ(3654, event.location.x()); EXPECT_EQ(1055, event.location.y()); EXPECT_EQ(0, event.slot); @@ -859,8 +860,7 @@ TEST_F(TouchEventConverterEvdevTest, ShouldUseLeftButtonIfNoTouchButton) { EXPECT_EQ(2u, size()); event = dispatched_touch_event(1); EXPECT_EQ(ui::ET_TOUCH_MOVED, event.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(1433965491001953), - event.timestamp); + EXPECT_EQ(ToTestTimeTicks(1433965491001953), event.timestamp); EXPECT_EQ(3644, event.location.x()); EXPECT_EQ(1059, event.location.y()); EXPECT_EQ(0, event.slot); @@ -874,8 +874,7 @@ TEST_F(TouchEventConverterEvdevTest, ShouldUseLeftButtonIfNoTouchButton) { EXPECT_EQ(3u, size()); event = dispatched_touch_event(2); EXPECT_EQ(ui::ET_TOUCH_RELEASED, event.type); - EXPECT_EQ(base::TimeTicks::FromInternalValue(1433965491225959), - event.timestamp); + EXPECT_EQ(ToTestTimeTicks(1433965491225959), event.timestamp); EXPECT_EQ(3644, event.location.x()); EXPECT_EQ(1059, event.location.y()); EXPECT_EQ(0, event.slot); diff --git a/chromium/ui/events/ozone/evdev/touch_filter/horizontally_aligned_touch_noise_filter.cc b/chromium/ui/events/ozone/evdev/touch_filter/horizontally_aligned_touch_noise_filter.cc index 446bc08a208..29dd7a8ed31 100644 --- a/chromium/ui/events/ozone/evdev/touch_filter/horizontally_aligned_touch_noise_filter.cc +++ b/chromium/ui/events/ozone/evdev/touch_filter/horizontally_aligned_touch_noise_filter.cc @@ -41,12 +41,12 @@ void HorizontallyAlignedTouchNoiseFilter::Filter( "Ozone.TouchNoiseFilter.HorizontallyAlignedDistance", distance); if (distance <= kMaxDistance) { - VLOG(2) << base::StringPrintf("Cancel tracking id %d, down at %" PRId64 - " at %f,%f near touch %d at " - "%f,%f", - touch.tracking_id, time.ToInternalValue(), - touch.x, touch.y, other_touch.tracking_id, - other_touch.x, other_touch.y); + VLOG(2) << base::StringPrintf( + "Cancel tracking id %d, down at %" PRId64 + " at %f,%f near touch %d at " + "%f,%f", + touch.tracking_id, time.since_origin().InMicroseconds(), touch.x, + touch.y, other_touch.tracking_id, other_touch.x, other_touch.y); slots_with_noise->set(touch.slot); } } diff --git a/chromium/ui/events/x/events_x_unittest.cc b/chromium/ui/events/x/events_x_unittest.cc index a7082e60259..fdd4fc2899f 100644 --- a/chromium/ui/events/x/events_x_unittest.cc +++ b/chromium/ui/events/x/events_x_unittest.cc @@ -211,7 +211,7 @@ TEST_F(EventsXTest, ClickCount) { XEvent event; gfx::Point location(5, 10); - base::TimeDelta time_stamp = base::TimeTicks::Now() - base::TimeTicks() - + base::TimeDelta time_stamp = base::TimeTicks::Now().since_origin() - base::TimeDelta::FromMilliseconds(10); for (int i = 1; i <= 3; ++i) { InitButtonEvent(&event, true, location, 1, 0); @@ -540,15 +540,22 @@ TEST_F(EventsXTest, IgnoresMotionEventForMouseWheelScroll) { } namespace { + +// Returns a fake TimeTicks based on the given millisecond offset. +base::TimeTicks TimeTicksFromMillis(int64_t millis) { + return base::TimeTicks() + base::TimeDelta::FromMilliseconds(millis); +} + class MockTickClock : public base::TickClock { public: - explicit MockTickClock(uint64_t milliseconds) - : ticks_(base::TimeTicks::FromInternalValue(milliseconds * 1000)) {} + explicit MockTickClock(int64_t milliseconds) + : ticks_(TimeTicksFromMillis(milliseconds)) {} base::TimeTicks NowTicks() override { return ticks_; } private: base::TimeTicks ticks_; }; + } // namespace TEST_F(EventsXTest, TimestampRolloverAndAdjustWhenDecreasing) { @@ -556,19 +563,17 @@ TEST_F(EventsXTest, TimestampRolloverAndAdjustWhenDecreasing) { InitButtonEvent(&event, true, gfx::Point(5, 10), 1, 0); ResetTimestampRolloverCountersForTesting( - base::MakeUnique<MockTickClock>(0x100000001LL)); + base::MakeUnique<MockTickClock>(0x100000001)); event.xbutton.time = 0xFFFFFFFF; - EXPECT_EQ(base::TimeDelta::FromMilliseconds(0xFFFFFFFF).ToInternalValue(), - ui::EventTimeFromNative(&event).ToInternalValue()); + EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromNative(&event)); ResetTimestampRolloverCountersForTesting( - base::MakeUnique<MockTickClock>(0x100000007LL)); + base::MakeUnique<MockTickClock>(0x100000007)); event.xbutton.time = 3; - EXPECT_EQ( - base::TimeDelta::FromMilliseconds(0x100000000LL + 3).ToInternalValue(), - ui::EventTimeFromNative(&event).ToInternalValue()); + EXPECT_EQ(TimeTicksFromMillis(0x100000000 + 3), + ui::EventTimeFromNative(&event)); } TEST_F(EventsXTest, NoTimestampRolloverWhenMonotonicIncreasing) { @@ -578,18 +583,15 @@ TEST_F(EventsXTest, NoTimestampRolloverWhenMonotonicIncreasing) { ResetTimestampRolloverCountersForTesting(base::MakeUnique<MockTickClock>(10)); event.xbutton.time = 6; - EXPECT_EQ(base::TimeDelta::FromMilliseconds(6).ToInternalValue(), - ui::EventTimeFromNative(&event).ToInternalValue()); + EXPECT_EQ(TimeTicksFromMillis(6), ui::EventTimeFromNative(&event)); event.xbutton.time = 7; - EXPECT_EQ(base::TimeDelta::FromMilliseconds(7).ToInternalValue(), - ui::EventTimeFromNative(&event).ToInternalValue()); + EXPECT_EQ(TimeTicksFromMillis(7), ui::EventTimeFromNative(&event)); ResetTimestampRolloverCountersForTesting( base::MakeUnique<MockTickClock>(0x100000005)); event.xbutton.time = 0xFFFFFFFF; - EXPECT_EQ(base::TimeDelta::FromMilliseconds(0xFFFFFFFF).ToInternalValue(), - ui::EventTimeFromNative(&event).ToInternalValue()); + EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromNative(&event)); } } // namespace ui diff --git a/chromium/ui/gfx/BUILD.gn b/chromium/ui/gfx/BUILD.gn index e579efd759a..141803adf75 100644 --- a/chromium/ui/gfx/BUILD.gn +++ b/chromium/ui/gfx/BUILD.gn @@ -44,8 +44,6 @@ component("geometry_skia") { component("gfx") { sources = [ - "android/gfx_jni_registrar.cc", - "android/gfx_jni_registrar.h", "android/java_bitmap.cc", "android/java_bitmap.h", "android/view_configuration.cc", @@ -94,6 +92,7 @@ component("gfx") { "image/image_family.h", "image/image_ios.mm", "image/image_mac.mm", + "image/image_platform.h", "image/image_png_rep.cc", "image/image_png_rep.h", "image/image_skia.cc", @@ -206,6 +205,7 @@ component("gfx") { "canvas_skia_paint.h", "image/canvas_image_source.cc", "image/canvas_image_source.h", + "image/image_generic.cc", "image/image_skia_operations.cc", "image/image_skia_operations.h", "paint_throbber.cc", @@ -256,7 +256,7 @@ component("gfx") { ] # Text rendering conditions (complicated so separated out). - if (use_aura || is_mac || (is_android && enable_vr)) { + if (use_aura || is_mac || (is_android && enable_vr) || is_fuchsia) { # Mac doesn't use RenderTextHarfBuzz by default yet. sources += [ "harfbuzz_font_skia.cc", @@ -330,8 +330,7 @@ component("gfx") { sources -= [ "canvas_notimplemented.cc" ] } - # Desktop and Android+VR only. - if (use_aura || (!is_ios && !is_android) || (is_android && enable_vr)) { + if (!is_ios) { sources += [ "paint_vector_icon.cc", "paint_vector_icon.h", @@ -388,6 +387,14 @@ component("gfx") { if (use_cairo) { configs += [ "//build/config/linux/pangocairo" ] } + + if (is_fuchsia) { + sources += [ + "font_fallback_fuchsia.cc", + "font_render_params_fuchsia.cc", + "platform_font_fuchsia.cc", + ] + } } component("color_space") { @@ -483,6 +490,13 @@ source_set("selection_bound_sources") { ] } +# Depend on this to use buffer_types.h without pulling in all of gfx. +source_set("buffer_types") { + sources = [ + "buffer_types.h", + ] +} + # The GPU memory buffer stuff is separate from "gfx" to allow GPU-related # things to use these files without pulling in all of gfx, which includes large # things like Skia. @@ -510,7 +524,6 @@ source_set("memory_buffer_sources") { sources = [ "buffer_format_util.cc", "buffer_format_util.h", - "buffer_types.h", "client_native_pixmap.h", "client_native_pixmap_factory.cc", "client_native_pixmap_factory.h", @@ -529,13 +542,15 @@ source_set("memory_buffer_sources") { sources += [ "gpu_memory_buffer.cc", "gpu_memory_buffer.h", - "gpu_memory_buffer_tracing.cc", - "gpu_memory_buffer_tracing.h", ] } defines = [ "GFX_IMPLEMENTATION" ] + public_deps = [ + ":buffer_types", + ] + deps = [ ":gfx_switches", ":native_widget_types", @@ -702,11 +717,12 @@ test("gfx_unittests") { ] } + if (!is_ios) { + sources += [ "paint_vector_icon_unittest.cc" ] + } + if (!is_android && !is_ios) { - sources += [ - "paint_vector_icon_unittest.cc", - "render_text_unittest.cc", - ] + sources += [ "render_text_unittest.cc" ] } # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. diff --git a/chromium/ui/gfx/DEPS b/chromium/ui/gfx/DEPS index a6891ad405f..808d12b3ae0 100644 --- a/chromium/ui/gfx/DEPS +++ b/chromium/ui/gfx/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+base", + "+cc/base", "+cc/paint", "+device/vr/features/features.h", "+skia/ext", diff --git a/chromium/ui/gfx/android/view_configuration.cc b/chromium/ui/gfx/android/view_configuration.cc index a79f0f92aa3..11fe458ccab 100644 --- a/chromium/ui/gfx/android/view_configuration.cc +++ b/chromium/ui/gfx/android/view_configuration.cc @@ -175,8 +175,4 @@ int ViewConfiguration::GetMinScalingSpanInDips() { return g_view_configuration.Get().min_scaling_span_in_dips(); } -bool ViewConfiguration::RegisterViewConfiguration(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace gfx diff --git a/chromium/ui/gfx/android/view_configuration.h b/chromium/ui/gfx/android/view_configuration.h index 4495a5eccc6..e7353de3d07 100644 --- a/chromium/ui/gfx/android/view_configuration.h +++ b/chromium/ui/gfx/android/view_configuration.h @@ -26,9 +26,6 @@ class GFX_EXPORT ViewConfiguration { static int GetDoubleTapSlopInDips(); static int GetMinScalingSpanInDips(); - - // Registers methods with JNI and returns true if succeeded. - static bool RegisterViewConfiguration(JNIEnv* env); }; } // namespace gfx diff --git a/chromium/ui/gfx/animation/animation_container.cc b/chromium/ui/gfx/animation/animation_container.cc index e06803d06e1..ef15c8f64c2 100644 --- a/chromium/ui/gfx/animation/animation_container.cc +++ b/chromium/ui/gfx/animation/animation_container.cc @@ -13,8 +13,9 @@ using base::TimeTicks; namespace gfx { AnimationContainer::AnimationContainer() - : last_tick_time_(base::TimeTicks::Now()), observer_(NULL) { -} + : last_tick_time_(base::TimeTicks::Now()), + min_timer_interval_count_(0), + observer_(NULL) {} AnimationContainer::~AnimationContainer() { // The animations own us and stop themselves before being deleted. If @@ -29,8 +30,12 @@ void AnimationContainer::Start(AnimationContainerElement* element) { if (elements_.empty()) { last_tick_time_ = base::TimeTicks::Now(); SetMinTimerInterval(element->GetTimerInterval()); + min_timer_interval_count_ = 1; } else if (element->GetTimerInterval() < min_timer_interval_) { SetMinTimerInterval(element->GetTimerInterval()); + min_timer_interval_count_ = 1; + } else if (element->GetTimerInterval() == min_timer_interval_) { + min_timer_interval_count_++; } element->SetStartTime(last_tick_time_); @@ -40,16 +45,27 @@ void AnimationContainer::Start(AnimationContainerElement* element) { void AnimationContainer::Stop(AnimationContainerElement* element) { DCHECK(elements_.count(element) > 0); // The element must be running. + base::TimeDelta interval = element->GetTimerInterval(); elements_.erase(element); if (elements_.empty()) { timer_.Stop(); + min_timer_interval_count_ = 0; if (observer_) observer_->AnimationContainerEmpty(this); - } else { - TimeDelta min_timer_interval = GetMinInterval(); - if (min_timer_interval > min_timer_interval_) - SetMinTimerInterval(min_timer_interval); + } else if (interval == min_timer_interval_) { + min_timer_interval_count_--; + + // If the last element at the current (minimum) timer interval has been + // removed then go find the new minimum and the number of elements at that + // same minimum. + if (min_timer_interval_count_ == 0) { + std::pair<base::TimeDelta, size_t> interval_count = + GetMinIntervalAndCount(); + DCHECK(interval_count.first > min_timer_interval_); + SetMinTimerInterval(interval_count.first); + min_timer_interval_count_ = interval_count.second; + } } } @@ -87,17 +103,30 @@ void AnimationContainer::SetMinTimerInterval(base::TimeDelta delta) { timer_.Start(FROM_HERE, min_timer_interval_, this, &AnimationContainer::Run); } -TimeDelta AnimationContainer::GetMinInterval() { +std::pair<TimeDelta, size_t> AnimationContainer::GetMinIntervalAndCount() + const { DCHECK(!elements_.empty()); + // Find the minimum interval and the number of elements sharing that same + // interval. It is tempting to create a map of intervals -> counts in order to + // make this O(log n) instead of O(n). However, profiling shows that this + // offers no practical performance gain (the most common case is that all + // elements in the set share the same interval). TimeDelta min; + size_t count = 1; Elements::const_iterator i = elements_.begin(); min = (*i)->GetTimerInterval(); for (++i; i != elements_.end(); ++i) { - if ((*i)->GetTimerInterval() < min) - min = (*i)->GetTimerInterval(); + auto interval = (*i)->GetTimerInterval(); + if (interval < min) { + min = interval; + count = 1; + } else if (interval == min) { + count++; + } } - return min; + + return std::make_pair(min, count); } } // namespace gfx diff --git a/chromium/ui/gfx/animation/animation_container.h b/chromium/ui/gfx/animation/animation_container.h index a19c0539d2f..58ad1ea1d01 100644 --- a/chromium/ui/gfx/animation/animation_container.h +++ b/chromium/ui/gfx/animation/animation_container.h @@ -5,8 +5,9 @@ #ifndef UI_GFX_ANIMATION_ANIMATION_CONTAINER_H_ #define UI_GFX_ANIMATION_ANIMATION_CONTAINER_H_ -#include <set> +#include <utility> +#include "base/containers/flat_set.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" @@ -55,7 +56,13 @@ class ANIMATION_EXPORT AnimationContainer private: friend class base::RefCounted<AnimationContainer>; - typedef std::set<AnimationContainerElement*> Elements; + // This set is usually quite small so a flat_set is the most obvious choice. + // However, in extreme cases this can grow to 100s or even 1000s of elements. + // Since this set is duplicated on every call to 'Run' and indexed very + // frequently the cache locality of the vector is more important than the + // costlier (but rarer) insertion. Profiling shows that flat_set continues to + // perform best in these cases (up to 12x faster than std::set). + typedef base::flat_set<AnimationContainerElement*> Elements; ~AnimationContainer(); @@ -65,8 +72,9 @@ class ANIMATION_EXPORT AnimationContainer // Sets min_timer_interval_ and restarts the timer. void SetMinTimerInterval(base::TimeDelta delta); - // Returns the min timer interval of all the timers. - base::TimeDelta GetMinInterval(); + // Returns the min timer interval of all the timers, and the count of timers + // at that interval. + std::pair<base::TimeDelta, size_t> GetMinIntervalAndCount() const; // Represents one of two possible values: // . If only a single animation has been started and the timer hasn't yet @@ -77,8 +85,14 @@ class ANIMATION_EXPORT AnimationContainer // Set of elements (animations) being managed. Elements elements_; - // Minimum interval the timers run at. + // Minimum interval the timers run at, plus the number of timers that have + // been seen at that interval. The most common case is for all of the + // animations to run at 60Hz, in which case all of the intervals are the same. + // This acts as a cache of size 1, and when an animation stops and is removed + // it means that the linear scan for the new minimum timer can almost always + // be avoided. base::TimeDelta min_timer_interval_; + size_t min_timer_interval_count_; base::RepeatingTimer timer_; diff --git a/chromium/ui/gfx/animation/animation_unittest.cc b/chromium/ui/gfx/animation/animation_unittest.cc index 66e9d98e085..bf1cf0138f1 100644 --- a/chromium/ui/gfx/animation/animation_unittest.cc +++ b/chromium/ui/gfx/animation/animation_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/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" #include "build/build_config.h" @@ -79,7 +78,7 @@ class DeletingAnimationDelegate : public AnimationDelegate { public: void AnimationEnded(const Animation* animation) override { delete animation; - base::MessageLoop::current()->QuitWhenIdle(); + base::RunLoop::QuitCurrentWhenIdleDeprecated(); } }; diff --git a/chromium/ui/gfx/animation/linear_animation.cc b/chromium/ui/gfx/animation/linear_animation.cc index 2886a87aad5..7181214d0fe 100644 --- a/chromium/ui/gfx/animation/linear_animation.cc +++ b/chromium/ui/gfx/animation/linear_animation.cc @@ -21,6 +21,8 @@ static base::TimeDelta CalculateInterval(int frame_rate) { return base::TimeDelta::FromMicroseconds(timer_interval); } +const int LinearAnimation::kDefaultFrameRate = 60; + LinearAnimation::LinearAnimation(AnimationDelegate* delegate, int frame_rate) : LinearAnimation({}, frame_rate, delegate) {} diff --git a/chromium/ui/gfx/animation/linear_animation.h b/chromium/ui/gfx/animation/linear_animation.h index 9f3654d442a..d1393da0fd0 100644 --- a/chromium/ui/gfx/animation/linear_animation.h +++ b/chromium/ui/gfx/animation/linear_animation.h @@ -18,7 +18,7 @@ class AnimationDelegate; class ANIMATION_EXPORT LinearAnimation : public Animation { public: // Default frame rate (hz). - static const int kDefaultFrameRate = 60; + static const int kDefaultFrameRate; // Initializes everything except the duration. // diff --git a/chromium/ui/gfx/animation/test_animation_delegate.h b/chromium/ui/gfx/animation/test_animation_delegate.h index 6cc65afbc7e..3c40a8993e3 100644 --- a/chromium/ui/gfx/animation/test_animation_delegate.h +++ b/chromium/ui/gfx/animation/test_animation_delegate.h @@ -6,7 +6,7 @@ #define UI_GFX_ANIMATION_TEST_ANIMATION_DELEGATE_H_ #include "base/macros.h" -#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "ui/gfx/animation/animation_delegate.h" namespace gfx { @@ -20,13 +20,13 @@ class TestAnimationDelegate : public AnimationDelegate { virtual void AnimationEnded(const Animation* animation) { finished_ = true; - base::MessageLoop::current()->QuitWhenIdle(); + base::RunLoop::QuitCurrentWhenIdleDeprecated(); } virtual void AnimationCanceled(const Animation* animation) { finished_ = true; canceled_ = true; - base::MessageLoop::current()->QuitWhenIdle(); + base::RunLoop::QuitCurrentWhenIdleDeprecated(); } bool finished() const { diff --git a/chromium/ui/gfx/animation/tween.cc b/chromium/ui/gfx/animation/tween.cc index 1cebe30c4ae..1fced2c368a 100644 --- a/chromium/ui/gfx/animation/tween.cc +++ b/chromium/ui/gfx/animation/tween.cc @@ -98,8 +98,8 @@ uint8_t BlendColorComponents(uint8_t start, } double TimeDeltaDivide(base::TimeDelta dividend, base::TimeDelta divisor) { - return static_cast<double>(dividend.ToInternalValue()) / - static_cast<double>(divisor.ToInternalValue()); + return static_cast<double>(dividend.InMicroseconds()) / + static_cast<double>(divisor.InMicroseconds()); } } // namespace diff --git a/chromium/ui/gfx/buffer_types.h b/chromium/ui/gfx/buffer_types.h index 9cb5b7b6ee5..b35f587576f 100644 --- a/chromium/ui/gfx/buffer_types.h +++ b/chromium/ui/gfx/buffer_types.h @@ -37,12 +37,18 @@ enum class BufferFormat { // by the CPU. *_CPU_READ_WRITE_PERSISTENT adds the additional condition that // successive Map() calls (with Unmap() calls between) will return a pointer to // the same memory contents. SCANOUT implies GPU_READ_WRITE. +// *_VDA_WRITE is for cases where a video decode accellerator writes into +// the buffers. + // TODO(reveman): Add GPU_READ_WRITE for use-cases where SCANOUT is not // required. enum class BufferUsage { GPU_READ, SCANOUT, + // SCANOUT_CAMERA_READ_WRITE implies CPU_READ_WRITE. + SCANOUT_CAMERA_READ_WRITE, SCANOUT_CPU_READ_WRITE, + SCANOUT_VDA_WRITE, GPU_READ_CPU_READ_WRITE, // TODO(reveman): Merge this with GPU_READ_CPU_READ_WRITE when SurfaceTexture // backed buffers are single buffered and support it. diff --git a/chromium/ui/gfx/canvas.cc b/chromium/ui/gfx/canvas.cc index 414c2c90e8d..ff5f8a8373c 100644 --- a/chromium/ui/gfx/canvas.cc +++ b/chromium/ui/gfx/canvas.cc @@ -287,15 +287,19 @@ void Canvas::Draw1pxLine(PointF p1, PointF p2, SkColor color) { void Canvas::DrawCircle(const Point& center_point, int radius, const cc::PaintFlags& flags) { - DrawCircle(PointF(center_point), radius, flags); + canvas_->drawOval( + SkRect::MakeLTRB(center_point.x() - radius, center_point.y() - radius, + center_point.x() + radius, center_point.y() + radius), + flags); } void Canvas::DrawCircle(const PointF& center_point, float radius, const cc::PaintFlags& flags) { - canvas_->drawCircle(SkFloatToScalar(center_point.x()), - SkFloatToScalar(center_point.y()), - SkFloatToScalar(radius), flags); + canvas_->drawOval( + SkRect::MakeLTRB(center_point.x() - radius, center_point.y() - radius, + center_point.x() + radius, center_point.y() + radius), + flags); } void Canvas::DrawRoundRect(const Rect& rect, diff --git a/chromium/ui/gfx/color_space.cc b/chromium/ui/gfx/color_space.cc index 96b9306e739..f6352710c37 100644 --- a/chromium/ui/gfx/color_space.cc +++ b/chromium/ui/gfx/color_space.cc @@ -35,6 +35,14 @@ ColorSpace::ColorSpace(PrimaryID primaries, matrix_(matrix), range_(range) {} +ColorSpace::ColorSpace(PrimaryID primaries, + const SkColorSpaceTransferFn& fn, + MatrixID matrix, + RangeID range) + : primaries_(primaries), matrix_(matrix), range_(range) { + SetCustomTransferFunction(fn); +} + ColorSpace::ColorSpace(const ColorSpace& other) : primaries_(other.primaries_), transfer_(other.transfer_), @@ -92,17 +100,22 @@ ColorSpace ColorSpace::CreateCustom(const SkMatrix44& to_XYZD50, result.custom_primary_matrix_[3 * row + col] = to_XYZD50.get(row, col); } } - result.custom_transfer_params_[0] = fn.fA; - result.custom_transfer_params_[1] = fn.fB; - result.custom_transfer_params_[2] = fn.fC; - result.custom_transfer_params_[3] = fn.fD; - result.custom_transfer_params_[4] = fn.fE; - result.custom_transfer_params_[5] = fn.fF; - result.custom_transfer_params_[6] = fn.fG; - // TODO(ccameron): Use enums for near matches to know color spaces. + result.SetCustomTransferFunction(fn); return result; } +void ColorSpace::SetCustomTransferFunction(const SkColorSpaceTransferFn& fn) { + custom_transfer_params_[0] = fn.fA; + custom_transfer_params_[1] = fn.fB; + custom_transfer_params_[2] = fn.fC; + custom_transfer_params_[3] = fn.fD; + custom_transfer_params_[4] = fn.fE; + custom_transfer_params_[5] = fn.fF; + custom_transfer_params_[6] = fn.fG; + // TODO(ccameron): Use enums for near matches to know color spaces. + transfer_ = TransferID::CUSTOM; +} + // static ColorSpace ColorSpace::CreateCustom(const SkMatrix44& to_XYZD50, ColorSpace::TransferID transfer_id) { @@ -154,14 +167,23 @@ bool ColorSpace::operator==(const ColorSpace& other) const { if (primaries_ != other.primaries_ || transfer_ != other.transfer_ || matrix_ != other.matrix_ || range_ != other.range_) return false; - if (primaries_ == PrimaryID::CUSTOM && - memcmp(custom_primary_matrix_, other.custom_primary_matrix_, - sizeof(custom_primary_matrix_))) - return false; - if (transfer_ == TransferID::CUSTOM && - memcmp(custom_transfer_params_, other.custom_transfer_params_, - sizeof(custom_transfer_params_))) - return false; + if (primaries_ == PrimaryID::CUSTOM) { + if (memcmp(custom_primary_matrix_, other.custom_primary_matrix_, + sizeof(custom_primary_matrix_))) { + return false; + } + } + if (transfer_ == TransferID::CUSTOM) { + if (memcmp(custom_transfer_params_, other.custom_transfer_params_, + sizeof(custom_transfer_params_))) { + return false; + } + } + if (primaries_ == PrimaryID::ICC_BASED || + transfer_ == TransferID::ICC_BASED) { + if (icc_profile_id_ != other.icc_profile_id_) + return false; + } return true; } @@ -191,8 +213,12 @@ ColorSpace ColorSpace::GetParametricApproximation() const { // Query the ICC profile, if available, for the parametric approximation. ICCProfile icc_profile; - if (GetICCProfile(&icc_profile)) + if (icc_profile_id_ && GetICCProfile(&icc_profile)) { return icc_profile.GetParametricColorSpace(); + } else { + DLOG(ERROR) + << "Unable to acquire ICC profile for parametric approximation."; + } // Fall back to sRGB if the ICC profile is no longer cached. return CreateSRGB(); @@ -281,8 +307,10 @@ std::string ColorSpace::ToString() const { ss << ", transfer:"; if (transfer_ == TransferID::CUSTOM) { ss << "["; - for (size_t i = 0; i < 7; ++i) + for (size_t i = 0; i < 7; ++i) { ss << custom_transfer_params_[i]; + ss << ","; + } ss << "]"; } else { ss << static_cast<int>(transfer_); @@ -303,6 +331,26 @@ ColorSpace ColorSpace::GetAsFullRangeRGB() const { return result; } +ColorSpace ColorSpace::GetRasterColorSpace() const { + // Rasterization can only be done into parametric color spaces. + if (!IsParametric()) + return GetParametricApproximation(); + // Rasterization doesn't support more than 8 bit unorm values. If the output + // space has an extended range, use Display P3 for the rasterization space, + // to get a somewhat wider color gamut. + if (HasExtendedSkTransferFn()) + return CreateDisplayP3D65(); + return *this; +} + +ColorSpace ColorSpace::GetBlendingColorSpace() const { + // HDR output on windows requires output have a linear transfer function. + // Linear blending breaks the web, so use extended-sRGB for blending. + if (transfer_ == TransferID::LINEAR_HDR) + return CreateExtendedSRGB(); + return *this; +} + sk_sp<SkColorSpace> ColorSpace::ToSkColorSpace() const { // If we got a specific SkColorSpace from the ICCProfile that this color space // was created from, use that. @@ -423,6 +471,47 @@ bool ColorSpace::GetICCProfile(ICCProfile* icc_profile) const { return true; } +bool ColorSpace::GetICCProfileData(std::vector<char>* output_data) const { + if (!IsValid()) { + DLOG(ERROR) << "Cannot fetch ICCProfile for invalid space."; + return false; + } + if (matrix_ != MatrixID::RGB) { + DLOG(ERROR) << "Not creating non-RGB ICCProfile"; + return false; + } + if (range_ != RangeID::FULL) { + DLOG(ERROR) << "Not creating non-full-range ICCProfile"; + return false; + } + + // If this was created from an ICC profile, retrieve that exact profile. + ICCProfile icc_profile; + if (ICCProfile::FromId(icc_profile_id_, &icc_profile)) { + *output_data = icc_profile.data_; + return true; + } + + // Otherwise, construct an ICC profile based on the best approximated + // primaries and matrix. + SkMatrix44 to_XYZD50_matrix; + GetPrimaryMatrix(&to_XYZD50_matrix); + SkColorSpaceTransferFn fn; + if (!GetTransferFunction(&fn)) { + DLOG(ERROR) << "Failed to get ColorSpace transfer function for ICCProfile."; + return false; + } + sk_sp<SkData> data = SkICC::WriteToICC(fn, to_XYZD50_matrix); + if (!data || !data->size()) { + DLOG(ERROR) << "Failed to create SkICC."; + return false; + } + const char* data_as_char = reinterpret_cast<const char*>(data->data()); + output_data->insert(output_data->begin(), data_as_char, + data_as_char + data->size()); + return true; +} + void ColorSpace::GetPrimaryMatrix(SkMatrix44* to_XYZD50) const { SkColorSpacePrimaries primaries = {0}; switch (primaries_) { @@ -621,16 +710,6 @@ bool ColorSpace::GetTransferFunction(SkColorSpaceTransferFn* fn) const { case ColorSpace::TransferID::GAMMA28: fn->fG = 2.8f; return true; - case ColorSpace::TransferID::BT709: - case ColorSpace::TransferID::SMPTE170M: - case ColorSpace::TransferID::BT2020_10: - case ColorSpace::TransferID::BT2020_12: - fn->fA = 0.909672431050f; - fn->fB = 0.090327568950f; - fn->fC = 0.222222222222f; - fn->fD = 0.081242862158f; - fn->fG = 2.222222222222f; - return true; case ColorSpace::TransferID::SMPTE240M: fn->fA = 0.899626676224f; fn->fB = 0.100373323776f; @@ -638,6 +717,19 @@ bool ColorSpace::GetTransferFunction(SkColorSpaceTransferFn* fn) const { fn->fD = 0.091286342118f; fn->fG = 2.222222222222f; return true; + case ColorSpace::TransferID::BT709: + case ColorSpace::TransferID::SMPTE170M: + case ColorSpace::TransferID::BT2020_10: + case ColorSpace::TransferID::BT2020_12: + // With respect to rendering BT709 + // * SMPTE 1886 suggests that we should be using gamma 2.4. + // * Most displays actually use a gamma of 2.2, and most media playing + // software uses the sRGB transfer function. + // * User studies shows that users don't really care. + // * Apple's CoreVideo uses gamma=1.961. + // Bearing all of that in mind, use the same transfer funciton as sRGB, + // which will allow more optimization, and will more closely match other + // media players. case ColorSpace::TransferID::IEC61966_2_1: case ColorSpace::TransferID::IEC61966_2_1_HDR: fn->fA = 0.947867345704f; diff --git a/chromium/ui/gfx/color_space.h b/chromium/ui/gfx/color_space.h index 61574a5fcb8..8a514de26be 100644 --- a/chromium/ui/gfx/color_space.h +++ b/chromium/ui/gfx/color_space.h @@ -7,6 +7,7 @@ #include <stdint.h> #include <ostream> +#include <vector> #include "base/gtest_prod_util.h" #include "base/macros.h" @@ -22,6 +23,7 @@ struct ParamTraits; namespace gfx { class ICCProfile; +class ICCProfileCache; // Used to represet a color space for the purpose of color conversion. // This is designed to be safe and compact enough to send over IPC @@ -124,6 +126,10 @@ class COLOR_SPACE_EXPORT ColorSpace { TransferID transfer, MatrixID matrix, RangeID full_range); + ColorSpace(PrimaryID primaries, + const SkColorSpaceTransferFn& fn, + MatrixID matrix, + RangeID full_range); ColorSpace(const ColorSpace& other); ColorSpace(ColorSpace&& other); ColorSpace& operator=(const ColorSpace& other); @@ -143,6 +149,7 @@ class COLOR_SPACE_EXPORT ColorSpace { // Extended sRGB matches sRGB for values in [0, 1], and extends the transfer // function to all real values. static ColorSpace CreateExtendedSRGB(); + // scRGB uses the same primaries as sRGB but has a linear transfer function // for all real values. static ColorSpace CreateSCRGBLinear(); @@ -160,18 +167,28 @@ class COLOR_SPACE_EXPORT ColorSpace { // Returns true if the decoded values can be outside of the 0.0-1.0 range. bool IsHDR() const; + // Returns true if the encoded values can be outside of the 0.0-1.0 range. bool FullRangeEncodedValues() const; // Returns true if this color space can be represented parametrically. bool IsParametric() const; + // Return a parametric approximation of this color space (if it is not already // parametric). - gfx::ColorSpace GetParametricApproximation() const; + ColorSpace GetParametricApproximation() const; // Return this color space with any range adjust or YUV to RGB conversion // stripped off. - gfx::ColorSpace GetAsFullRangeRGB() const; + ColorSpace GetAsFullRangeRGB() const; + + // If |this| is the final output color space, return the color space that + // would be appropriate for rasterization. + ColorSpace GetRasterColorSpace() const; + + // If |this| is the final output color space, return the color space that + // would be appropriate for blending. + ColorSpace GetBlendingColorSpace() const; // This will return nullptr for non-RGB spaces, spaces with non-FULL // range, and unspecified spaces. @@ -180,6 +197,7 @@ class COLOR_SPACE_EXPORT ColorSpace { // Populate |icc_profile| with an ICC profile that represents this color // space. Returns false if this space is not representable. bool GetICCProfile(ICCProfile* icc_profile) const; + bool GetICCProfileData(std::vector<char>* data) const; void GetPrimaryMatrix(SkMatrix44* to_XYZD50) const; bool GetTransferFunction(SkColorSpaceTransferFn* fn) const; @@ -190,6 +208,8 @@ class COLOR_SPACE_EXPORT ColorSpace { void GetRangeAdjustMatrix(SkMatrix44* matrix) const; private: + void SetCustomTransferFunction(const SkColorSpaceTransferFn& fn); + // Returns true if the transfer function is defined by an // SkColorSpaceTransferFn which is extended to all real values. bool HasExtendedSkTransferFn() const; @@ -213,10 +233,11 @@ class COLOR_SPACE_EXPORT ColorSpace { sk_sp<SkColorSpace> icc_profile_sk_color_space_; friend class ICCProfile; + friend class ICCProfileCache; friend class ColorTransform; friend class ColorTransformInternal; friend class ColorSpaceWin; - friend struct IPC::ParamTraits<gfx::ColorSpace>; + friend struct IPC::ParamTraits<ColorSpace>; FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace); }; diff --git a/chromium/ui/gfx/color_space_unittest.cc b/chromium/ui/gfx/color_space_unittest.cc index 211604c14fb..bef60b8f80c 100644 --- a/chromium/ui/gfx/color_space_unittest.cc +++ b/chromium/ui/gfx/color_space_unittest.cc @@ -239,6 +239,23 @@ TEST(ColorSpace, ApproximateTransferFnBadMatch) { } } +TEST(ColorSpace, RasterAndBlend) { + ColorSpace display_color_space; + + // A linear transfer function being used for HDR should be blended using an + // sRGB-like transfer function. + display_color_space = ColorSpace::CreateSCRGBLinear(); + EXPECT_EQ(ColorSpace::CreateExtendedSRGB(), + display_color_space.GetBlendingColorSpace()); + EXPECT_EQ(ColorSpace::CreateDisplayP3D65(), + display_color_space.GetRasterColorSpace()); + + // If not used for HDR, a linear transfer function should be left unchanged. + display_color_space = ColorSpace::CreateXYZD50(); + EXPECT_EQ(display_color_space, display_color_space.GetBlendingColorSpace()); + EXPECT_EQ(display_color_space, display_color_space.GetRasterColorSpace()); +} + TEST(ColorSpace, ToSkColorSpace) { const size_t kNumTests = 4; ColorSpace color_spaces[kNumTests] = { diff --git a/chromium/ui/gfx/color_transform.cc b/chromium/ui/gfx/color_transform.cc index f3c3041fde6..3c31fb3f9f5 100644 --- a/chromium/ui/gfx/color_transform.cc +++ b/chromium/ui/gfx/color_transform.cc @@ -811,16 +811,6 @@ void ColorTransformInternal::AppendColorSpaceToColorSpaceTransform( ColorTransform::Intent intent) { if (intent == ColorTransform::Intent::INTENT_PERCEPTUAL) { switch (src.transfer_) { - case ColorSpace::TransferID::BT709: - case ColorSpace::TransferID::SMPTE170M: - // SMPTE 1886 suggests that we should be using gamma 2.4 for BT709 - // content. However, most displays actually use a gamma of 2.2, and - // user studies shows that users don't really care. Using the same - // gamma as the display will let us optimize a lot more, so lets stick - // with using the SRGB transfer function. - src.transfer_ = ColorSpace::TransferID::IEC61966_2_1; - break; - case ColorSpace::TransferID::SMPTEST2084: if (!dst.IsHDR()) { // We don't have an HDR display, so replace SMPTE 2084 with diff --git a/chromium/ui/gfx/color_transform_unittest.cc b/chromium/ui/gfx/color_transform_unittest.cc index d5217a77012..906adedceb8 100644 --- a/chromium/ui/gfx/color_transform_unittest.cc +++ b/chromium/ui/gfx/color_transform_unittest.cc @@ -133,6 +133,14 @@ TEST(SimpleColorSpace, TransferFnCancel) { ColorTransform::NewColorTransform( bt709, gamma24, ColorTransform::Intent::INTENT_PERCEPTUAL)); EXPECT_EQ(bt709_to_gamma24->NumberOfStepsForTesting(), 2u); + + // Rec 601 YUV to RGB conversion should have a single step. + gfx::ColorSpace rec601 = gfx::ColorSpace::CreateREC601(); + std::unique_ptr<ColorTransform> rec601_yuv_to_rgb( + ColorTransform::NewColorTransform( + rec601, rec601.GetAsFullRangeRGB(), + ColorTransform::Intent::INTENT_PERCEPTUAL)); + EXPECT_EQ(rec601_yuv_to_rgb->NumberOfStepsForTesting(), 1u); } TEST(SimpleColorSpace, SRGBFromICCAndNotICC) { diff --git a/chromium/ui/gfx/font_fallback_fuchsia.cc b/chromium/ui/gfx/font_fallback_fuchsia.cc new file mode 100644 index 00000000000..bfdc7aa2ce9 --- /dev/null +++ b/chromium/ui/gfx/font_fallback_fuchsia.cc @@ -0,0 +1,18 @@ +// 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/gfx/font_fallback.h" + +#include <string> +#include <vector> + +namespace gfx { + +std::vector<Font> GetFallbackFonts(const Font& font) { + // TODO(fuchsia): Stubbed while bringing up headless build, see + // https://crbug.com/743296. + return std::vector<Font>(); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/font_render_params.h b/chromium/ui/gfx/font_render_params.h index ade3672f493..39d72e35203 100644 --- a/chromium/ui/gfx/font_render_params.h +++ b/chromium/ui/gfx/font_render_params.h @@ -113,8 +113,7 @@ GFX_EXPORT FontRenderParams GetFontRenderParams( GFX_EXPORT void ClearFontRenderParamsCacheForTest(); #endif -#if defined(OS_CHROMEOS) || defined(OS_LINUX) || \ - (defined(OS_ANDROID) && BUILDFLAG(ENABLE_VR)) +#if defined(OS_LINUX) || (defined(OS_ANDROID) && BUILDFLAG(ENABLE_VR)) // Gets the device scale factor to query the FontRenderParams. GFX_EXPORT float GetFontRenderParamsDeviceScaleFactor(); diff --git a/chromium/ui/gfx/font_render_params_fuchsia.cc b/chromium/ui/gfx/font_render_params_fuchsia.cc new file mode 100644 index 00000000000..f693f926c22 --- /dev/null +++ b/chromium/ui/gfx/font_render_params_fuchsia.cc @@ -0,0 +1,38 @@ +// 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/gfx/font_render_params.h" + +#include "base/logging.h" +#include "base/macros.h" + +namespace gfx { + +namespace { + +// Returns the system's default settings. +FontRenderParams LoadDefaults() { + FontRenderParams params; + params.antialiasing = true; + params.autohinter = true; + params.use_bitmaps = true; + params.subpixel_rendering = FontRenderParams::SUBPIXEL_RENDERING_NONE; + params.subpixel_positioning = true; + params.hinting = FontRenderParams::HINTING_SLIGHT; + + return params; +} + +} // namespace + +FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query, + std::string* family_out) { + if (family_out) + NOTIMPLEMENTED(); + // Customized font rendering settings are not supported, only defaults. + CR_DEFINE_STATIC_LOCAL(const gfx::FontRenderParams, params, (LoadDefaults())); + return params; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/geometry/point.h b/chromium/ui/gfx/geometry/point.h index bb248d5432b..b1ba5065de4 100644 --- a/chromium/ui/gfx/geometry/point.h +++ b/chromium/ui/gfx/geometry/point.h @@ -9,7 +9,7 @@ #include <string> #include <tuple> -#include "base/numerics/saturated_arithmetic.h" +#include "base/numerics/clamped_math.h" #include "build/build_config.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/gfx_export.h" @@ -56,18 +56,18 @@ class GFX_EXPORT Point { } void Offset(int delta_x, int delta_y) { - x_ = base::SaturatedAddition(x_, delta_x); - y_ = base::SaturatedAddition(y_, delta_y); + x_ = base::ClampAdd(x_, delta_x); + y_ = base::ClampAdd(y_, delta_y); } void operator+=(const Vector2d& vector) { - x_ = base::SaturatedAddition(x_, vector.x()); - y_ = base::SaturatedAddition(y_, vector.y()); + x_ = base::ClampAdd(x_, vector.x()); + y_ = base::ClampAdd(y_, vector.y()); } void operator-=(const Vector2d& vector) { - x_ = base::SaturatedSubtraction(x_, vector.x()); - y_ = base::SaturatedSubtraction(y_, vector.y()); + x_ = base::ClampSub(x_, vector.x()); + y_ = base::ClampSub(y_, vector.y()); } void SetToMin(const Point& other); @@ -116,8 +116,8 @@ inline Point operator-(const Point& lhs, const Vector2d& rhs) { } inline Vector2d operator-(const Point& lhs, const Point& rhs) { - return Vector2d(base::SaturatedSubtraction(lhs.x(), rhs.x()), - base::SaturatedSubtraction(lhs.y(), rhs.y())); + return Vector2d(base::ClampSub(lhs.x(), rhs.x()), + base::ClampSub(lhs.y(), rhs.y())); } inline Point PointAtOffsetFromOrigin(const Vector2d& offset_from_origin) { diff --git a/chromium/ui/gfx/geometry/rect.cc b/chromium/ui/gfx/geometry/rect.cc index b5ceda58291..63c0bbf04f0 100644 --- a/chromium/ui/gfx/geometry/rect.cc +++ b/chromium/ui/gfx/geometry/rect.cc @@ -15,7 +15,7 @@ #endif #include "base/logging.h" -#include "base/numerics/saturated_arithmetic.h" +#include "base/numerics/clamped_math.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "ui/gfx/geometry/insets.h" @@ -69,8 +69,8 @@ static void SaturatedClampRange(int min, int max, int* origin, int* span) { return; } - int effective_span = base::SaturatedSubtraction(max, min); - int span_loss = base::SaturatedSubtraction(max, min + effective_span); + int effective_span = base::ClampSub(max, min); + int span_loss = base::ClampSub(max, min + effective_span); // If the desired width is within the limits of ints, we can just // use the simple computations to represent the range precisely. @@ -83,12 +83,12 @@ static void SaturatedClampRange(int min, int max, int* origin, int* span) { // Now we have to approximate. If one of min or max is close enough // to zero we choose to represent that one precisely. The other side is // probably practically "infinite", so we move it. - if (base::SaturatedAbsolute(max) < std::numeric_limits<int>::max() / 2) { + constexpr unsigned kMaxDimension = std::numeric_limits<int>::max() / 2; + if (base::SafeUnsignedAbs(max) < kMaxDimension) { // Maintain origin + span == max. *span = effective_span; *origin = max - effective_span; - } else if (base::SaturatedAbsolute(min) < - std::numeric_limits<int>::max() / 2) { + } else if (base::SafeUnsignedAbs(min) < kMaxDimension) { // Maintain origin == min. *span = effective_span; *origin = min; @@ -116,10 +116,8 @@ void Rect::Inset(int left, int top, int right, int bottom) { origin_ += Vector2d(left, top); // left+right might overflow/underflow, but width() - (left+right) might // overflow as well. - set_width(base::SaturatedSubtraction(width(), - base::SaturatedAddition(left, right))); - set_height(base::SaturatedSubtraction(height(), - base::SaturatedAddition(top, bottom))); + set_width(base::ClampSub(width(), base::ClampAdd(left, right))); + set_height(base::ClampSub(height(), base::ClampAdd(top, bottom))); } void Rect::Offset(int horizontal, int vertical) { diff --git a/chromium/ui/gfx/geometry/rect.h b/chromium/ui/gfx/geometry/rect.h index 1858d44d2c7..c33d4a7bae6 100644 --- a/chromium/ui/gfx/geometry/rect.h +++ b/chromium/ui/gfx/geometry/rect.h @@ -227,7 +227,7 @@ class GFX_EXPORT Rect { // Clamp the size to avoid integer overflow in bottom() and right(). // This returns the width given an origin and a width. - // TODO(enne): this should probably use base::SaturatedAddition, but that + // TODO(enne): this should probably use base::ClampAdd, but that // function is not a constexpr. static constexpr int GetClampedValue(int origin, int size) { return AddWouldOverflow(origin, size) @@ -340,6 +340,41 @@ inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) { return ScaleToEnclosedRect(rect, scale, scale); } +// Scales |rect| by scaling its four corner points. If the corner points lie on +// non-integral coordinate after scaling, their values are rounded to the +// nearest integer. +// This is helpful during layout when relative positions of multiple gfx::Rect +// in a given coordinate space needs to be same after scaling as it was before +// scaling. ie. this gives a lossless relative positioning of rects. +inline Rect ScaleToRoundedRect(const Rect& rect, float x_scale, float y_scale) { + if (x_scale == 1.f && y_scale == 1.f) + return rect; + + DCHECK( + base::IsValueInRangeForNumericType<int>(std::round(rect.x() * x_scale))); + DCHECK( + base::IsValueInRangeForNumericType<int>(std::round(rect.y() * y_scale))); + DCHECK(base::IsValueInRangeForNumericType<int>( + std::round(rect.right() * x_scale))); + DCHECK(base::IsValueInRangeForNumericType<int>( + std::round(rect.bottom() * y_scale))); + + int x = static_cast<int>(std::round(rect.x() * x_scale)); + int y = static_cast<int>(std::round(rect.y() * y_scale)); + int r = rect.width() == 0 + ? x + : static_cast<int>(std::round(rect.right() * x_scale)); + int b = rect.height() == 0 + ? y + : static_cast<int>(std::round(rect.bottom() * y_scale)); + + return Rect(x, y, r - x, b - y); +} + +inline Rect ScaleToRoundedRect(const Rect& rect, float scale) { + return ScaleToRoundedRect(rect, scale, scale); +} + // This is declared here for use in gtest-based unit tests but is defined in // the //ui/gfx:test_support target. Depend on that to use this in your unit // test. This should not be used in production code - call ToString() instead. diff --git a/chromium/ui/gfx/geometry/size.cc b/chromium/ui/gfx/geometry/size.cc index 69486723a1d..781b84d7f51 100644 --- a/chromium/ui/gfx/geometry/size.cc +++ b/chromium/ui/gfx/geometry/size.cc @@ -12,8 +12,8 @@ #include <ApplicationServices/ApplicationServices.h> #endif +#include "base/numerics/clamped_math.h" #include "base/numerics/safe_math.h" -#include "base/numerics/saturated_arithmetic.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "ui/gfx/geometry/safe_integer_conversions.h" @@ -58,8 +58,8 @@ base::CheckedNumeric<int> Size::GetCheckedArea() const { } void Size::Enlarge(int grow_width, int grow_height) { - SetSize(base::SaturatedAddition(width(), grow_width), - base::SaturatedAddition(height(), grow_height)); + SetSize(base::ClampAdd(width(), grow_width), + base::ClampAdd(height(), grow_height)); } void Size::SetToMin(const Size& other) { diff --git a/chromium/ui/gfx/geometry/vector2d.cc b/chromium/ui/gfx/geometry/vector2d.cc index 2b4875c39cb..0ce3b20baa5 100644 --- a/chromium/ui/gfx/geometry/vector2d.cc +++ b/chromium/ui/gfx/geometry/vector2d.cc @@ -6,7 +6,7 @@ #include <cmath> -#include "base/numerics/saturated_arithmetic.h" +#include "base/numerics/clamped_math.h" #include "base/strings/stringprintf.h" namespace gfx { @@ -16,13 +16,13 @@ bool Vector2d::IsZero() const { } void Vector2d::Add(const Vector2d& other) { - x_ = base::SaturatedAddition(other.x_, x_); - y_ = base::SaturatedAddition(other.y_, y_); + x_ = base::ClampAdd(other.x_, x_); + y_ = base::ClampAdd(other.y_, y_); } void Vector2d::Subtract(const Vector2d& other) { - x_ = base::SaturatedSubtraction(x_, other.x_); - y_ = base::SaturatedSubtraction(y_, other.y_); + x_ = base::ClampSub(x_, other.x_); + y_ = base::ClampSub(y_, other.y_); } int64_t Vector2d::LengthSquared() const { diff --git a/chromium/ui/gfx/gpu_memory_buffer_tracing.cc b/chromium/ui/gfx/gpu_memory_buffer_tracing.cc deleted file mode 100644 index 5f587074862..00000000000 --- a/chromium/ui/gfx/gpu_memory_buffer_tracing.cc +++ /dev/null @@ -1,21 +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/gfx/gpu_memory_buffer_tracing.h" - -#include "base/format_macros.h" -#include "base/strings/stringprintf.h" - -namespace gfx { - -base::trace_event::MemoryAllocatorDumpGuid GetSharedMemoryGUIDForTracing( - uint64_t tracing_process_id, - GpuMemoryBufferId buffer_id) { - // TODO(hajimehoshi): This should be unified to shared memory GUIDs in - // base/memory/shared_memory_tracker.cc - return base::trace_event::MemoryAllocatorDumpGuid(base::StringPrintf( - "shared_memory_gpu/%" PRIx64 "/%d", tracing_process_id, buffer_id.id)); -} - -} // namespace gfx diff --git a/chromium/ui/gfx/gpu_memory_buffer_tracing.h b/chromium/ui/gfx/gpu_memory_buffer_tracing.h deleted file mode 100644 index 0b66a91abd8..00000000000 --- a/chromium/ui/gfx/gpu_memory_buffer_tracing.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/trace_event/memory_allocator_dump_guid.h" -#include "ui/gfx/gfx_export.h" -#include "ui/gfx/gpu_memory_buffer.h" - -namespace gfx { - -base::trace_event::MemoryAllocatorDumpGuid GFX_EXPORT -GetSharedMemoryGUIDForTracing(uint64_t tracing_process_id, - GpuMemoryBufferId buffer_id); - -} // namespace gfx diff --git a/chromium/ui/gfx/icc_profile.cc b/chromium/ui/gfx/icc_profile.cc index a982db54eff..0a9517ac7bd 100644 --- a/chromium/ui/gfx/icc_profile.cc +++ b/chromium/ui/gfx/icc_profile.cc @@ -5,10 +5,12 @@ #include "ui/gfx/icc_profile.h" #include <list> +#include <set> #include "base/command_line.h" #include "base/containers/mru_cache.h" #include "base/lazy_instance.h" +#include "base/metrics/histogram_macros.h" #include "base/synchronization/lock.h" #include "third_party/skia/include/core/SkColorSpaceXform.h" #include "third_party/skia/include/core/SkICC.h" @@ -21,36 +23,194 @@ const uint64_t ICCProfile::test_id_adobe_rgb_ = 1; const uint64_t ICCProfile::test_id_color_spin_ = 2; const uint64_t ICCProfile::test_id_generic_rgb_ = 3; const uint64_t ICCProfile::test_id_srgb_ = 4; -const uint64_t ICCProfile::test_id_no_analytic_tr_fn_ = 5; -const uint64_t ICCProfile::test_id_a2b_only_ = 6; -const uint64_t ICCProfile::test_id_overshoot_ = 7; -namespace { +// A MRU cache of ICC profiles. The cache key is a uin64_t which a +// gfx::ColorSpace may use to refer back to an ICC profile in the cache. The +// data cached for each profile is the gfx::ICCProfile structure (which includes +// the associated gfx::ColorSpace approximations and SkColorSpace structures) +// and whether or not the ICC profile has been histogrammed. +class ICCProfileCache { + public: + // Allow keeping around a maximum of 16 cached ICC profiles. Beware that + // we will do a linear search thorugh currently-cached ICC profiles, + // when creating a new ICC profile. + static const size_t kMaxCachedICCProfiles = 16; + + ICCProfileCache() : id_to_icc_profile_mru_(kMaxCachedICCProfiles) {} + ~ICCProfileCache() {} + + // Add |icc_profile| to the cache. If |icc_profile| does not have an id set + // yet, assign an id to it. + void InsertAndSetIdIfNeeded(ICCProfile* icc_profile) { + base::AutoLock lock(lock_); + + if (FindByIdUnderLock(icc_profile->id_, icc_profile)) + return; + + if (FindByDataUnderLock(icc_profile->data_.data(), + icc_profile->data_.size(), icc_profile)) { + return; + } + + if (!icc_profile->id_) + icc_profile->id_ = next_unused_id_++; + + // Ensure that GetColorSpace() point back to this ICCProfile. + gfx::ColorSpace& color_space = icc_profile->color_space_; + color_space.icc_profile_id_ = icc_profile->id_; + + // Ensure that the GetParametricColorSpace() point back to this ICCProfile + // only if the parametric version is accurate. + if (color_space.primaries_ != ColorSpace::PrimaryID::ICC_BASED && + color_space.transfer_ != ColorSpace::TransferID::ICC_BASED) { + icc_profile->parametric_color_space_.icc_profile_id_ = icc_profile->id_; + } + + Entry entry; + entry.icc_profile = *icc_profile; + id_to_icc_profile_mru_.Put(icc_profile->id_, entry); + } + + // We maintain UMA histograms of display ICC profiles. Only histogram a + // display once for each |display_id| (because we will re-read the same + // ICC profile repeatedly when reading other display profiles, which will + // skew samples). Return true if we need to histogram this profile for + // |display_id|, and ensure that all future calls will return false for + // |display_id|. + bool GetAndSetNeedsHistogram(uint64_t display_id, + const ICCProfile& icc_profile) { + base::AutoLock lock(lock_); + + // If we don't find the profile in the cache, don't histogram it. + auto found = id_to_icc_profile_mru_.Get(icc_profile.id_); + if (found == id_to_icc_profile_mru_.end()) + return false; + + // If we have already histogrammed this display, don't histogram it. + std::set<int64_t>& histogrammed_display_ids = + found->second.histogrammed_display_ids; + if (histogrammed_display_ids.count(display_id)) + return false; + + // Histogram this display, and mark that we have done so. + histogrammed_display_ids.insert(display_id); + return true; + } + + // Move this ICC profile to the most recently used end of the cache, + // re-inserting if needed. + void TouchEntry(const ICCProfile& icc_profile) { + base::AutoLock lock(lock_); + + if (!icc_profile.id_) + return; + + // Look up the profile by id to move it to the front of the MRU. + auto found = id_to_icc_profile_mru_.Get(icc_profile.id_); + if (found != id_to_icc_profile_mru_.end()) + return; + + // Look up the profile by its data. If there is a new entry for the same + // data, don't add a duplicate. + if (FindByDataUnderLock(icc_profile.data_.data(), icc_profile.data_.size(), + nullptr)) { + return; + } -// Allow keeping around a maximum of 8 cached ICC profiles. Beware that -// we will do a linear search thorugh currently-cached ICC profiles, -// when creating a new ICC profile. -const size_t kMaxCachedICCProfiles = 8; + // If the entry was not found, insert it. + Entry entry; + entry.icc_profile = icc_profile; + id_to_icc_profile_mru_.Put(icc_profile.id_, entry); + } -struct Cache { - Cache() : id_to_icc_profile_mru(kMaxCachedICCProfiles) {} - ~Cache() {} + // Look up an ICC profile in the cache by its data (to ensure that the same + // data gets the same id every time). On success, return true and populate + // |icc_profile| with the associated profile. + bool FindByData(const void* data, size_t size, ICCProfile* icc_profile) { + base::AutoLock lock(lock_); + return FindByDataUnderLock(data, size, icc_profile); + } + + // Look up an ICC profile in the cache by its id. On success, return true and + // populate |icc_profile| with the associated profile. + bool FindById(uint64_t id, ICCProfile* icc_profile) { + base::AutoLock lock(lock_); + return FindByIdUnderLock(id, icc_profile); + } + + private: + struct Entry { + ICCProfile icc_profile; + + // The set of display ids which have have caused this ICC profile to be + // recorded in UMA histograms. Only record an ICC profile once per display + // id (since the same profile will be re-read repeatedly, e.g, when displays + // are resized). + std::set<int64_t> histogrammed_display_ids; + }; + + // Body for FindById, executed when the cache lock is already held. + bool FindByIdUnderLock(uint64_t id, ICCProfile* icc_profile) { + lock_.AssertAcquired(); + if (!id) + return false; + + auto found = id_to_icc_profile_mru_.Get(id); + if (found == id_to_icc_profile_mru_.end()) + return false; + + *icc_profile = found->second.icc_profile; + return true; + } + + // Body for FindByData, executed when the cache lock is already held. + bool FindByDataUnderLock(const void* data, + size_t size, + ICCProfile* icc_profile) { + lock_.AssertAcquired(); + if (size == 0) + return false; + + for (const auto& id_entry_pair : id_to_icc_profile_mru_) { + const ICCProfile& cached_profile = id_entry_pair.second.icc_profile; + const std::vector<char>& iter_data = cached_profile.data_; + if (iter_data.size() != size || memcmp(data, iter_data.data(), size)) + continue; + + if (icc_profile) { + *icc_profile = cached_profile; + id_to_icc_profile_mru_.Get(cached_profile.id_); + } + return true; + } + return false; + } // Start from-ICC-data IDs at the end of the hard-coded test id list above. - uint64_t next_unused_id = 10; - base::MRUCache<uint64_t, ICCProfile> id_to_icc_profile_mru; - base::Lock lock; + uint64_t next_unused_id_ = 10; + base::MRUCache<uint64_t, Entry> id_to_icc_profile_mru_; + + // Lock that must be held to access |id_to_icc_profile_mru_| and + // |next_unused_id_|. + base::Lock lock_; }; -static base::LazyInstance<Cache>::DestructorAtExit g_cache = + +namespace { + +static base::LazyInstance<ICCProfileCache>::DestructorAtExit g_cache = LAZY_INSTANCE_INITIALIZER; -void ExtractColorSpaces(const std::vector<char>& data, - gfx::ColorSpace* parametric_color_space, - bool* parametric_color_space_is_accurate, - sk_sp<SkColorSpace>* useable_sk_color_space) { +} // namespace + +// static +ICCProfile::AnalyzeResult ICCProfile::ExtractColorSpaces( + const std::vector<char>& data, + gfx::ColorSpace* parametric_color_space, + float* parametric_tr_fn_max_error, + sk_sp<SkColorSpace>* useable_sk_color_space) { // Initialize the output parameters as invalid. *parametric_color_space = gfx::ColorSpace(); - *parametric_color_space_is_accurate = false; + *parametric_tr_fn_max_error = 0; *useable_sk_color_space = nullptr; // Parse the profile and attempt to create a SkColorSpaceXform out of it. @@ -58,20 +218,20 @@ void ExtractColorSpaces(const std::vector<char>& data, sk_sp<SkICC> sk_icc = SkICC::Make(data.data(), data.size()); if (!sk_icc) { DLOG(ERROR) << "Failed to parse ICC profile to SkICC."; - return; + return kICCFailedToParse; } sk_sp<SkColorSpace> sk_icc_color_space = SkColorSpace::MakeICC(data.data(), data.size()); if (!sk_icc_color_space) { DLOG(ERROR) << "Failed to parse ICC profile to SkColorSpace."; - return; + return kICCFailedToExtractSkColorSpace; } std::unique_ptr<SkColorSpaceXform> sk_color_space_xform = SkColorSpaceXform::New(sk_srgb_color_space.get(), sk_icc_color_space.get()); if (!sk_color_space_xform) { DLOG(ERROR) << "Parsed ICC profile but can't create SkColorSpaceXform."; - return; + return kICCFailedToCreateXform; } // Because this SkColorSpace can be used to construct a transform, mark it @@ -82,15 +242,14 @@ void ExtractColorSpaces(const std::vector<char>& data, // If our SkColorSpace representation is sRGB then return that. if (SkColorSpace::Equals(sk_srgb_color_space.get(), sk_icc_color_space.get())) { - *parametric_color_space_is_accurate = true; - return; + return kICCExtractedSRGBColorSpace; } // A primary matrix is required for our parametric approximation. SkMatrix44 to_XYZD50_matrix; if (!sk_icc->toXYZD50(&to_XYZD50_matrix)) { DLOG(ERROR) << "Failed to extract ICC profile primary matrix."; - return; + return kICCFailedToExtractMatrix; } // Try to directly extract a numerical transfer function. @@ -98,27 +257,7 @@ void ExtractColorSpaces(const std::vector<char>& data, if (sk_icc->isNumericalTransferFn(&exact_tr_fn)) { *parametric_color_space = gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, exact_tr_fn); - *parametric_color_space_is_accurate = true; - return; - } - - // If that fails, try to numerically approximate the transfer function. - SkColorSpaceTransferFn approx_tr_fn; - float approx_tr_fn_max_error = 0; - if (SkApproximateTransferFn(sk_icc, &approx_tr_fn_max_error, &approx_tr_fn)) { - const float kMaxError = 2.f / 256.f; - if (approx_tr_fn_max_error < kMaxError) { - *parametric_color_space = - gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, approx_tr_fn); - *parametric_color_space_is_accurate = true; - return; - } else { - DLOG(ERROR) - << "Failed to accurately approximate transfer function, error: " - << 256.f * approx_tr_fn_max_error << "/256"; - } - } else { - DLOG(ERROR) << "Failed approximate transfer function."; + return kICCExtractedMatrixAndAnalyticTrFn; } // If we fail to get a transfer function, use the sRGB transfer function, @@ -127,9 +266,31 @@ void ExtractColorSpaces(const std::vector<char>& data, // SkColorSpace. *parametric_color_space = gfx::ColorSpace::CreateCustom( to_XYZD50_matrix, ColorSpace::TransferID::IEC61966_2_1); -} -} // namespace + // Attempt to fit a parametric transfer function to the table data in the + // profile. + SkColorSpaceTransferFn approx_tr_fn; + if (!SkApproximateTransferFn(sk_icc, parametric_tr_fn_max_error, + &approx_tr_fn)) { + DLOG(ERROR) << "Failed approximate transfer function."; + return kICCFailedToConvergeToApproximateTrFn; + } + + // If this converged, but has too high error, use the sRGB transfer function + // from above. + const float kMaxError = 2.f / 256.f; + if (*parametric_tr_fn_max_error >= kMaxError) { + DLOG(ERROR) << "Failed to accurately approximate transfer function, error: " + << 256.f * (*parametric_tr_fn_max_error) << "/256"; + return kICCFailedToApproximateTrFnAccurately; + }; + + // If the error is sufficiently low, declare that the approximation is + // accurate. + *parametric_color_space = + gfx::ColorSpace::CreateCustom(to_XYZD50_matrix, approx_tr_fn); + return kICCExtractedMatrixAndApproximatedTrFn; +} ICCProfile::ICCProfile() = default; ICCProfile::ICCProfile(ICCProfile&& other) = default; @@ -147,7 +308,15 @@ bool ICCProfile::operator!=(const ICCProfile& other) const { } bool ICCProfile::IsValid() const { - return successfully_parsed_by_sk_icc_; + switch (analyze_result_) { + case kICCFailedToParse: + case kICCFailedToExtractSkColorSpace: + case kICCFailedToCreateXform: + return false; + default: + break; + } + return true; } // static @@ -159,30 +328,14 @@ ICCProfile ICCProfile::FromData(const void* data, size_t size) { ICCProfile ICCProfile::FromDataWithId(const void* data, size_t size, uint64_t new_profile_id) { - if (!size) - return ICCProfile(); + ICCProfile icc_profile; - const char* data_as_char = reinterpret_cast<const char*>(data); - { - // Linearly search the cached ICC profiles to find one with the same data. - // If it exists, re-use its id and touch it in the cache. - Cache& cache = g_cache.Get(); - base::AutoLock lock(cache.lock); - for (auto iter = cache.id_to_icc_profile_mru.begin(); - iter != cache.id_to_icc_profile_mru.end(); ++iter) { - const std::vector<char>& iter_data = iter->second.data_; - if (iter_data.size() != size || memcmp(data, iter_data.data(), size)) - continue; - auto found = cache.id_to_icc_profile_mru.Get(iter->second.id_); - return found->second; - } - if (!new_profile_id) - new_profile_id = cache.next_unused_id++; - } + if (!size) + return icc_profile; // Create a new cached id and add it to the cache. - ICCProfile icc_profile; icc_profile.id_ = new_profile_id; + const char* data_as_char = reinterpret_cast<const char*>(data); icc_profile.data_.insert(icc_profile.data_.begin(), data_as_char, data_as_char + size); icc_profile.ComputeColorSpaceAndCache(); @@ -203,93 +356,89 @@ const std::vector<char>& ICCProfile::GetData() const { } const ColorSpace& ICCProfile::GetColorSpace() const { - // Move this ICC profile to the most recently used end of the cache, - // inserting if needed. - if (id_) { - Cache& cache = g_cache.Get(); - base::AutoLock lock(cache.lock); - auto found = cache.id_to_icc_profile_mru.Get(id_); - if (found == cache.id_to_icc_profile_mru.end()) - found = cache.id_to_icc_profile_mru.Put(id_, *this); - } + g_cache.Get().TouchEntry(*this); return color_space_; } const ColorSpace& ICCProfile::GetParametricColorSpace() const { - // Move this ICC profile to the most recently used end of the cache, - // inserting if needed. - if (id_) { - Cache& cache = g_cache.Get(); - base::AutoLock lock(cache.lock); - auto found = cache.id_to_icc_profile_mru.Get(id_); - if (found == cache.id_to_icc_profile_mru.end()) - found = cache.id_to_icc_profile_mru.Put(id_, *this); - } + g_cache.Get().TouchEntry(*this); return parametric_color_space_; } // static bool ICCProfile::FromId(uint64_t id, ICCProfile* icc_profile) { - if (!id) - return false; - - Cache& cache = g_cache.Get(); - base::AutoLock lock(cache.lock); - - auto found = cache.id_to_icc_profile_mru.Get(id); - if (found == cache.id_to_icc_profile_mru.end()) - return false; - - *icc_profile = found->second; - return true; + return g_cache.Get().FindById(id, icc_profile); } void ICCProfile::ComputeColorSpaceAndCache() { - if (!id_) + // Early out for empty entries. + if (data_.empty()) return; - // If this already exists in the cache, just update its |color_space_|. - { - Cache& cache = g_cache.Get(); - base::AutoLock lock(cache.lock); - auto found = cache.id_to_icc_profile_mru.Get(id_); - if (found != cache.id_to_icc_profile_mru.end()) { - color_space_ = found->second.color_space_; - parametric_color_space_ = found->second.parametric_color_space_; - successfully_parsed_by_sk_icc_ = - found->second.successfully_parsed_by_sk_icc_; - return; - } - } + // If this id already exists in the cache, copy |this| from the cache entry. + if (g_cache.Get().FindById(id_, this)) + return; + + // If this data already exists in the cache, copy |this| from the cache entry. + if (g_cache.Get().FindByData(data_.data(), data_.size(), this)) + return; // Parse the ICC profile sk_sp<SkColorSpace> useable_sk_color_space; - bool parametric_color_space_is_accurate = false; - ExtractColorSpaces(data_, ¶metric_color_space_, - ¶metric_color_space_is_accurate, - &useable_sk_color_space); - if (parametric_color_space_is_accurate) { - successfully_parsed_by_sk_icc_ = true; - parametric_color_space_.icc_profile_id_ = id_; - color_space_ = parametric_color_space_; - } else if (useable_sk_color_space) { - successfully_parsed_by_sk_icc_ = true; - color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED, - ColorSpace::TransferID::ICC_BASED); - color_space_.icc_profile_id_ = id_; - color_space_.icc_profile_sk_color_space_ = useable_sk_color_space; - } else { - successfully_parsed_by_sk_icc_ = false; - DCHECK(!color_space_.IsValid()); - color_space_ = parametric_color_space_; + analyze_result_ = + ExtractColorSpaces(data_, ¶metric_color_space_, + ¶metric_tr_fn_error_, &useable_sk_color_space); + switch (analyze_result_) { + case kICCExtractedSRGBColorSpace: + case kICCExtractedMatrixAndAnalyticTrFn: + case kICCExtractedMatrixAndApproximatedTrFn: + // Successfully and accurately extracted color space. + color_space_ = parametric_color_space_; + break; + case kICCFailedToExtractRawTrFn: + case kICCFailedToExtractMatrix: + case kICCFailedToConvergeToApproximateTrFn: + case kICCFailedToApproximateTrFnAccurately: + // Successfully but extracted a color space, but it isn't accurate enough. + color_space_ = ColorSpace(ColorSpace::PrimaryID::ICC_BASED, + ColorSpace::TransferID::ICC_BASED); + color_space_.icc_profile_sk_color_space_ = useable_sk_color_space; + break; + case kICCFailedToParse: + case kICCFailedToExtractSkColorSpace: + case kICCFailedToCreateXform: + // Can't even use this color space as a LUT. + DCHECK(!parametric_color_space_.IsValid()); + color_space_ = parametric_color_space_; + break; } // Add to the cache. - { - Cache& cache = g_cache.Get(); - base::AutoLock lock(cache.lock); - cache.id_to_icc_profile_mru.Put(id_, *this); + g_cache.Get().InsertAndSetIdIfNeeded(this); +} + +void ICCProfile::HistogramDisplay(int64_t display_id) const { + if (!g_cache.Get().GetAndSetNeedsHistogram(display_id, *this)) + return; + + UMA_HISTOGRAM_ENUMERATION("Blink.ColorSpace.Destination.ICCResult", + analyze_result_, kICCProfileAnalyzeLast); + + // Add histograms for numerical approximation. + bool nonlinear_fit_converged = + analyze_result_ == kICCExtractedMatrixAndApproximatedTrFn || + analyze_result_ == kICCFailedToApproximateTrFnAccurately; + bool nonlinear_fit_did_not_converge = + analyze_result_ == kICCFailedToConvergeToApproximateTrFn; + if (nonlinear_fit_converged || nonlinear_fit_did_not_converge) { + UMA_HISTOGRAM_BOOLEAN("Blink.ColorSpace.Destination.NonlinearFitConverged", + nonlinear_fit_converged); + } + if (nonlinear_fit_converged) { + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Blink.ColorSpace.Destination.NonlinearFitError", + static_cast<int>(parametric_tr_fn_error_ * 255), 0, 127, 16); } } diff --git a/chromium/ui/gfx/icc_profile.h b/chromium/ui/gfx/icc_profile.h index 03170adfbda..257b536dba3 100644 --- a/chromium/ui/gfx/icc_profile.h +++ b/chromium/ui/gfx/icc_profile.h @@ -23,6 +23,8 @@ template <typename, typename> struct StructTraits; namespace gfx { +class ICCProfileCache; + // Used to represent a full ICC profile, usually retrieved from a monitor. It // can be lossily compressed into a ColorSpace object. This structure should // only be sent from higher-privilege processes to lower-privilege processes, @@ -49,11 +51,6 @@ class COLOR_SPACE_EXPORT ICCProfile { static ICCProfile FromCGColorSpace(CGColorSpaceRef cg_color_space); #endif - // This will recover a ICCProfile from a compact ColorSpace representation. - // Internally, this will make an effort to create an identical ICCProfile - // to the one that created |color_space|, but this is not guaranteed. - static ICCProfile FromColorSpace(const gfx::ColorSpace& color_space); - // Create directly from profile data. static ICCProfile FromData(const void* icc_profile, size_t size); @@ -68,6 +65,10 @@ class COLOR_SPACE_EXPORT ICCProfile { const std::vector<char>& GetData() const; + // Histogram how we this was approximated by a gfx::ColorSpace. Only + // histogram a given profile once per display. + void HistogramDisplay(int64_t display_id) const; + #if defined(OS_WIN) // This will read monitor ICC profiles from disk and cache the results for the // other functions to read. This should not be called on the UI or IO thread. @@ -76,33 +77,49 @@ class COLOR_SPACE_EXPORT ICCProfile { #endif private: + // This must match ICCProfileAnalyzeResult enum in histograms.xml. + enum AnalyzeResult { + kICCExtractedMatrixAndAnalyticTrFn = 0, + kICCExtractedMatrixAndApproximatedTrFn = 1, + kICCFailedToConvergeToApproximateTrFn = 2, + kICCFailedToExtractRawTrFn = 3, + kICCFailedToExtractMatrix = 4, + kICCFailedToParse = 5, + kICCFailedToExtractSkColorSpace = 6, + kICCFailedToCreateXform = 7, + kICCFailedToApproximateTrFnAccurately = 8, + kICCExtractedSRGBColorSpace = 9, + kICCProfileAnalyzeLast = kICCExtractedSRGBColorSpace, + }; + + friend class ICCProfileCache; friend ICCProfile ICCProfileForTestingAdobeRGB(); friend ICCProfile ICCProfileForTestingColorSpin(); friend ICCProfile ICCProfileForTestingGenericRGB(); friend ICCProfile ICCProfileForTestingSRGB(); - friend ICCProfile ICCProfileForTestingNoAnalyticTrFn(); - friend ICCProfile ICCProfileForTestingA2BOnly(); - friend ICCProfile ICCProfileForTestingOvershoot(); friend ICCProfile ICCProfileForLayoutTests(); static const uint64_t test_id_adobe_rgb_; static const uint64_t test_id_color_spin_; static const uint64_t test_id_generic_rgb_; static const uint64_t test_id_srgb_; - static const uint64_t test_id_no_analytic_tr_fn_; - static const uint64_t test_id_a2b_only_; - static const uint64_t test_id_overshoot_; // Populate |icc_profile| with the ICCProfile corresponding to id |id|. Return // false if |id| is not in the cache. static bool FromId(uint64_t id, ICCProfile* icc_profile); // This method is used to hard-code the |id_| to a specific value, and is - // used by test methods to ensure that they don't conflict with the values - // generated in the browser. + // used by layout test methods to ensure that they don't conflict with the + // values generated in the browser. static ICCProfile FromDataWithId(const void* icc_profile, size_t size, uint64_t id); + static AnalyzeResult ExtractColorSpaces( + const std::vector<char>& data, + gfx::ColorSpace* parametric_color_space, + float* parametric_tr_fn_max_error, + sk_sp<SkColorSpace>* useable_sk_color_space); + void ComputeColorSpaceAndCache(); // This globally identifies this ICC profile. It is used to look up this ICC @@ -111,6 +128,9 @@ class COLOR_SPACE_EXPORT ICCProfile { uint64_t id_ = 0; std::vector<char> data_; + // The result of attepting to extract a color space from the color profile. + AnalyzeResult analyze_result_ = kICCFailedToParse; + // |color_space| always links back to this ICC profile, and its SkColorSpace // is always equal to the SkColorSpace created from this ICCProfile. gfx::ColorSpace color_space_; @@ -119,8 +139,10 @@ class COLOR_SPACE_EXPORT ICCProfile { // is accurate, and its SkColorSpace will always be parametrically created. gfx::ColorSpace parametric_color_space_; - // This is set to true if SkICC successfully parsed this profile. - bool successfully_parsed_by_sk_icc_ = false; + // The L-infinity error of the parametric color space fit. This is undefined + // unless |analyze_result_| is kICCFailedToApproximateTrFnAccurately or + // kICCExtractedMatrixAndApproximatedTrFn. + float parametric_tr_fn_error_ = -1; FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, BT709toSRGBICC); FRIEND_TEST_ALL_PREFIXES(SimpleColorSpace, GetColorSpace); diff --git a/chromium/ui/gfx/icc_profile_unittest.cc b/chromium/ui/gfx/icc_profile_unittest.cc index e1222d5a3af..d9a008d71cd 100644 --- a/chromium/ui/gfx/icc_profile_unittest.cc +++ b/chromium/ui/gfx/icc_profile_unittest.cc @@ -122,6 +122,9 @@ TEST(ICCProfile, ParametricVersusExact) { // as invalid. ICCProfile a2b = ICCProfileForTestingA2BOnly(); EXPECT_FALSE(a2b.GetColorSpace().IsValid()); + + // Even though it is invalid, it should not be equal to the empty constructor. + EXPECT_NE(a2b, gfx::ICCProfile()); } TEST(ICCProfile, GarbageData) { @@ -158,4 +161,97 @@ TEST(ICCProfile, GenericRGB) { EXPECT_TRUE(SkMatrixIsApproximatelyIdentity(eye)); } +// This tests the ICCProfile MRU cache. This cache is sloppy and should be +// rewritten, now that some of the original design constraints have been lifted. +// This test exists only to ensure that we are aware of behavior changes, not to +// enforce that behavior does not change. +// https://crbug.com/766736 +TEST(ICCProfile, ExhaustCache) { + // Get an ICCProfile that can't be parametrically approximated. + ICCProfile original = ICCProfileForTestingNoAnalyticTrFn(); + ColorSpace original_color_space_0 = original.GetColorSpace(); + + // Recover the ICCProfile from its GetColorSpace. Recovery should succeed, and + // the ICCProfiles should be equal. + ICCProfile recovered_0; + EXPECT_TRUE(original_color_space_0.GetICCProfile(&recovered_0)); + EXPECT_EQ(original, recovered_0); + + // The GetColorSpace of the recovered version should match the original. + ColorSpace recovered_0_color_space = recovered_0.GetColorSpace(); + EXPECT_EQ(recovered_0_color_space, original_color_space_0); + + // Create an identical ICCProfile to the original. It should equal the + // original, and its GetColorSpace should equal the original. + ICCProfile identical_0 = ICCProfileForTestingNoAnalyticTrFn(); + EXPECT_EQ(original, identical_0); + ColorSpace identical_color_space_0 = identical_0.GetColorSpace(); + EXPECT_EQ(identical_color_space_0, original_color_space_0); + + // Create 128 distinct ICC profiles. This will destroy the cached + // ICCProfile<->ColorSpace mapping. + for (size_t i = 0; i < 128; ++i) { + SkMatrix44 toXYZD50; + ColorSpace::CreateSRGB().GetPrimaryMatrix(&toXYZD50); + SkColorSpaceTransferFn fn; + fn.fA = 1; + fn.fB = 0; + fn.fC = 1; + fn.fD = 0; + fn.fE = 0; + fn.fF = 0; + fn.fG = 1.5f + i / 128.f; + ColorSpace color_space = ColorSpace::CreateCustom(toXYZD50, fn); + ICCProfile icc_profile; + color_space.GetICCProfile(&icc_profile); + } + + // Recover the original ICCProfile from its GetColorSpace. Recovery should + // fail, because it has been pushed out of the cache. + ICCProfile recovered_1; + EXPECT_FALSE(original_color_space_0.GetICCProfile(&recovered_1)); + EXPECT_NE(original, recovered_1); + + // Create an identical ICCProfile to the original. It should equal the + // original, because the comparison is based on data. + ICCProfile identical_1 = ICCProfileForTestingNoAnalyticTrFn(); + EXPECT_EQ(original, identical_1); + + // The identical ICCProfile's GetColorSpace will not match, because the + // original points to the now-uncached version. + ColorSpace identical_1_color_space = identical_1.GetColorSpace(); + EXPECT_NE(identical_1_color_space, original_color_space_0); + + // The original ICCProfile is now orphaned because there exists a new entry + // with the same data. + ColorSpace original_color_space_2 = original.GetColorSpace(); + ICCProfile recovered_2; + EXPECT_FALSE(original_color_space_2.GetICCProfile(&recovered_2)); + EXPECT_NE(original, recovered_2); + + // Blow away the cache one more time. + for (size_t i = 0; i < 128; ++i) { + SkMatrix44 toXYZD50; + ColorSpace::CreateSRGB().GetPrimaryMatrix(&toXYZD50); + SkColorSpaceTransferFn fn; + fn.fA = 1; + fn.fB = 0; + fn.fC = 1; + fn.fD = 0; + fn.fE = 0; + fn.fF = 0; + fn.fG = 1.5f + i / 128.f; + ColorSpace color_space = ColorSpace::CreateCustom(toXYZD50, fn); + ICCProfile icc_profile; + color_space.GetICCProfile(&icc_profile); + } + + // Now the original ICCProfile can re-insert itself into the cache, with its + // original id. + ColorSpace original_color_space_3 = original.GetColorSpace(); + ICCProfile recovered_3; + EXPECT_TRUE(original_color_space_3.GetICCProfile(&recovered_3)); + EXPECT_EQ(original, recovered_3); +} + } // namespace gfx diff --git a/chromium/ui/gfx/image/canvas_image_source.h b/chromium/ui/gfx/image/canvas_image_source.h index 1108f0d7c6d..1b1846635c9 100644 --- a/chromium/ui/gfx/image/canvas_image_source.h +++ b/chromium/ui/gfx/image/canvas_image_source.h @@ -5,6 +5,8 @@ #ifndef UI_GFX_IMAGE_CANVAS_IMAGE_SOURCE_H_ #define UI_GFX_IMAGE_CANVAS_IMAGE_SOURCE_H_ +#include <utility> + #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -30,7 +32,7 @@ class GFX_EXPORT CanvasImageSource : public gfx::ImageSkiaSource { static ImageSkia MakeImageSkia(Args&&... args) { auto source = base::MakeUnique<T>(std::forward<Args>(args)...); gfx::Size size = source->size(); - return gfx::ImageSkia(source.release(), size); + return gfx::ImageSkia(std::move(source), size); } CanvasImageSource(const gfx::Size& size, bool is_opaque); diff --git a/chromium/ui/gfx/image/image.cc b/chromium/ui/gfx/image/image.cc index b2984fb0f3d..7b49776f9ed 100644 --- a/chromium/ui/gfx/image/image.cc +++ b/chromium/ui/gfx/image/image.cc @@ -5,8 +5,8 @@ #include "ui/gfx/image/image.h" #include <algorithm> -#include <set> #include <utility> +#include <vector> #include "base/logging.h" #include "base/macros.h" @@ -14,13 +14,9 @@ #include "build/build_config.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image_platform.h" #include "ui/gfx/image/image_png_rep.h" #include "ui/gfx/image/image_skia.h" -#include "ui/gfx/image/image_skia_source.h" - -#if !defined(OS_IOS) -#include "ui/gfx/codec/png_codec.h" -#endif #if defined(OS_IOS) #include "base/mac/foundation_util.h" @@ -33,136 +29,14 @@ namespace gfx { -namespace internal { - -#if defined(OS_IOS) -scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage( - UIImage* uiimage); -// Caller takes ownership of the returned UIImage. -UIImage* CreateUIImageFromPNG( - const std::vector<ImagePNGRep>& image_png_reps); -gfx::Size UIImageSize(UIImage* image); -#elif defined(OS_MACOSX) -scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage( - NSImage* nsimage); -// Caller takes ownership of the returned NSImage. -NSImage* NSImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps, - CGColorSpaceRef color_space); -gfx::Size NSImageSize(NSImage* image); -#endif // defined(OS_MACOSX) - -#if defined(OS_IOS) -ImageSkia* ImageSkiaFromPNG( - const std::vector<ImagePNGRep>& image_png_reps); -scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( - const ImageSkia* skia); -#else -// Returns a 16x16 red image to visually show error in decoding PNG. -// Caller takes ownership of returned ImageSkia. -ImageSkia* GetErrorImageSkia() { - SkBitmap bitmap; - bitmap.allocN32Pixels(16, 16); - bitmap.eraseARGB(0xff, 0xff, 0, 0); - return new ImageSkia(ImageSkiaRep(bitmap, 1.0f)); -} - -class PNGImageSource : public ImageSkiaSource { - public: - PNGImageSource() {} - ~PNGImageSource() override {} - - ImageSkiaRep GetImageForScale(float scale) override { - if (image_skia_reps_.empty()) - return ImageSkiaRep(); - - const ImageSkiaRep* rep = NULL; - // gfx::ImageSkia passes one of the resource scale factors. The source - // should return: - // 1) The ImageSkiaRep with the highest scale if all available - // scales are smaller than |scale|. - // 2) The ImageSkiaRep with the smallest one that is larger than |scale|. - for (ImageSkiaRepSet::const_iterator iter = image_skia_reps_.begin(); - iter != image_skia_reps_.end(); ++iter) { - if ((*iter).scale() == scale) - return (*iter); - if (!rep || rep->scale() < (*iter).scale()) - rep = &(*iter); - if (rep->scale() >= scale) - break; - } - return rep ? *rep : ImageSkiaRep(); - } - - const gfx::Size size() const { - return size_; - } - - bool AddPNGData(const ImagePNGRep& png_rep) { - const gfx::ImageSkiaRep rep = ToImageSkiaRep(png_rep); - if (rep.is_null()) - return false; - if (size_.IsEmpty()) - size_ = gfx::Size(rep.GetWidth(), rep.GetHeight()); - image_skia_reps_.insert(rep); - return true; - } +namespace { - static ImageSkiaRep ToImageSkiaRep(const ImagePNGRep& png_rep) { - scoped_refptr<base::RefCountedMemory> raw_data = png_rep.raw_data; - CHECK(raw_data.get()); - SkBitmap bitmap; - if (!PNGCodec::Decode(raw_data->front(), raw_data->size(), - &bitmap)) { - LOG(ERROR) << "Unable to decode PNG for " << png_rep.scale << "."; - return ImageSkiaRep(); - } - return ImageSkiaRep(bitmap, png_rep.scale); - } +using RepresentationMap = + std::map<Image::RepresentationType, std::unique_ptr<internal::ImageRep>>; - private: - struct Compare { - bool operator()(const ImageSkiaRep& rep1, const ImageSkiaRep& rep2) { - return rep1.scale() < rep2.scale(); - } - }; +} // namespace - typedef std::set<ImageSkiaRep, Compare> ImageSkiaRepSet; - ImageSkiaRepSet image_skia_reps_; - gfx::Size size_; - - DISALLOW_COPY_AND_ASSIGN(PNGImageSource); -}; - -ImageSkia* ImageSkiaFromPNG( - const std::vector<ImagePNGRep>& image_png_reps) { - if (image_png_reps.empty()) - return GetErrorImageSkia(); - std::unique_ptr<PNGImageSource> image_source(new PNGImageSource); - - for (size_t i = 0; i < image_png_reps.size(); ++i) { - if (!image_source->AddPNGData(image_png_reps[i])) - return GetErrorImageSkia(); - } - const gfx::Size& size = image_source->size(); - DCHECK(!size.IsEmpty()); - if (size.IsEmpty()) - return GetErrorImageSkia(); - return new ImageSkia(image_source.release(), size); -} - -scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( - const ImageSkia* image_skia) { - ImageSkiaRep image_skia_rep = image_skia->GetRepresentation(1.0f); - - scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes()); - if (image_skia_rep.scale() != 1.0f || - !PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false, - &png_bytes->data())) { - return NULL; - } - return png_bytes; -} -#endif +namespace internal { class ImageRepPNG; class ImageRepSkia; @@ -182,25 +56,41 @@ class ImageRep { virtual ~ImageRep() {} // Cast helpers ("fake RTTI"). - ImageRepPNG* AsImageRepPNG() { + const ImageRepPNG* AsImageRepPNG() const { CHECK_EQ(type_, Image::kImageRepPNG); - return reinterpret_cast<ImageRepPNG*>(this); + return reinterpret_cast<const ImageRepPNG*>(this); + } + ImageRepPNG* AsImageRepPNG() { + return const_cast<ImageRepPNG*>( + static_cast<const ImageRep*>(this)->AsImageRepPNG()); } - ImageRepSkia* AsImageRepSkia() { + const ImageRepSkia* AsImageRepSkia() const { CHECK_EQ(type_, Image::kImageRepSkia); - return reinterpret_cast<ImageRepSkia*>(this); + return reinterpret_cast<const ImageRepSkia*>(this); + } + ImageRepSkia* AsImageRepSkia() { + return const_cast<ImageRepSkia*>( + static_cast<const ImageRep*>(this)->AsImageRepSkia()); } #if defined(OS_IOS) - ImageRepCocoaTouch* AsImageRepCocoaTouch() { + const ImageRepCocoaTouch* AsImageRepCocoaTouch() const { CHECK_EQ(type_, Image::kImageRepCocoaTouch); - return reinterpret_cast<ImageRepCocoaTouch*>(this); + return reinterpret_cast<const ImageRepCocoaTouch*>(this); + } + ImageRepCocoaTouch* AsImageRepCocoaTouch() { + return const_cast<ImageRepCocoaTouch*>( + static_cast<const ImageRep*>(this)->AsImageRepCocoaTouch()); } #elif defined(OS_MACOSX) - ImageRepCocoa* AsImageRepCocoa() { + const ImageRepCocoa* AsImageRepCocoa() const { CHECK_EQ(type_, Image::kImageRepCocoa); - return reinterpret_cast<ImageRepCocoa*>(this); + return reinterpret_cast<const ImageRepCocoa*>(this); + } + ImageRepCocoa* AsImageRepCocoa() { + return const_cast<ImageRepCocoa*>( + static_cast<const ImageRep*>(this)->AsImageRepCocoa()); } #endif @@ -273,6 +163,7 @@ class ImageRepSkia : public ImageRep { gfx::Size Size() const override { return image_->size(); } + const ImageSkia* image() const { return image_.get(); } ImageSkia* image() { return image_.get(); } private: @@ -356,16 +247,51 @@ class ImageStorage : public base::RefCounted<ImageStorage> { { } - Image::RepresentationType default_representation_type() { + Image::RepresentationType default_representation_type() const { + DCHECK(IsOnValidSequence()); return default_representation_type_; } - Image::RepresentationMap& representations() { return representations_; } + + bool HasRepresentation(Image::RepresentationType type) const { + DCHECK(IsOnValidSequence()); + return representations_.count(type) != 0; + } + + size_t RepresentationCount() const { + DCHECK(IsOnValidSequence()); + return representations_.size(); + } + + const ImageRep* GetRepresentation(Image::RepresentationType rep_type, + bool must_exist) const { + DCHECK(IsOnValidSequence()); + RepresentationMap::const_iterator it = representations_.find(rep_type); + if (it == representations_.end()) { + CHECK(!must_exist); + return NULL; + } + return it->second.get(); + } + + const ImageRep* AddRepresentation(std::unique_ptr<ImageRep> rep) const { + DCHECK(IsOnValidSequence()); + Image::RepresentationType type = rep->type(); + auto result = representations_.insert(std::make_pair(type, std::move(rep))); + + // insert should not fail (implies that there was already a representation + // of that type in the map). + CHECK(result.second) << "type was already in map."; + + return result.first->second.get(); + } #if defined(OS_MACOSX) && !defined(OS_IOS) void set_default_representation_color_space(CGColorSpaceRef color_space) { + DCHECK(IsOnValidSequence()); default_representation_color_space_ = color_space; } - CGColorSpaceRef default_representation_color_space() { + CGColorSpaceRef default_representation_color_space() const { + DCHECK(IsOnValidSequence()); return default_representation_color_space_; } #endif // defined(OS_MACOSX) && !defined(OS_IOS) @@ -389,7 +315,7 @@ class ImageStorage : public base::RefCounted<ImageStorage> { // All the representations of an Image. Size will always be at least one, with // more for any converted representations. - Image::RepresentationMap representations_; + mutable RepresentationMap representations_; DISALLOW_COPY_AND_ASSIGN(ImageStorage); }; @@ -443,16 +369,15 @@ Image::Image(NSImage* image) { } #endif -Image::Image(const Image& other) : storage_(other.storage_) { -} +Image::Image(const Image& other) = default; -Image& Image::operator=(const Image& other) { - storage_ = other.storage_; - return *this; -} +Image::Image(Image&& other) = default; -Image::~Image() { -} +Image& Image::operator=(const Image& other) = default; + +Image& Image::operator=(Image&& other) = default; + +Image::~Image() {} // static Image Image::CreateFrom1xBitmap(const SkBitmap& bitmap) { @@ -487,12 +412,12 @@ const SkBitmap* Image::ToSkBitmap() const { } const ImageSkia* Image::ToImageSkia() const { - internal::ImageRep* rep = GetRepresentation(kImageRepSkia, false); + const internal::ImageRep* rep = GetRepresentation(kImageRepSkia, false); if (!rep) { std::unique_ptr<internal::ImageRep> scoped_rep; switch (DefaultRepresentationType()) { case kImageRepPNG: { - internal::ImageRepPNG* png_rep = + const internal::ImageRepPNG* png_rep = GetRepresentation(kImageRepPNG, true)->AsImageRepPNG(); scoped_rep.reset(new internal::ImageRepSkia( internal::ImageSkiaFromPNG(png_rep->image_reps()))); @@ -500,7 +425,7 @@ const ImageSkia* Image::ToImageSkia() const { } #if defined(OS_IOS) case kImageRepCocoaTouch: { - internal::ImageRepCocoaTouch* native_rep = + const internal::ImageRepCocoaTouch* native_rep = GetRepresentation(kImageRepCocoaTouch, true) ->AsImageRepCocoaTouch(); scoped_rep.reset(new internal::ImageRepSkia( @@ -509,7 +434,7 @@ const ImageSkia* Image::ToImageSkia() const { } #elif defined(OS_MACOSX) case kImageRepCocoa: { - internal::ImageRepCocoa* native_rep = + const internal::ImageRepCocoa* native_rep = GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa(); scoped_rep.reset(new internal::ImageRepSkia( new ImageSkia(ImageSkiaFromNSImage(native_rep->image())))); @@ -527,19 +452,19 @@ const ImageSkia* Image::ToImageSkia() const { #if defined(OS_IOS) UIImage* Image::ToUIImage() const { - internal::ImageRep* rep = GetRepresentation(kImageRepCocoaTouch, false); + const internal::ImageRep* rep = GetRepresentation(kImageRepCocoaTouch, false); if (!rep) { std::unique_ptr<internal::ImageRep> scoped_rep; switch (DefaultRepresentationType()) { case kImageRepPNG: { - internal::ImageRepPNG* png_rep = + const internal::ImageRepPNG* png_rep = GetRepresentation(kImageRepPNG, true)->AsImageRepPNG(); scoped_rep.reset(new internal::ImageRepCocoaTouch( internal::CreateUIImageFromPNG(png_rep->image_reps()))); break; } case kImageRepSkia: { - internal::ImageRepSkia* skia_rep = + const internal::ImageRepSkia* skia_rep = GetRepresentation(kImageRepSkia, true)->AsImageRepSkia(); UIImage* image = UIImageFromImageSkia(*skia_rep->image()); base::mac::NSObjectRetain(image); @@ -556,22 +481,22 @@ UIImage* Image::ToUIImage() const { } #elif defined(OS_MACOSX) NSImage* Image::ToNSImage() const { - internal::ImageRep* rep = GetRepresentation(kImageRepCocoa, false); + const internal::ImageRep* rep = GetRepresentation(kImageRepCocoa, false); if (!rep) { std::unique_ptr<internal::ImageRep> scoped_rep; CGColorSpaceRef default_representation_color_space = - storage_->default_representation_color_space(); + storage()->default_representation_color_space(); switch (DefaultRepresentationType()) { case kImageRepPNG: { - internal::ImageRepPNG* png_rep = + const internal::ImageRepPNG* png_rep = GetRepresentation(kImageRepPNG, true)->AsImageRepPNG(); scoped_rep.reset(new internal::ImageRepCocoa(internal::NSImageFromPNG( png_rep->image_reps(), default_representation_color_space))); break; } case kImageRepSkia: { - internal::ImageRepSkia* skia_rep = + const internal::ImageRepSkia* skia_rep = GetRepresentation(kImageRepSkia, true)->AsImageRepSkia(); NSImage* image = NSImageFromImageSkiaWithColorSpace(*skia_rep->image(), default_representation_color_space); @@ -593,7 +518,7 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { if (IsEmpty()) return new base::RefCountedBytes(); - internal::ImageRep* rep = GetRepresentation(kImageRepPNG, false); + const internal::ImageRep* rep = GetRepresentation(kImageRepPNG, false); if (rep) { const std::vector<ImagePNGRep>& image_png_reps = @@ -609,23 +534,22 @@ scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const { switch (DefaultRepresentationType()) { #if defined(OS_IOS) case kImageRepCocoaTouch: { - internal::ImageRepCocoaTouch* cocoa_touch_rep = - GetRepresentation(kImageRepCocoaTouch, true) - ->AsImageRepCocoaTouch(); + const internal::ImageRepCocoaTouch* cocoa_touch_rep = + GetRepresentation(kImageRepCocoaTouch, true)->AsImageRepCocoaTouch(); png_bytes = internal::Get1xPNGBytesFromUIImage( cocoa_touch_rep->image()); break; } #elif defined(OS_MACOSX) case kImageRepCocoa: { - internal::ImageRepCocoa* cocoa_rep = + const internal::ImageRepCocoa* cocoa_rep = GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa(); png_bytes = internal::Get1xPNGBytesFromNSImage(cocoa_rep->image()); break; } #endif case kImageRepSkia: { - internal::ImageRepSkia* skia_rep = + const internal::ImageRepSkia* skia_rep = GetRepresentation(kImageRepSkia, true)->AsImageRepSkia(); png_bytes = internal::Get1xPNGBytesFromImageSkia(skia_rep->image()); break; @@ -697,14 +621,11 @@ NSImage* Image::CopyNSImage() const { #endif bool Image::HasRepresentation(RepresentationType type) const { - return storage_.get() && storage_->representations().count(type) != 0; + return storage() && storage()->HasRepresentation(type); } size_t Image::RepresentationCount() const { - if (!storage_.get()) - return 0; - - return storage_->representations().size(); + return storage() ? storage()->RepresentationCount() : 0; } bool Image::IsEmpty() const { @@ -729,46 +650,28 @@ gfx::Size Image::Size() const { return GetRepresentation(DefaultRepresentationType(), true)->Size(); } -void Image::SwapRepresentations(Image* other) { - storage_.swap(other->storage_); -} - #if defined(OS_MACOSX) && !defined(OS_IOS) void Image::SetSourceColorSpace(CGColorSpaceRef color_space) { - if (storage_.get()) - storage_->set_default_representation_color_space(color_space); + if (storage()) + storage()->set_default_representation_color_space(color_space); } #endif // defined(OS_MACOSX) && !defined(OS_IOS) Image::RepresentationType Image::DefaultRepresentationType() const { - CHECK(storage_.get()); - return storage_->default_representation_type(); + CHECK(storage()); + return storage()->default_representation_type(); } -internal::ImageRep* Image::GetRepresentation( - RepresentationType rep_type, bool must_exist) const { - CHECK(storage_.get()); - RepresentationMap::const_iterator it = - storage_->representations().find(rep_type); - if (it == storage_->representations().end()) { - CHECK(!must_exist); - return NULL; - } - return it->second.get(); +const internal::ImageRep* Image::GetRepresentation(RepresentationType rep_type, + bool must_exist) const { + CHECK(storage()); + return storage()->GetRepresentation(rep_type, must_exist); } -internal::ImageRep* Image::AddRepresentation( +const internal::ImageRep* Image::AddRepresentation( std::unique_ptr<internal::ImageRep> rep) const { - CHECK(storage_.get()); - RepresentationType type = rep->type(); - auto result = - storage_->representations().insert(std::make_pair(type, std::move(rep))); - - // insert should not fail (implies that there was already a representation of - // that type in the map). - CHECK(result.second) << "type was already in map."; - - return result.first->second.get(); + CHECK(storage()); + return storage()->AddRepresentation(std::move(rep)); } } // namespace gfx diff --git a/chromium/ui/gfx/image/image.h b/chromium/ui/gfx/image/image.h index 01344242d93..f0d6cbb0b2f 100644 --- a/chromium/ui/gfx/image/image.h +++ b/chromium/ui/gfx/image/image.h @@ -56,9 +56,6 @@ class GFX_EXPORT Image { kImageRepPNG, }; - using RepresentationMap = - std::map<RepresentationType, std::unique_ptr<internal::ImageRep>>; - // Creates an empty image with no representations. Image(); @@ -87,9 +84,17 @@ class GFX_EXPORT Image { // Initializes a new Image by AddRef()ing |other|'s internal storage. Image(const Image& other); + // Moves a reference from |other| to the new image without changing the + // reference count. + Image(Image&& other); + // Copies a reference to |other|'s storage. Image& operator=(const Image& other); + // Moves a reference from |other|'s storage without changing the reference + // count. + Image& operator=(Image&& other); + // Deletes the image and, if the only owner of the storage, all of its cached // representations. ~Image(); @@ -169,9 +174,6 @@ class GFX_EXPORT Image { int Height() const; gfx::Size Size() const; - // Swaps this image's internal representations with |other|. - void SwapRepresentations(gfx::Image* other); - #if defined(OS_MACOSX) && !defined(OS_IOS) // Set the default representation's color space. This is used for converting // to NSImage. This is used to compensate for PNGCodec not writing or reading @@ -185,15 +187,21 @@ class GFX_EXPORT Image { // Returns the ImageRep of the appropriate type or NULL if there is no // representation of that type (and must_exist is false). - internal::ImageRep* GetRepresentation( - RepresentationType rep_type, bool must_exist) const; + const internal::ImageRep* GetRepresentation(RepresentationType rep_type, + bool must_exist) const; // Stores a representation into the map. A representation of that type must // not already be in the map. Returns a pointer to the representation stored // inside the map. - internal::ImageRep* AddRepresentation( + const internal::ImageRep* AddRepresentation( std::unique_ptr<internal::ImageRep> rep) const; + // Getter should be used internally (unless a handle to the scoped_refptr is + // needed) instead of directly accessing |storage_|, to ensure logical + // constness is upheld. + const internal::ImageStorage* storage() const { return storage_.get(); } + internal::ImageStorage* storage() { return storage_.get(); } + // Internal class that holds all the representations. This allows the Image to // be cheaply copied. scoped_refptr<internal::ImageStorage> storage_; diff --git a/chromium/ui/gfx/image/image_family.cc b/chromium/ui/gfx/image/image_family.cc index b9d28bddd9d..0adc6692336 100644 --- a/chromium/ui/gfx/image/image_family.cc +++ b/chromium/ui/gfx/image/image_family.cc @@ -25,8 +25,17 @@ ImageFamily::const_iterator::const_iterator( ImageFamily::const_iterator::~const_iterator() {} ImageFamily::ImageFamily() {} +ImageFamily::ImageFamily(ImageFamily&& other) = default; ImageFamily::~ImageFamily() {} +ImageFamily& ImageFamily::operator=(ImageFamily&& other) = default; + +ImageFamily ImageFamily::Clone() const { + ImageFamily clone; + clone.map_ = map_; + return clone; +} + void ImageFamily::Add(const gfx::Image& image) { gfx::Size size = image.Size(); if (size.IsEmpty()) { diff --git a/chromium/ui/gfx/image/image_family.h b/chromium/ui/gfx/image/image_family.h index e8920b9a88f..3d0890a425d 100644 --- a/chromium/ui/gfx/image/image_family.h +++ b/chromium/ui/gfx/image/image_family.h @@ -98,8 +98,11 @@ class GFX_EXPORT ImageFamily { }; ImageFamily(); + ImageFamily(ImageFamily&& other); ~ImageFamily(); + ImageFamily& operator=(ImageFamily&& other); + // Gets an iterator to the first image. const_iterator begin() const { return const_iterator(map_.begin()); } // Gets an iterator to one after the last image. @@ -111,6 +114,10 @@ class GFX_EXPORT ImageFamily { // Removes all images from the family. void clear() { return map_.clear(); } + // Creates a shallow copy of the family. The Images inside share their backing + // store with the original Images. + ImageFamily Clone() const; + // Adds an image to the family. If another image is already present at the // same size, it will be overwritten. void Add(const gfx::Image& image); @@ -162,6 +169,12 @@ class GFX_EXPORT ImageFamily { // Map from (aspect ratio, width) to image. std::map<MapKey, gfx::Image> map_; + + // Even though the Images in the family are copyable (reference-counted), the + // family itself should not be implicitly copied, as it would result in a + // shallow clone of the entire map and updates to many reference counts. + // ImageFamily can be explicitly Clone()d, but std::move is preferred. + DISALLOW_COPY_AND_ASSIGN(ImageFamily); }; } // namespace gfx diff --git a/chromium/ui/gfx/image/image_family_unittest.cc b/chromium/ui/gfx/image/image_family_unittest.cc index 88d52981400..e3973d037ac 100644 --- a/chromium/ui/gfx/image/image_family_unittest.cc +++ b/chromium/ui/gfx/image/image_family_unittest.cc @@ -69,6 +69,26 @@ TEST_F(ImageFamilyTest, Clear) { EXPECT_TRUE(image_family_.empty()); } +TEST_F(ImageFamilyTest, MoveConstructor) { + gfx::ImageFamily family(std::move(image_family_)); + EXPECT_TRUE(image_family_.empty()); + EXPECT_FALSE(family.empty()); +} + +TEST_F(ImageFamilyTest, MoveAssignment) { + gfx::ImageFamily family; + EXPECT_TRUE(family.empty()); + family = std::move(image_family_); + EXPECT_TRUE(image_family_.empty()); + EXPECT_FALSE(family.empty()); +} + +TEST_F(ImageFamilyTest, Clone) { + gfx::ImageFamily family = image_family_.Clone(); + EXPECT_FALSE(image_family_.empty()); + EXPECT_FALSE(family.empty()); +} + // Tests iteration over an ImageFamily. TEST_F(ImageFamilyTest, Iteration) { gfx::ImageFamily::const_iterator it = image_family_.begin(); diff --git a/chromium/ui/gfx/image/image_generic.cc b/chromium/ui/gfx/image/image_generic.cc new file mode 100644 index 00000000000..3f424915dc9 --- /dev/null +++ b/chromium/ui/gfx/image/image_generic.cc @@ -0,0 +1,123 @@ +// 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/gfx/image/image_platform.h" + +#include <set> +#include <utility> + +#include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/image/image_skia_source.h" + +namespace gfx { +namespace internal { + +namespace { + +// Returns a 16x16 red image to visually show error in decoding PNG. +// Caller takes ownership of returned ImageSkia. +ImageSkia* GetErrorImageSkia() { + SkBitmap bitmap; + bitmap.allocN32Pixels(16, 16); + bitmap.eraseARGB(0xff, 0xff, 0, 0); + return new ImageSkia(ImageSkiaRep(bitmap, 1.0f)); +} + +class PNGImageSource : public ImageSkiaSource { + public: + PNGImageSource() {} + ~PNGImageSource() override {} + + ImageSkiaRep GetImageForScale(float scale) override { + if (image_skia_reps_.empty()) + return ImageSkiaRep(); + + const ImageSkiaRep* rep = NULL; + // gfx::ImageSkia passes one of the resource scale factors. The source + // should return: + // 1) The ImageSkiaRep with the highest scale if all available + // scales are smaller than |scale|. + // 2) The ImageSkiaRep with the smallest one that is larger than |scale|. + for (ImageSkiaRepSet::const_iterator iter = image_skia_reps_.begin(); + iter != image_skia_reps_.end(); ++iter) { + if ((*iter).scale() == scale) + return (*iter); + if (!rep || rep->scale() < (*iter).scale()) + rep = &(*iter); + if (rep->scale() >= scale) + break; + } + return rep ? *rep : ImageSkiaRep(); + } + + const gfx::Size size() const { return size_; } + + bool AddPNGData(const ImagePNGRep& png_rep) { + const gfx::ImageSkiaRep rep = ToImageSkiaRep(png_rep); + if (rep.is_null()) + return false; + if (size_.IsEmpty()) + size_ = gfx::Size(rep.GetWidth(), rep.GetHeight()); + image_skia_reps_.insert(rep); + return true; + } + + static ImageSkiaRep ToImageSkiaRep(const ImagePNGRep& png_rep) { + scoped_refptr<base::RefCountedMemory> raw_data = png_rep.raw_data; + CHECK(raw_data.get()); + SkBitmap bitmap; + if (!PNGCodec::Decode(raw_data->front(), raw_data->size(), &bitmap)) { + LOG(ERROR) << "Unable to decode PNG for " << png_rep.scale << "."; + return ImageSkiaRep(); + } + return ImageSkiaRep(bitmap, png_rep.scale); + } + + private: + struct Compare { + bool operator()(const ImageSkiaRep& rep1, const ImageSkiaRep& rep2) { + return rep1.scale() < rep2.scale(); + } + }; + + typedef std::set<ImageSkiaRep, Compare> ImageSkiaRepSet; + ImageSkiaRepSet image_skia_reps_; + gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(PNGImageSource); +}; + +} // namespace + +ImageSkia* ImageSkiaFromPNG(const std::vector<ImagePNGRep>& image_png_reps) { + if (image_png_reps.empty()) + return GetErrorImageSkia(); + std::unique_ptr<PNGImageSource> image_source(new PNGImageSource); + + for (size_t i = 0; i < image_png_reps.size(); ++i) { + if (!image_source->AddPNGData(image_png_reps[i])) + return GetErrorImageSkia(); + } + const gfx::Size& size = image_source->size(); + DCHECK(!size.IsEmpty()); + if (size.IsEmpty()) + return GetErrorImageSkia(); + return new ImageSkia(std::move(image_source), size); +} + +scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( + const ImageSkia* image_skia) { + ImageSkiaRep image_skia_rep = image_skia->GetRepresentation(1.0f); + + scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes()); + if (image_skia_rep.scale() != 1.0f || + !PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false, + &png_bytes->data())) { + return NULL; + } + return png_bytes; +} + +} // namespace internal +} // namespace gfx diff --git a/chromium/ui/gfx/image/image_ios.mm b/chromium/ui/gfx/image/image_ios.mm index 9520f32082b..e1c644d1b5f 100644 --- a/chromium/ui/gfx/image/image_ios.mm +++ b/chromium/ui/gfx/image/image_ios.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_platform.h" #include <stddef.h> #import <UIKit/UIKit.h> diff --git a/chromium/ui/gfx/image/image_mac.mm b/chromium/ui/gfx/image/image_mac.mm index 6358a36dc2d..bce798620b3 100644 --- a/chromium/ui/gfx/image/image_mac.mm +++ b/chromium/ui/gfx/image/image_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/gfx/image/image.h" +#include "ui/gfx/image/image_platform.h" #import <AppKit/AppKit.h> #include <stddef.h> diff --git a/chromium/ui/gfx/image/image_platform.h b/chromium/ui/gfx/image/image_platform.h new file mode 100644 index 00000000000..ca49cbebb13 --- /dev/null +++ b/chromium/ui/gfx/image/image_platform.h @@ -0,0 +1,57 @@ +// 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. + +// This file declares platform-specific helper functions in gfx::internal for +// use by image.cc. +// +// The functions are implemented in image_generic.cc (all platforms other than +// iOS), image_ios.mm and image_mac.mm. + +#ifndef UI_GFX_IMAGE_IMAGE_PLATFORM_H_ +#define UI_GFX_IMAGE_IMAGE_PLATFORM_H_ + +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/ref_counted_memory.h" +#include "build/build_config.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image_png_rep.h" +#include "ui/gfx/image/image_skia.h" + +#if defined(OS_IOS) +#include "base/mac/foundation_util.h" +#include "ui/gfx/image/image_skia_util_ios.h" +#elif defined(OS_MACOSX) +#include "base/mac/foundation_util.h" +#include "base/mac/mac_util.h" +#include "ui/gfx/image/image_skia_util_mac.h" +#endif + +namespace gfx { +namespace internal { + +#if defined(OS_IOS) +scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage( + UIImage* uiimage); +// Caller takes ownership of the returned UIImage. +UIImage* CreateUIImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps); +gfx::Size UIImageSize(UIImage* image); +#elif defined(OS_MACOSX) +scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage( + NSImage* nsimage); +// Caller takes ownership of the returned NSImage. +NSImage* NSImageFromPNG(const std::vector<ImagePNGRep>& image_png_reps, + CGColorSpaceRef color_space); +gfx::Size NSImageSize(NSImage* image); +#endif // defined(OS_MACOSX) + +ImageSkia* ImageSkiaFromPNG(const std::vector<ImagePNGRep>& image_png_reps); +scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( + const ImageSkia* image_skia); + +} // namespace internal +} // namespace gfx + +#endif // UI_GFX_IMAGE_IMAGE_PLATFORM_H_ diff --git a/chromium/ui/gfx/image/image_skia.cc b/chromium/ui/gfx/image/image_skia.cc index bfcd9bb04a8..1ceeaf9916e 100644 --- a/chromium/ui/gfx/image/image_skia.cc +++ b/chromium/ui/gfx/image/image_skia.cc @@ -299,9 +299,6 @@ ImageSkia::ImageSkia(std::unique_ptr<ImageSkiaSource> source, float scale) DetachStorageFromSequence(); } -ImageSkia::ImageSkia(ImageSkiaSource* source, const gfx::Size& size) - : ImageSkia(base::WrapUnique(source), size) {} - ImageSkia::ImageSkia(const ImageSkiaRep& image_rep) { Init(image_rep); // No other thread has reference to this, so it's safe to detach the sequence. diff --git a/chromium/ui/gfx/image/image_skia.h b/chromium/ui/gfx/image/image_skia.h index 02f1ae21bcf..aa04e584336 100644 --- a/chromium/ui/gfx/image/image_skia.h +++ b/chromium/ui/gfx/image/image_skia.h @@ -53,10 +53,6 @@ class GFX_EXPORT ImageSkia { // at |scale| and uses its dimensions to calculate the size in DIP. ImageSkia(std::unique_ptr<ImageSkiaSource> source, float scale); - // Deprecated versions of the above constructors. ImageSkia takes ownership of - // |source|. - ImageSkia(ImageSkiaSource* source, const gfx::Size& size); - explicit ImageSkia(const gfx::ImageSkiaRep& image_rep); // Copies a reference to |other|'s storage. diff --git a/chromium/ui/gfx/image/image_skia_operations.cc b/chromium/ui/gfx/image/image_skia_operations.cc index f48d2e1b9c9..df5186a8a79 100644 --- a/chromium/ui/gfx/image/image_skia_operations.cc +++ b/chromium/ui/gfx/image/image_skia_operations.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkClipOp.h" #include "third_party/skia/include/core/SkDrawLooper.h" @@ -489,7 +490,8 @@ ImageSkia ImageSkiaOperations::CreateBlendedImage(const ImageSkia& first, if (first.isNull() || second.isNull()) return ImageSkia(); - return ImageSkia(new BlendingImageSource(first, second, alpha), first.size()); + return ImageSkia(base::MakeUnique<BlendingImageSource>(first, second, alpha), + first.size()); } // static @@ -499,7 +501,8 @@ ImageSkia ImageSkiaOperations::CreateSuperimposedImage( if (first.isNull() || second.isNull()) return ImageSkia(); - return ImageSkia(new SuperimposedImageSource(first, second), first.size()); + return ImageSkia(base::MakeUnique<SuperimposedImageSource>(first, second), + first.size()); } // static @@ -508,7 +511,8 @@ ImageSkia ImageSkiaOperations::CreateTransparentImage(const ImageSkia& image, if (image.isNull()) return ImageSkia(); - return ImageSkia(new TransparentImageSource(image, alpha), image.size()); + return ImageSkia(base::MakeUnique<TransparentImageSource>(image, alpha), + image.size()); } // static @@ -517,7 +521,7 @@ ImageSkia ImageSkiaOperations::CreateMaskedImage(const ImageSkia& rgb, if (rgb.isNull() || alpha.isNull()) return ImageSkia(); - return ImageSkia(new MaskedImageSource(rgb, alpha), rgb.size()); + return ImageSkia(base::MakeUnique<MaskedImageSource>(rgb, alpha), rgb.size()); } // static @@ -527,8 +531,9 @@ ImageSkia ImageSkiaOperations::CreateTiledImage(const ImageSkia& source, if (source.isNull()) return ImageSkia(); - return ImageSkia(new TiledImageSource(source, src_x, src_y, dst_w, dst_h), - gfx::Size(dst_w, dst_h)); + return ImageSkia( + base::MakeUnique<TiledImageSource>(source, src_x, src_y, dst_w, dst_h), + gfx::Size(dst_w, dst_h)); } // static @@ -538,7 +543,8 @@ ImageSkia ImageSkiaOperations::CreateHSLShiftedImage( if (image.isNull()) return ImageSkia(); - return ImageSkia(new HSLImageSource(image, hsl_shift), image.size()); + return ImageSkia(base::MakeUnique<HSLImageSource>(image, hsl_shift), + image.size()); } // static @@ -548,7 +554,8 @@ ImageSkia ImageSkiaOperations::CreateButtonBackground(SkColor color, if (image.isNull() || mask.isNull()) return ImageSkia(); - return ImageSkia(new ButtonImageSource(color, image, mask), mask.size()); + return ImageSkia(base::MakeUnique<ButtonImageSource>(color, image, mask), + mask.size()); } // static @@ -560,8 +567,9 @@ ImageSkia ImageSkiaOperations::ExtractSubset(const ImageSkia& image, return ImageSkia(); } - return ImageSkia(new ExtractSubsetImageSource(image, clipped_bounds), - clipped_bounds.size()); + return ImageSkia( + base::MakeUnique<ExtractSubsetImageSource>(image, clipped_bounds), + clipped_bounds.size()); } // static @@ -572,8 +580,9 @@ ImageSkia ImageSkiaOperations::CreateResizedImage( if (source.isNull()) return ImageSkia(); - return ImageSkia(new ResizeSource(source, method, target_dip_size), - target_dip_size); + return ImageSkia( + base::MakeUnique<ResizeSource>(source, method, target_dip_size), + target_dip_size); } // static @@ -587,7 +596,8 @@ ImageSkia ImageSkiaOperations::CreateImageWithDropShadow( gfx::Size shadow_image_size = source.size(); shadow_image_size.Enlarge(shadow_padding.width(), shadow_padding.height()); - return ImageSkia(new DropShadowSource(source, shadows), shadow_image_size); + return ImageSkia(base::MakeUnique<DropShadowSource>(source, shadows), + shadow_image_size); } // static @@ -595,7 +605,7 @@ ImageSkia ImageSkiaOperations::CreateHorizontalShadow( const std::vector<ShadowValue>& shadows, bool fades_down) { auto* source = new HorizontalShadowSource(shadows, fades_down); - return ImageSkia(source, source->size()); + return ImageSkia(base::WrapUnique(source), source->size()); } // static @@ -605,11 +615,10 @@ ImageSkia ImageSkiaOperations::CreateRotatedImage( if (source.isNull()) return ImageSkia(); - return ImageSkia(new RotatedSource(source, rotation), - SkBitmapOperations::ROTATION_180_CW == rotation ? - source.size() : - gfx::Size(source.height(), source.width())); - + return ImageSkia(base::MakeUnique<RotatedSource>(source, rotation), + SkBitmapOperations::ROTATION_180_CW == rotation + ? source.size() + : gfx::Size(source.height(), source.width())); } // static @@ -621,7 +630,8 @@ ImageSkia ImageSkiaOperations::CreateIconWithBadge(const ImageSkia& icon, if (badge.isNull()) return icon; - return ImageSkia(new IconWithBadgeSource(icon, badge), icon.size()); + return ImageSkia(base::MakeUnique<IconWithBadgeSource>(icon, badge), + icon.size()); } } // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_unittest.cc b/chromium/ui/gfx/image/image_skia_unittest.cc index df9b42dbed3..971325e1ad7 100644 --- a/chromium/ui/gfx/image/image_skia_unittest.cc +++ b/chromium/ui/gfx/image/image_skia_unittest.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/macros.h" +#include "base/memory/ptr_util.h" #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -154,7 +155,7 @@ class ImageSkiaTest : public testing::Test { TEST_F(ImageSkiaTest, FixedSource) { ImageSkiaRep image(Size(100, 200), 0.0f); - ImageSkia image_skia(new FixedSource(image), Size(100, 200)); + ImageSkia image_skia(base::MakeUnique<FixedSource>(image), Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f); @@ -191,7 +192,8 @@ TEST_F(ImageSkiaTest, FixedSource) { TEST_F(ImageSkiaTest, FixedScaledSource) { ImageSkiaRep image(Size(100, 200), 1.0f); - ImageSkia image_skia(new FixedScaleSource(image), Size(100, 200)); + ImageSkia image_skia(base::MakeUnique<FixedScaleSource>(image), + Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f); @@ -219,7 +221,8 @@ TEST_F(ImageSkiaTest, FixedScaledSource) { TEST_F(ImageSkiaTest, FixedUnscaledSource) { ImageSkiaRep image(Size(100, 200), 0.0f); - ImageSkia image_skia(new FixedScaleSource(image), Size(100, 200)); + ImageSkia image_skia(base::MakeUnique<FixedScaleSource>(image), + Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f); @@ -244,7 +247,8 @@ TEST_F(ImageSkiaTest, FixedUnscaledSource) { } TEST_F(ImageSkiaTest, DynamicSource) { - ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200)); + ImageSkia image_skia(base::MakeUnique<DynamicSource>(Size(100, 200)), + Size(100, 200)); EXPECT_EQ(0U, image_skia.image_reps().size()); const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f); EXPECT_EQ(100, result_100p.GetWidth()); @@ -277,7 +281,8 @@ TEST_F(ImageSkiaTest, ManyRepsPerScaleFactor) { const int kSmallIcon2x = 32; const int kLargeIcon1x = 32; - ImageSkia image(new NullSource(), gfx::Size(kSmallIcon1x, kSmallIcon1x)); + ImageSkia image(base::MakeUnique<NullSource>(), + gfx::Size(kSmallIcon1x, kSmallIcon1x)); // Simulate a source which loads images on a delay. Upon // GetImageForScaleFactor, it immediately returns null and starts loading // image reps slowly. @@ -308,7 +313,8 @@ TEST_F(ImageSkiaTest, ManyRepsPerScaleFactor) { } TEST_F(ImageSkiaTest, GetBitmap) { - ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200)); + ImageSkia image_skia(base::MakeUnique<DynamicSource>(Size(100, 200)), + Size(100, 200)); const SkBitmap* bitmap = image_skia.bitmap(); EXPECT_NE(static_cast<SkBitmap*>(NULL), bitmap); EXPECT_FALSE(bitmap->isNull()); @@ -427,7 +433,8 @@ TEST_F(ImageSkiaTest, StaticOnThreadTest) { } TEST_F(ImageSkiaTest, SourceOnThreadTest) { - ImageSkia image(new DynamicSource(Size(100, 200)), Size(100, 200)); + ImageSkia image(base::MakeUnique<DynamicSource>(Size(100, 200)), + Size(100, 200)); EXPECT_FALSE(image.IsThreadSafe()); test::TestOnThread image_on_thread(&image); @@ -507,7 +514,7 @@ std::vector<float> GetSortedScaleFactors(const gfx::ImageSkia& image) { TEST_F(ImageSkiaTest, ArbitraryScaleFactor) { // source is owned by |image| DynamicSource* source = new DynamicSource(Size(100, 200)); - ImageSkia image(source, gfx::Size(100, 200)); + ImageSkia image(base::WrapUnique(source), gfx::Size(100, 200)); image.GetRepresentation(1.5f); EXPECT_EQ(2.0f, source->GetLastRequestedScaleAndReset()); @@ -579,8 +586,9 @@ TEST_F(ImageSkiaTest, ArbitraryScaleFactor) { } TEST_F(ImageSkiaTest, ArbitraryScaleFactorWithMissingResource) { - ImageSkia image(new FixedScaleSource( - ImageSkiaRep(Size(100, 200), 1.0f)), Size(100, 200)); + ImageSkia image( + base::MakeUnique<FixedScaleSource>(ImageSkiaRep(Size(100, 200), 1.0f)), + Size(100, 200)); // Requesting 1.5f -- falls back to 2.0f, but couldn't find. It should // look up 1.0f and then rescale it. Note that the rescaled ImageSkiaRep will @@ -594,8 +602,9 @@ TEST_F(ImageSkiaTest, ArbitraryScaleFactorWithMissingResource) { TEST_F(ImageSkiaTest, UnscaledImageForArbitraryScaleFactor) { // 0.0f means unscaled. - ImageSkia image(new FixedScaleSource( - ImageSkiaRep(Size(100, 200), 0.0f)), Size(100, 200)); + ImageSkia image( + base::MakeUnique<FixedScaleSource>(ImageSkiaRep(Size(100, 200), 0.0f)), + Size(100, 200)); // Requesting 2.0f, which should return 1.0f unscaled image. const ImageSkiaRep& rep = image.GetRepresentation(2.0f); diff --git a/chromium/ui/gfx/image/image_unittest.cc b/chromium/ui/gfx/image/image_unittest.cc index 317e59830fd..1e0ccd3ac93 100644 --- a/chromium/ui/gfx/image/image_unittest.cc +++ b/chromium/ui/gfx/image/image_unittest.cc @@ -50,26 +50,6 @@ TEST_F(ImageTest, EmptyImage) { EXPECT_TRUE(image.IsEmpty()); EXPECT_EQ(0, image.Width()); EXPECT_EQ(0, image.Height()); - - // Test the copy constructor. - gfx::Image imageCopy(image); - EXPECT_TRUE(imageCopy.IsEmpty()); - EXPECT_EQ(0, imageCopy.Width()); - EXPECT_EQ(0, imageCopy.Height()); - - // Test calling SwapRepresentations() with an empty image. - gfx::Image image2(gt::CreateImageSkia(25, 25)); - EXPECT_FALSE(image2.IsEmpty()); - EXPECT_EQ(25, image2.Width()); - EXPECT_EQ(25, image2.Height()); - - image.SwapRepresentations(&image2); - EXPECT_FALSE(image.IsEmpty()); - EXPECT_EQ(25, image.Width()); - EXPECT_EQ(25, image.Height()); - EXPECT_TRUE(image2.IsEmpty()); - EXPECT_EQ(0, image2.Width()); - EXPECT_EQ(0, image2.Height()); } // Test constructing a gfx::Image from an empty PlatformImage. @@ -590,61 +570,72 @@ TEST_F(ImageTest, SkBitmapConversionPreservesTransparency) { } } -TEST_F(ImageTest, SwapRepresentations) { +TEST_F(ImageTest, Copy) { const size_t kRepCount = kUsesSkiaNatively ? 1U : 2U; gfx::Image image1(gt::CreateImageSkia(25, 25)); - const gfx::ImageSkia* image_skia1 = image1.ToImageSkia(); + EXPECT_EQ(25, image1.Width()); + EXPECT_EQ(25, image1.Height()); + gfx::Image image2(image1); + EXPECT_EQ(25, image2.Width()); + EXPECT_EQ(25, image2.Height()); + EXPECT_EQ(1U, image1.RepresentationCount()); + EXPECT_EQ(1U, image2.RepresentationCount()); + EXPECT_EQ(image1.ToImageSkia(), image2.ToImageSkia()); - gfx::Image image2(gt::CreatePlatformImage()); - const gfx::ImageSkia* image_skia2 = image2.ToImageSkia(); - gt::PlatformImage platform_image = gt::ToPlatformType(image2); + EXPECT_TRUE(gt::IsPlatformImageValid(gt::ToPlatformType(image2))); EXPECT_EQ(kRepCount, image2.RepresentationCount()); + EXPECT_EQ(kRepCount, image1.RepresentationCount()); +} - image1.SwapRepresentations(&image2); +TEST_F(ImageTest, Assign) { + gfx::Image image1(gt::CreatePlatformImage()); + EXPECT_EQ(25, image1.Width()); + EXPECT_EQ(25, image1.Height()); + // Assignment must be on a separate line to the declaration in order to test + // assignment operator (instead of copy constructor). + gfx::Image image2; + image2 = image1; + EXPECT_EQ(25, image2.Width()); + EXPECT_EQ(25, image2.Height()); - EXPECT_EQ(image_skia2, image1.ToImageSkia()); - EXPECT_TRUE(gt::PlatformImagesEqual(platform_image, - gt::ToPlatformType(image1))); - EXPECT_EQ(image_skia1, image2.ToImageSkia()); - EXPECT_EQ(kRepCount, image1.RepresentationCount()); + EXPECT_EQ(1U, image1.RepresentationCount()); EXPECT_EQ(1U, image2.RepresentationCount()); + EXPECT_EQ(image1.ToSkBitmap(), image2.ToSkBitmap()); } -TEST_F(ImageTest, Copy) { +TEST_F(ImageTest, Move) { const size_t kRepCount = kUsesSkiaNatively ? 1U : 2U; gfx::Image image1(gt::CreateImageSkia(25, 25)); EXPECT_EQ(25, image1.Width()); EXPECT_EQ(25, image1.Height()); - gfx::Image image2(image1); + gfx::Image image2(std::move(image1)); EXPECT_EQ(25, image2.Width()); EXPECT_EQ(25, image2.Height()); - EXPECT_EQ(1U, image1.RepresentationCount()); + EXPECT_EQ(0U, image1.RepresentationCount()); EXPECT_EQ(1U, image2.RepresentationCount()); - EXPECT_EQ(image1.ToImageSkia(), image2.ToImageSkia()); EXPECT_TRUE(gt::IsPlatformImageValid(gt::ToPlatformType(image2))); + EXPECT_EQ(0U, image1.RepresentationCount()); EXPECT_EQ(kRepCount, image2.RepresentationCount()); - EXPECT_EQ(kRepCount, image1.RepresentationCount()); } -TEST_F(ImageTest, Assign) { +TEST_F(ImageTest, MoveAssign) { gfx::Image image1(gt::CreatePlatformImage()); EXPECT_EQ(25, image1.Width()); EXPECT_EQ(25, image1.Height()); // Assignment must be on a separate line to the declaration in order to test - // assignment operator (instead of copy constructor). + // move assignment operator (instead of move constructor). gfx::Image image2; - image2 = image1; + image2 = std::move(image1); EXPECT_EQ(25, image2.Width()); EXPECT_EQ(25, image2.Height()); - EXPECT_EQ(1U, image1.RepresentationCount()); + EXPECT_EQ(0U, image1.RepresentationCount()); EXPECT_EQ(1U, image2.RepresentationCount()); - EXPECT_EQ(image1.ToSkBitmap(), image2.ToSkBitmap()); } TEST_F(ImageTest, MultiResolutionImageSkia) { diff --git a/chromium/ui/gfx/image/mojo/image_skia_struct_traits.cc b/chromium/ui/gfx/image/mojo/image_skia_struct_traits.cc index 98ef823b972..cb9487fee78 100644 --- a/chromium/ui/gfx/image/mojo/image_skia_struct_traits.cc +++ b/chromium/ui/gfx/image/mojo/image_skia_struct_traits.cc @@ -107,32 +107,12 @@ bool StructTraits<gfx::mojom::ImageSkiaRepDataView, gfx::ImageSkiaRep>::Read( } // static -void* StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia>::SetUpContext( +std::vector<gfx::ImageSkiaRep> +StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia>::image_reps( const gfx::ImageSkia& input) { // Trigger the image to load everything. input.EnsureRepsForSupportedScales(); - - // Use a context to return a stable list of ImageSkiaRep objects. That is, - // multiple calls of image_reps() should return exactly the same list of - // ImageSkiaRep objects. So that ImageSkiaRep with the same backing pixel - // buffer is properly serialized and only once. - return new std::vector<gfx::ImageSkiaRep>(input.image_reps()); -} - -// static -void StructTraits<gfx::mojom::ImageSkiaDataView, - gfx::ImageSkia>::TearDownContext(const gfx::ImageSkia& input, - void* context) { - delete static_cast<std::vector<gfx::ImageSkiaRep>*>(context); -} - -// static -const std::vector<gfx::ImageSkiaRep>& -StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia>::image_reps( - const gfx::ImageSkia& input, - void* context) { - // See the comment in SetUpContext regarding context usage. - return *(static_cast<std::vector<gfx::ImageSkiaRep>*>(context)); + return input.image_reps(); } // static diff --git a/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h b/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h index 974ec9cf1d7..401b282789e 100644 --- a/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h +++ b/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h @@ -56,15 +56,9 @@ struct StructTraits<gfx::mojom::ImageSkiaRepDataView, gfx::ImageSkiaRep> { template <> struct StructTraits<gfx::mojom::ImageSkiaDataView, gfx::ImageSkia> { - static void* SetUpContext(const gfx::ImageSkia& input); - static void TearDownContext(const gfx::ImageSkia& input, void* context); - static const std::vector<gfx::ImageSkiaRep>& image_reps( - const gfx::ImageSkia& input, - void* context); + static std::vector<gfx::ImageSkiaRep> image_reps(const gfx::ImageSkia& input); - static bool IsNull(const gfx::ImageSkia& input) { - return input.image_reps().empty(); - } + static bool IsNull(const gfx::ImageSkia& input) { return input.isNull(); } static void SetToNull(gfx::ImageSkia* out) { *out = gfx::ImageSkia(); } static bool Read(gfx::mojom::ImageSkiaDataView data, gfx::ImageSkia* out); diff --git a/chromium/ui/gfx/image/mojo/image_traits_unittest.cc b/chromium/ui/gfx/image/mojo/image_traits_unittest.cc index 6f35c10289e..086790a35cf 100644 --- a/chromium/ui/gfx/image/mojo/image_traits_unittest.cc +++ b/chromium/ui/gfx/image/mojo/image_traits_unittest.cc @@ -6,6 +6,7 @@ #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" @@ -130,20 +131,22 @@ TEST_F(ImageTraitsTest, NullImageSkia) { EXPECT_TRUE(output.isNull()); } -TEST_F(ImageTraitsTest, ImageSkiaWithNoRepsTreatedAsNull) { +TEST_F(ImageTraitsTest, ImageSkiaRepsAreCreatedAsNeeded) { const gfx::Size kSize(1, 2); - ImageSkia image(new TestImageSkiaSource(kSize), kSize); - ASSERT_FALSE(image.isNull()); + ImageSkia image(base::MakeUnique<TestImageSkiaSource>(kSize), kSize); + EXPECT_FALSE(image.isNull()); + EXPECT_TRUE(image.image_reps().empty()); - ImageSkia output(ImageSkiaRep(gfx::Size(1, 1), 1.0f)); - ASSERT_FALSE(output.isNull()); - service()->EchoImageSkia(image, &output); + ImageSkia output; EXPECT_TRUE(output.isNull()); + service()->EchoImageSkia(image, &output); + EXPECT_FALSE(image.image_reps().empty()); + EXPECT_FALSE(output.isNull()); } TEST_F(ImageTraitsTest, ImageSkia) { const gfx::Size kSize(1, 2); - ImageSkia image(new TestImageSkiaSource(kSize), kSize); + ImageSkia image(base::MakeUnique<TestImageSkiaSource>(kSize), kSize); image.GetRepresentation(1.0f); image.GetRepresentation(2.0f); @@ -155,7 +158,7 @@ TEST_F(ImageTraitsTest, ImageSkia) { TEST_F(ImageTraitsTest, EmptyRepPreserved) { const gfx::Size kSize(1, 2); - ImageSkia image(new TestImageSkiaSource(kSize), kSize); + ImageSkia image(base::MakeUnique<TestImageSkiaSource>(kSize), kSize); image.GetRepresentation(1.0f); SkBitmap empty_bitmap; @@ -170,7 +173,7 @@ TEST_F(ImageTraitsTest, EmptyRepPreserved) { TEST_F(ImageTraitsTest, ImageSkiaWithOperations) { const gfx::Size kSize(32, 32); - ImageSkia image(new TestImageSkiaSource(kSize), kSize); + ImageSkia image(base::MakeUnique<TestImageSkiaSource>(kSize), kSize); const gfx::Size kNewSize(16, 16); ImageSkia resized = ImageSkiaOperations::CreateResizedImage( 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 a91e50645fe..e5d11bb830b 100644 --- a/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc +++ b/chromium/ui/gfx/linux/client_native_pixmap_factory_dmabuf.cc @@ -68,6 +68,8 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { format == gfx::BufferFormat::BGRA_8888 || format == gfx::BufferFormat::RGBX_8888 || format == gfx::BufferFormat::RGBA_8888; + case gfx::BufferUsage::SCANOUT_VDA_WRITE: + return false; case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: { #if defined(OS_CHROMEOS) @@ -83,6 +85,18 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { return false; #endif } + case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: { +#if defined(OS_CHROMEOS) + // Each platform only supports one camera buffer type. We list the + // supported buffer formats on all platforms here. When allocating a + // camera buffer the caller is responsible for making sure a buffer is + // successfully allocated. For example, allocating YUV420_BIPLANAR + // for SCANOUT_CAMERA_READ_WRITE may only work on Intel boards. + return format == gfx::BufferFormat::YUV_420_BIPLANAR; +#else + return false; +#endif + } } NOTREACHED(); return false; @@ -96,6 +110,7 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: + case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: #if defined(OS_CHROMEOS) return ClientNativePixmapDmaBuf::ImportFromDmabuf(handle, size); #else @@ -104,6 +119,7 @@ class ClientNativePixmapFactoryDmabuf : public ClientNativePixmapFactory { #endif case gfx::BufferUsage::GPU_READ: case gfx::BufferUsage::SCANOUT: + case gfx::BufferUsage::SCANOUT_VDA_WRITE: // Close all the fds. for (const auto& fd : handle.fds) base::ScopedFD scoped_fd(fd.fd); diff --git a/chromium/ui/gfx/mojo/BUILD.gn b/chromium/ui/gfx/mojo/BUILD.gn index 9722968cbd9..b71ee2532d3 100644 --- a/chromium/ui/gfx/mojo/BUILD.gn +++ b/chromium/ui/gfx/mojo/BUILD.gn @@ -29,15 +29,3 @@ mojom("test_interfaces") { ":mojo", ] } - -source_set("struct_traits") { - sources = [ - "selection_bound_struct_traits.h", - "transform_struct_traits.h", - ] - public_deps = [ - ":mojo_shared_cpp_sources", - "//mojo/public/cpp/bindings", - "//ui/gfx", - ] -} diff --git a/chromium/ui/gfx/mojo/buffer_types.mojom b/chromium/ui/gfx/mojo/buffer_types.mojom index 29c50d54179..79b22417a02 100644 --- a/chromium/ui/gfx/mojo/buffer_types.mojom +++ b/chromium/ui/gfx/mojo/buffer_types.mojom @@ -32,7 +32,9 @@ enum BufferFormat { enum BufferUsage { GPU_READ, SCANOUT, + SCANOUT_CAMERA_READ_WRITE, SCANOUT_CPU_READ_WRITE, + SCANOUT_VDA_WRITE, GPU_READ_CPU_READ_WRITE, GPU_READ_CPU_READ_WRITE_PERSISTENT, diff --git a/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc b/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc index 186e2eb5502..e9aea16a63c 100644 --- a/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc +++ b/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc @@ -8,30 +8,17 @@ namespace mojo { -void* StructTraits<gfx::mojom::NativePixmapHandleDataView, - gfx::NativePixmapHandle>:: - SetUpContext(const gfx::NativePixmapHandle& pixmap_handle) { - auto* handles = new std::vector<mojo::ScopedHandle>(); +std::vector<mojo::ScopedHandle> +StructTraits<gfx::mojom::NativePixmapHandleDataView, gfx::NativePixmapHandle>:: + fds(const gfx::NativePixmapHandle& pixmap_handle) { + std::vector<mojo::ScopedHandle> handles; #if defined(OS_LINUX) for (const base::FileDescriptor& fd : pixmap_handle.fds) - handles->emplace_back(mojo::WrapPlatformFile(fd.fd)); + handles.emplace_back(mojo::WrapPlatformFile(fd.fd)); #endif // defined(OS_LINUX) return handles; } -void StructTraits<gfx::mojom::NativePixmapHandleDataView, - gfx::NativePixmapHandle>:: - TearDownContext(const gfx::NativePixmapHandle& handle, void* context) { - delete static_cast<std::vector<mojo::ScopedHandle>*>(context); -} - -std::vector<mojo::ScopedHandle>& StructTraits< - gfx::mojom::NativePixmapHandleDataView, - gfx::NativePixmapHandle>::fds(const gfx::NativePixmapHandle& pixmap_handle, - void* context) { - return *static_cast<std::vector<mojo::ScopedHandle>*>(context); -} - bool StructTraits< gfx::mojom::NativePixmapHandleDataView, gfx::NativePixmapHandle>::Read(gfx::mojom::NativePixmapHandleDataView data, diff --git a/chromium/ui/gfx/mojo/buffer_types_struct_traits.h b/chromium/ui/gfx/mojo/buffer_types_struct_traits.h index 0fdf909f5fc..368d499c43d 100644 --- a/chromium/ui/gfx/mojo/buffer_types_struct_traits.h +++ b/chromium/ui/gfx/mojo/buffer_types_struct_traits.h @@ -126,8 +126,12 @@ struct EnumTraits<gfx::mojom::BufferUsage, gfx::BufferUsage> { return gfx::mojom::BufferUsage::GPU_READ; case gfx::BufferUsage::SCANOUT: return gfx::mojom::BufferUsage::SCANOUT; + case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: + return gfx::mojom::BufferUsage::SCANOUT_CAMERA_READ_WRITE; case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE: return gfx::mojom::BufferUsage::SCANOUT_CPU_READ_WRITE; + case gfx::BufferUsage::SCANOUT_VDA_WRITE: + return gfx::mojom::BufferUsage::SCANOUT_VDA_WRITE; case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: return gfx::mojom::BufferUsage::GPU_READ_CPU_READ_WRITE; case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: @@ -145,9 +149,15 @@ struct EnumTraits<gfx::mojom::BufferUsage, gfx::BufferUsage> { case gfx::mojom::BufferUsage::SCANOUT: *out = gfx::BufferUsage::SCANOUT; return true; + case gfx::mojom::BufferUsage::SCANOUT_CAMERA_READ_WRITE: + *out = gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE; + return true; case gfx::mojom::BufferUsage::SCANOUT_CPU_READ_WRITE: *out = gfx::BufferUsage::SCANOUT_CPU_READ_WRITE; return true; + case gfx::mojom::BufferUsage::SCANOUT_VDA_WRITE: + *out = gfx::BufferUsage::SCANOUT_VDA_WRITE; + return true; case gfx::mojom::BufferUsage::GPU_READ_CPU_READ_WRITE: *out = gfx::BufferUsage::GPU_READ_CPU_READ_WRITE; return true; @@ -239,10 +249,6 @@ struct StructTraits<gfx::mojom::NativePixmapPlaneDataView, template <> struct StructTraits<gfx::mojom::NativePixmapHandleDataView, gfx::NativePixmapHandle> { - static void* SetUpContext(const gfx::NativePixmapHandle& handle); - static void TearDownContext(const gfx::NativePixmapHandle& handle, - void* context); - static bool IsNull(const gfx::NativePixmapHandle& handle) { #if defined(OS_LINUX) return false; @@ -251,9 +257,8 @@ struct StructTraits<gfx::mojom::NativePixmapHandleDataView, return true; #endif } - static std::vector<mojo::ScopedHandle>& fds( - const gfx::NativePixmapHandle& pixmap_handle, - void* context); + static std::vector<mojo::ScopedHandle> fds( + const gfx::NativePixmapHandle& pixmap_handle); static const std::vector<gfx::NativePixmapPlane>& planes( const gfx::NativePixmapHandle& pixmap_handle) { diff --git a/chromium/ui/gfx/mojo/selection_bound.typemap b/chromium/ui/gfx/mojo/selection_bound.typemap index d188a3a234a..e2b16f7a264 100644 --- a/chromium/ui/gfx/mojo/selection_bound.typemap +++ b/chromium/ui/gfx/mojo/selection_bound.typemap @@ -5,7 +5,4 @@ mojom = "//ui/gfx/mojo/selection_bound.mojom" public_headers = [ "//ui/gfx/selection_bound.h" ] traits_headers = [ "//ui/gfx/mojo/selection_bound_struct_traits.h" ] -deps = [ - "//ui/gfx/mojo:struct_traits", -] type_mappings = [ "gfx.mojom.SelectionBound=gfx::SelectionBound" ] diff --git a/chromium/ui/gfx/mojo/transform.typemap b/chromium/ui/gfx/mojo/transform.typemap index 28da3090487..b675c8f1df4 100644 --- a/chromium/ui/gfx/mojo/transform.typemap +++ b/chromium/ui/gfx/mojo/transform.typemap @@ -5,7 +5,4 @@ mojom = "//ui/gfx/mojo/transform.mojom" public_headers = [ "//ui/gfx/transform.h" ] traits_headers = [ "//ui/gfx/mojo/transform_struct_traits.h" ] -deps = [ - "//ui/gfx/mojo:struct_traits", -] type_mappings = [ "gfx.mojom.Transform=gfx::Transform" ] diff --git a/chromium/ui/gfx/native_widget_types.h b/chromium/ui/gfx/native_widget_types.h index fbe0fef01ac..17f9be2b46e 100644 --- a/chromium/ui/gfx/native_widget_types.h +++ b/chromium/ui/gfx/native_widget_types.h @@ -11,7 +11,7 @@ #include "build/build_config.h" #if defined(OS_ANDROID) -#include <jni.h> +#include "base/android/scoped_java_ref.h" #elif defined(OS_MACOSX) #include <objc/objc.h> #endif @@ -104,7 +104,7 @@ class ViewAndroid; #endif class SkBitmap; -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) extern "C" { struct _AtkObject; typedef struct _AtkObject AtkObject; @@ -132,13 +132,7 @@ typedef NSEvent* NativeEvent; typedef void* NativeCursor; typedef ui::ViewAndroid* NativeView; typedef ui::WindowAndroid* NativeWindow; -typedef jobject NativeEvent; -#elif defined(OS_FUCHSIA) -// TODO(fuchsia): Update when we have a plan for UI on Fuchsia. -typedef void* NativeCursor; -typedef void* NativeView; -typedef void* NativeWindow; -typedef void* NativeEvent; +typedef base::android::ScopedJavaGlobalRef<jobject> NativeEvent; #else #error Unknown build environment. #endif @@ -154,7 +148,7 @@ typedef NSFont* NativeFont; typedef id NativeViewAccessible; #else // Android, Linux, Chrome OS, etc. // Linux doesn't have a native font type. -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) typedef AtkObject* NativeViewAccessible; #else typedef void* NativeViewAccessible; @@ -203,10 +197,6 @@ constexpr AcceleratedWidget kNullAcceleratedWidget = 0; #elif defined(USE_OZONE) typedef int32_t AcceleratedWidget; constexpr AcceleratedWidget kNullAcceleratedWidget = 0; -#elif defined(OS_FUCHSIA) -// TODO(fuchsia): Update when we have a plan for UI on Fuchsia. -typedef void* AcceleratedWidget; -constexpr AcceleratedWidget kNullAcceleratedWidget = 0; #else #error unknown platform #endif diff --git a/chromium/ui/gfx/paint_vector_icon.cc b/chromium/ui/gfx/paint_vector_icon.cc index 0c642bf4ec6..97edb15adeb 100644 --- a/chromium/ui/gfx/paint_vector_icon.cc +++ b/chromium/ui/gfx/paint_vector_icon.cc @@ -66,6 +66,7 @@ class PathParser { case V_LINE_TO: case R_V_LINE_TO: case CANVAS_DIMENSIONS: + case PATH_COLOR_ALPHA: return 1; case MOVE_TO: @@ -123,6 +124,7 @@ CommandType CommandFromString(const std::string& source) { return command; RETURN_IF_IS(NEW_PATH); + RETURN_IF_IS(PATH_COLOR_ALPHA); RETURN_IF_IS(PATH_COLOR_ARGB); RETURN_IF_IS(PATH_MODE_CLEAR); RETURN_IF_IS(STROKE); @@ -213,6 +215,10 @@ void PaintPath(Canvas* canvas, case NEW_PATH: break; + case PATH_COLOR_ALPHA: + flags.setAlpha(SkScalarFloorToInt(arg(0))); + break; + case PATH_COLOR_ARGB: flags.setColor(SkColorSetARGB( SkScalarFloorToInt(arg(0)), SkScalarFloorToInt(arg(1)), @@ -392,8 +398,9 @@ void PaintPath(Canvas* canvas, if (elapsed_time >= delay + duration) { state = 1; } else if (elapsed_time > delay) { - state = (elapsed_time - delay).ToInternalValue() / - static_cast<double>(duration.ToInternalValue()); + DCHECK(!duration.is_zero()); + state = (elapsed_time - delay).InMicroseconds() / + static_cast<double>(duration.InMicroseconds()); } auto weight = Tween::CalculateValue( @@ -432,13 +439,15 @@ void PaintPath(Canvas* canvas, previous_command_type = command_type; } - ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, flips_in_rtl); + ScopedCanvas scoped_canvas(canvas); if (dip_size != canvas_size) { SkScalar scale = SkIntToScalar(dip_size) / SkIntToScalar(canvas_size); canvas->sk_canvas()->scale(scale, scale); } + ScopedRTLFlipCanvas scoped_rtl_flip_canvas(canvas, canvas_size, flips_in_rtl); + if (!clip_rect.isEmpty()) canvas->sk_canvas()->clipRect(clip_rect); @@ -497,7 +506,7 @@ class VectorIconCache { if (iter != images_.end()) return iter->second; - ImageSkia icon_image(new VectorIconSource(description), + ImageSkia icon_image(base::MakeUnique<VectorIconSource>(description), Size(description.dip_size, description.dip_size)); images_.insert(std::make_pair(description, icon_image)); return icon_image; diff --git a/chromium/ui/gfx/platform_font_fuchsia.cc b/chromium/ui/gfx/platform_font_fuchsia.cc new file mode 100644 index 00000000000..d1500668323 --- /dev/null +++ b/chromium/ui/gfx/platform_font_fuchsia.cc @@ -0,0 +1,26 @@ +// 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/gfx/platform_font.h" + +#include "base/logging.h" + +namespace gfx { + +// static +PlatformFont* PlatformFont::CreateDefault() { + // TODO(fuchsia): Stubbed during headless bringup, https://crbug.com/743296. + NOTIMPLEMENTED(); + return NULL; +} + +// static +PlatformFont* PlatformFont::CreateFromNameAndSize(const std::string& font_name, + int font_size) { + // TODO(fuchsia): Stubbed during headless bringup, https://crbug.com/743296. + NOTIMPLEMENTED(); + return NULL; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/platform_font_win.cc b/chromium/ui/gfx/platform_font_win.cc index 100ee944050..35f5a212bcd 100644 --- a/chromium/ui/gfx/platform_font_win.cc +++ b/chromium/ui/gfx/platform_font_win.cc @@ -395,6 +395,11 @@ void PlatformFontWin::SetDirectWriteFactory(IDWriteFactory* factory) { } // static +bool PlatformFontWin::IsDirectWriteEnabled() { + return direct_write_factory_ != nullptr; +} + +// static void PlatformFontWin::GetTextMetricsForFont(HDC hdc, HFONT font, TEXTMETRIC* text_metrics) { @@ -465,7 +470,7 @@ PlatformFontWin::HFontRef* PlatformFontWin::CreateHFontRef(HFONT font) { GetTextMetricsForFont(screen_dc, font, &font_metrics); } - if (direct_write_factory_) + if (IsDirectWriteEnabled()) return CreateHFontRefFromSkia(font, font_metrics); return CreateHFontRefFromGDI(font, font_metrics); diff --git a/chromium/ui/gfx/platform_font_win.h b/chromium/ui/gfx/platform_font_win.h index ac4f6640802..5e8e72f4283 100644 --- a/chromium/ui/gfx/platform_font_win.h +++ b/chromium/ui/gfx/platform_font_win.h @@ -71,6 +71,8 @@ class GFX_EXPORT PlatformFontWin : public PlatformFont { // from skia and DirectWrite. static void SetDirectWriteFactory(IDWriteFactory* factory); + static bool IsDirectWriteEnabled(); + // Returns the GDI metrics for the font passed in. static void GetTextMetricsForFont(HDC hdc, HFONT font, diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc index 041c9f27ee4..f8999eb9f61 100644 --- a/chromium/ui/gfx/render_text.cc +++ b/chromium/ui/gfx/render_text.cc @@ -18,6 +18,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "cc/base/math_util.h" #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_shader.h" #include "third_party/icu/source/common/unicode/rbbi.h" @@ -231,8 +232,16 @@ void SkiaTextRenderer::SetShader(sk_sp<cc::PaintShader> shader) { void SkiaTextRenderer::DrawPosText(const SkPoint* pos, const uint16_t* glyphs, size_t glyph_count) { - const size_t byte_length = glyph_count * sizeof(glyphs[0]); - canvas_skia_->drawPosText(&glyphs[0], byte_length, &pos[0], flags_); + SkTextBlobBuilder builder; + const auto& run_buffer = builder.allocRunPos(flags_.ToSkPaint(), glyph_count); + + static_assert(sizeof(*glyphs) == sizeof(*run_buffer.glyphs), ""); + memcpy(run_buffer.glyphs, glyphs, glyph_count * sizeof(*glyphs)); + + static_assert(sizeof(*pos) == 2 * sizeof(*run_buffer.pos), ""); + memcpy(run_buffer.pos, pos, glyph_count * sizeof(*pos)); + + canvas_skia_->drawTextBlob(builder.make(), 0, 0, flags_); } void SkiaTextRenderer::DrawUnderline(int x, int y, int width) { @@ -1424,11 +1433,34 @@ base::string16 RenderText::Elide(const base::string16& text, StringSlicer slicer(text, ellipsis, elide_in_middle, elide_at_beginning); - // Use binary search to compute the elided text. + // Use binary(-like) search to compute the elided text. In particular, do + // an interpolation search, which is a binary search in which each guess + // is an attempt to smartly calculate the right point rather than blindly + // guessing midway between the endpoints. size_t lo = 0; size_t hi = text.length() - 1; + size_t guess = std::string::npos; + // These two widths are not exactly right but they're good enough to provide + // some guidance to the search. For example, |text_width| is actually the + // length of text.length(), not text.length()-1. + float lo_width = 0; + float hi_width = text_width; const base::i18n::TextDirection text_direction = GetTextDirection(text); - for (size_t guess = (lo + hi) / 2; lo <= hi; guess = (lo + hi) / 2) { + while (lo <= hi) { + // Linearly interpolate between |lo| and |hi|, which correspond to widths + // of |lo_width| and |hi_width| to estimate at what position + // |available_width| would be at. Because |lo_width| and |hi_width| are + // both estimates (may be off by a little because, for example, |lo_width| + // may have been calculated from |lo| minus one, not |lo|), we clamp to the + // the valid range. + // |last_guess| is merely used to verify that we're not repeating guesses. + const size_t last_guess = guess; + guess = lo + static_cast<size_t>(ToRoundedInt((available_width - lo_width) * + (hi - lo) / + (hi_width - lo_width))); + guess = cc::MathUtil::ClampToRange(guess, lo, hi); + DCHECK_NE(last_guess, guess); + // Restore colors. They will be truncated to size by SetText. render_text->colors_ = colors_; base::string16 new_text = @@ -1469,11 +1501,15 @@ base::string16 RenderText::Elide(const base::string16& text, break; if (guess_width > available_width) { hi = guess - 1; + hi_width = guess_width; // Move back on the loop terminating condition when the guess is too wide. - if (hi < lo) + if (hi < lo) { lo = hi; + lo_width = guess_width; + } } else { lo = guess + 1; + lo_width = guess_width; } } diff --git a/chromium/ui/gfx/render_text.h b/chromium/ui/gfx/render_text.h index 24ea3d4123d..024c73ed1ac 100644 --- a/chromium/ui/gfx/render_text.h +++ b/chromium/ui/gfx/render_text.h @@ -64,6 +64,7 @@ class GFX_EXPORT SkiaTextRenderer { void SetForegroundColor(SkColor foreground); void SetShader(sk_sp<cc::PaintShader> shader); void DrawSelection(const std::vector<Rect>& selection, SkColor color); + // TODO(vmpstr): Change this API to mimic SkCanvas::drawTextBlob instead. virtual void DrawPosText(const SkPoint* pos, const uint16_t* glyphs, size_t glyph_count); diff --git a/chromium/ui/gfx/render_text_harfbuzz.cc b/chromium/ui/gfx/render_text_harfbuzz.cc index 2f0286217a5..07a4bbd44b0 100644 --- a/chromium/ui/gfx/render_text_harfbuzz.cc +++ b/chromium/ui/gfx/render_text_harfbuzz.cc @@ -15,6 +15,7 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/profiler/scoped_tracker.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/trace_event/trace_event.h" @@ -207,8 +208,9 @@ bool IsNewlineSegment(const base::string16& text, // Helper template function for |TextRunHarfBuzz::GetClusterAt()|. |Iterator| // can be a forward or reverse iterator type depending on the text direction. +// Returns true on success, or false if an error is encountered. template <class Iterator> -void GetClusterAtImpl(size_t pos, +bool GetClusterAtImpl(size_t pos, Range range, Iterator elements_begin, Iterator elements_end, @@ -216,10 +218,14 @@ void GetClusterAtImpl(size_t pos, Range* chars, Range* glyphs) { Iterator element = std::upper_bound(elements_begin, elements_end, pos); + if (element == elements_begin) { + *chars = range; + *glyphs = Range(); + return false; + } + chars->set_end(element == elements_end ? range.end() : *element); glyphs->set_end(reversed ? elements_end - element : element - elements_begin); - - DCHECK(element != elements_begin); while (--element != elements_begin && *element == *(element - 1)); chars->set_start(*element); glyphs->set_start( @@ -231,6 +237,7 @@ void GetClusterAtImpl(size_t pos, DCHECK(!chars->is_empty()); DCHECK(!glyphs->is_reversed()); DCHECK(!glyphs->is_empty()); + return true; } // Returns the line segment index for the |line|, |text_x| pair. |text_x| is @@ -680,24 +687,37 @@ size_t TextRunHarfBuzz::CountMissingGlyphs() const { void TextRunHarfBuzz::GetClusterAt(size_t pos, Range* chars, Range* glyphs) const { - DCHECK(range.Contains(Range(pos, pos + 1))); DCHECK(chars); DCHECK(glyphs); - if (glyph_count == 0) { + bool success = true; + if (glyph_count == 0 || !range.Contains(Range(pos, pos + 1))) { *chars = range; *glyphs = Range(); - return; + success = false; } if (is_rtl) { - GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), glyph_to_char.rend(), - true, chars, glyphs); - return; + success &= GetClusterAtImpl(pos, range, glyph_to_char.rbegin(), + glyph_to_char.rend(), true, chars, glyphs); + } else { + success &= GetClusterAtImpl(pos, range, glyph_to_char.begin(), + glyph_to_char.end(), false, chars, glyphs); } - GetClusterAtImpl(pos, range, glyph_to_char.begin(), glyph_to_char.end(), - false, chars, glyphs); + if (!success) { + std::string glyph_to_char_string; + for (size_t i = 0; i < glyph_count && i < glyph_to_char.size(); ++i) { + glyph_to_char_string += base::SizeTToString(i) + "->" + + base::UintToString(glyph_to_char[i]) + ", "; + } + LOG(ERROR) << " TextRunHarfBuzz error, please report at crbug.com/724880:" + << " range: " << range.ToString() << ", rtl: " << is_rtl << "," + << " level: '" << level << "', script: " << script << "," + << " font: '" << font.GetActualFontNameForTesting() << "'," + << " glyph_count: " << glyph_count << ", pos: " << pos << "," + << " glyph_to_char: " << glyph_to_char_string; + } } RangeF TextRunHarfBuzz::GetGraphemeBounds(RenderTextHarfBuzz* render_text, diff --git a/chromium/ui/gfx/render_text_unittest.cc b/chromium/ui/gfx/render_text_unittest.cc index 72b1673b51b..1eee0004e3f 100644 --- a/chromium/ui/gfx/render_text_unittest.cc +++ b/chromium/ui/gfx/render_text_unittest.cc @@ -24,7 +24,7 @@ #include "base/test/scoped_task_environment.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkFontStyle.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkTypeface.h" #include "ui/gfx/break_list.h" @@ -70,7 +70,7 @@ class RenderTextTestApi { // Callers should ensure that the associated RenderText object is a // RenderTextHarfBuzz instance. - internal::TextRunList* GetHarfBuzzRunList() { + const internal::TextRunList* GetHarfBuzzRunList() const { RenderTextHarfBuzz* render_text = static_cast<RenderTextHarfBuzz*>(render_text_); return render_text->GetRunList(); @@ -428,6 +428,14 @@ class RenderTextTest : public testing::Test, renderer_(canvas()) {} protected: + bool IsWin8Plus() const { +#if defined(OS_WIN) + return base::win::GetVersion() >= base::win::VERSION_WIN8; +#else + return false; +#endif + } + std::unique_ptr<RenderText> CreateRenderTextInstance() const { switch (GetParam()) { case RENDER_TEXT_HARFBUZZ: @@ -450,14 +458,43 @@ class RenderTextTest : public testing::Test, void DrawVisualText() { test_api_->DrawVisualText(renderer()); } - internal::TextRunList* GetHarfBuzzRunList() { + const internal::TextRunList* GetHarfBuzzRunList() const { DCHECK_EQ(RENDER_TEXT_HARFBUZZ, GetParam()); return test_api_->GetHarfBuzzRunList(); } + // Converts the current run list into a human-readable string. Can be used in + // test assertions for a readable expectation and failure message. + // + // The string shows the runs in visual order. Each run is enclosed in square + // brackets, and shows the begin and end inclusive logical character position, + // with an arrow indicating the direction of the run. Single-character runs + // just show the character position. + // + // For example, the the logical bidirectional string "abc+\x05d0\x05d1\x05d2" + // (visual string: "abc+אבג") yields "[0->2][3][6<-4]". + std::string GetRunListStructureString() const { + const internal::TextRunList* run_list = GetHarfBuzzRunList(); + std::string result; + for (size_t i = 0; i < run_list->size(); ++i) { + size_t logical_index = run_list->visual_to_logical(i); + const internal::TextRunHarfBuzz& run = *run_list->runs()[logical_index]; + if (run.range.length() == 1) { + result.append(base::StringPrintf("[%d]", run.range.start())); + } else if (run.is_rtl) { + result.append(base::StringPrintf("[%d<-%d]", run.range.end() - 1, + run.range.start())); + } else { + result.append(base::StringPrintf("[%d->%d]", run.range.start(), + run.range.end() - 1)); + } + } + return result; + } + // Returns a vector of text fragments corresponding to the current list of // text runs. - std::vector<base::string16> GetRuns() { + std::vector<base::string16> GetRunListStrings() const { std::vector<base::string16> runs_as_text; const std::vector<RenderText::FontSpan> spans = render_text_->GetFontSpansForTesting(); @@ -468,11 +505,11 @@ class RenderTextTest : public testing::Test, return runs_as_text; } - // Sets the text to |text|, then returns GetRuns(). + // Sets the text to |text|, then returns GetRunListStrings(). std::vector<base::string16> RunsFor(const base::string16& text) { render_text_->SetText(text); test_api()->EnsureLayout(); - return GetRuns(); + return GetRunListStrings(); } void ResetRenderTextInstance() { @@ -968,43 +1005,58 @@ TEST_P(RenderTextTest, ElidedText) { const wchar_t* display_text; const bool elision_expected; } cases[] = { - // Strings shorter than the elision width should be laid out in full. - { L"", L"" , false }, - { L"M", L"" , false }, - { L" . ", L" . " , false }, - { kWeak, kWeak , false }, - { kLtr, kLtr , false }, - { kLtrRtl, kLtrRtl , false }, - { kLtrRtlLtr, kLtrRtlLtr, false }, - { kRtl, kRtl , false }, - { kRtlLtr, kRtlLtr , false }, - { kRtlLtrRtl, kRtlLtrRtl, false }, - // Strings as long as the elision width should be laid out in full. - { L"012ab", L"012ab" , false }, - // Long strings should be elided with an ellipsis appended at the end. - { L"012abc", L"012a\x2026", true }, - { L"012ab" L"\x5d0\x5d1", L"012a\x2026", true }, - { L"012a" L"\x5d1" L"b", L"012a\x2026", true }, - // No RLM marker added as digits (012) have weak directionality. - { L"01" L"\x5d0\x5d1\x5d2", L"01\x5d0\x5d1\x2026", true }, - // RLM marker added as "ab" have strong LTR directionality. - { L"ab" L"\x5d0\x5d1\x5d2", L"ab\x5d0\x5d1\x2026\x200f", true }, - // Test surrogate pairs. \xd834\xdd1e forms a single code point U+1D11E; - // \xd834\xdd22 forms a second code point U+1D122. The first should be kept; - // the second removed (no surrogate pair should be partially elided). - { L"0123\xd834\xdd1e\xd834\xdd22", L"0123\xd834\xdd1e\x2026", true }, - // Test combining character sequences. U+0915 U+093F forms a compound glyph; - // U+0915 U+0942 forms a second compound glyph. The first should be kept; - // the second removed (no combining sequence should be partially elided). - { L"0123\x0915\x093f\x0915\x0942", L"0123\x0915\x093f\x2026", true }, - // U+05E9 U+05BC U+05C1 U+05B8 forms a four-character compound glyph. Again, - // it should be either fully elided, or not elided at all. If completely - // elided, an LTR Mark (U+200E) should be added. - { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x05e9\x05bc\x05c1\x05b8", false }, - { L"0\x05e9\x05bc\x05c1\x05b8", L"0\x2026\x200E" , true }, - { L"01\x05e9\x05bc\x05c1\x05b8", L"01\x2026\x200E" , true }, - { L"012\x05e9\x05bc\x05c1\x05b8", L"012\x2026\x200E" , true }, - { L"012\xF0\x9D\x84\x9E", L"012\xF0\x2026" , true }, + // Strings shorter than the elision width should be laid out in full. + {L"", L"", false}, + {L"M", L"", false}, + {L" . ", L" . ", false}, + {kWeak, kWeak, false}, + {kLtr, kLtr, false}, + {kLtrRtl, kLtrRtl, false}, + {kLtrRtlLtr, kLtrRtlLtr, false}, + {kRtl, kRtl, false}, + {kRtlLtr, kRtlLtr, false}, + {kRtlLtrRtl, kRtlLtrRtl, false}, + // Strings as long as the elision width should be laid out in full. + {L"012ab", L"012ab", false}, + // Long strings should be elided with an ellipsis appended at the end. + {L"012abc", L"012a\x2026", true}, + {L"012ab" + L"\x5d0\x5d1", + L"012a\x2026", true}, + {L"012a" + L"\x5d1" + L"b", + L"012a\x2026", true}, + // No RLM marker added as digits (012) have weak directionality. + {L"01" + L"\x5d0\x5d1\x5d2", + L"01\x5d0\x5d1\x2026", true}, + // RLM marker added as "ab" have strong LTR directionality. + {L"ab" + L"\x5d0\x5d1\x5d2", + L"ab\x5d0\x5d1\x2026\x200f", true}, + // Test surrogate pairs. \xd834\xdd1e forms a single code point U+1D11E; + // \xd834\xdd22 forms a second code point U+1D122. The first should be + // kept; + // the second removed (no surrogate pair should be partially elided). + {L"0123\xd834\xdd1e\xd834\xdd22", L"0123\xd834\xdd1e\x2026", true}, + // Test combining character sequences. U+0915 U+093F forms a compound + // glyph; + // U+0915 U+0942 forms a second compound glyph. The first should be kept; + // the second removed (no combining sequence should be partially elided). + {L"0123\x0915\x093f\x0915\x0942", L"0123\x0915\x093f\x2026", true}, + // U+05E9 U+05BC U+05C1 U+05B8 forms a four-character compound glyph. + // Again, + // it should be either fully elided, or not elided at all. If completely + // elided, an LTR Mark (U+200E) should be added. + {L"0\x05e9\x05bc\x05c1\x05b8", L"0\x05e9\x05bc\x05c1\x05b8", false}, + {L"0\x05e9\x05bc\x05c1\x05b8", L"0\x2026\x200E", true}, + {L"01\x05e9\x05bc\x05c1\x05b8", L"01\x2026\x200E", true}, + {L"012\x05e9\x05bc\x05c1\x05b8", L"012\x2026\x200E", true}, + // \xF0\x9D\x84\x9E is the UTF-8 bytestring for MUSICAL SYMBOL G CLEF. It + // should not have any internal grapheme boundaries; it should be + // completely removed when eliding. + {L"012\xF0\x9D\x84\x9E", L"012\x2026", true}, }; std::unique_ptr<RenderText> expected_render_text(CreateRenderTextInstance()); @@ -3019,8 +3071,11 @@ TEST_P(RenderTextHarfBuzzTest, Multiline_NormalWidth) { {L"abc defg hijkl", Range(0, 9), Range(9, 14), {3, 1, 4, 1, 5}, 4, true}, {L"qwertyzxcvbn", Range(0, 10), Range(10, 12), {10, 2}, 1, true}, // RTL: should render left-to-right as "<space>43210 \n cba9876". + // Note this used to say "Arabic language", in Arabic, but the last + // character in the string (\x0629) got fancy in an updated Mac font, so + // now the penultimate character repeats. (See "NOTE" below). {L"\x0627\x0644\x0644\x063A\x0629 " - L"\x0627\x0644\x0639\x0631\x0628\x064A\x0629", + L"\x0627\x0644\x0639\x0631\x0628\x064A\x064A", Range(0, 6), Range(6, 13), {1 /* space first */, 5, 7}, @@ -3184,24 +3239,27 @@ TEST_P(RenderTextHarfBuzzTest, Multiline_NewlineCharacterReplacement) { // Ensure horizontal alignment works in multiline mode. TEST_P(RenderTextHarfBuzzTest, Multiline_HorizontalAlignment) { - const struct { + constexpr struct { const wchar_t* const text; const HorizontalAlignment alignment; + const base::i18n::TextDirection display_text_direction; } kTestStrings[] = { - { L"abcdefghij\nhijkl", ALIGN_LEFT }, - { L"nhijkl\nabcdefghij", ALIGN_LEFT }, - // hebrew, 2nd line shorter - { L"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7\n\x5d0\x5d1\x5d2\x5d3", - ALIGN_RIGHT }, - // hebrew, 2nd line longer - { L"\x5d0\x5d1\x5d2\x5d3\n\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7", - ALIGN_RIGHT }, - // arabic, 2nd line shorter - { L"\x62a\x62b\x62c\x62d\x62e\x62f\x630\n\x660\x661\x662\x663\x664", - ALIGN_RIGHT }, - // arabic, 2nd line longer - { L"\x660\x661\x662\x663\x664\n\x62a\x62b\x62c\x62d\x62e\x62f\x630", - ALIGN_RIGHT }, + {L"abcdefghi\nhijk", ALIGN_LEFT, base::i18n::LEFT_TO_RIGHT}, + {L"nhij\nabcdefghi", ALIGN_LEFT, base::i18n::LEFT_TO_RIGHT}, + // Hebrew, 2nd line shorter + {L"\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7\n\x5d0\x5d1\x5d2\x5d3", + ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT}, + // Hebrew, 2nd line longer + {L"\x5d0\x5d1\x5d2\x5d3\n\x5d0\x5d1\x5d2\x5d3\x5d4\x5d5\x5d6\x5d7", + ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT}, + // Arabic, 2nd line shorter. + {L"\x0627\x0627\x0627\x0627\x0627\x0627\x0627\x0627\n\x0627\x0644\x0644" + L"\x063A", + ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT}, + // Arabic, 2nd line longer. + {L"\x0627\x0644\x0644\x063A\n\x0627\x0627\x0627\x0627\x0627\x0627\x0627" + L"\x0627", + ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT}, }; const int kGlyphSize = 5; RenderTextHarfBuzz* render_text = GetRenderTextHarfBuzz(); @@ -3211,9 +3269,11 @@ TEST_P(RenderTextHarfBuzzTest, Multiline_HorizontalAlignment) { render_text->SetMultiline(true); for (size_t i = 0; i < arraysize(kTestStrings); ++i) { - SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "] %ls", i, - kTestStrings[i].text)); + SCOPED_TRACE(testing::Message("kTestStrings[") + << i << "] = " << kTestStrings[i].text); render_text->SetText(WideToUTF16(kTestStrings[i].text)); + EXPECT_EQ(kTestStrings[i].display_text_direction, + render_text->GetDisplayTextDirection()); render_text->Draw(canvas()); ASSERT_LE(2u, test_api()->lines().size()); if (kTestStrings[i].alignment == ALIGN_LEFT) { @@ -3224,6 +3284,16 @@ TEST_P(RenderTextHarfBuzzTest, Multiline_HorizontalAlignment) { base::WideToUTF16(kTestStrings[i].text), base::string16(1, '\n'), base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); ASSERT_EQ(2u, lines.size()); + + // Sanity check the input string lengths match the glyph lengths. + EXPECT_EQ(4u, std::min(lines[0].length(), lines[1].length())); + EXPECT_EQ(8u, std::max(lines[0].length(), lines[1].length())); + const internal::TextRunList* run_list = GetHarfBuzzRunList(); + ASSERT_EQ(3U, run_list->runs().size()); + EXPECT_EQ(lines[0].length(), run_list->runs()[0]->glyph_count); + EXPECT_EQ(1u, run_list->runs()[1]->glyph_count); // \n. + EXPECT_EQ(lines[1].length(), run_list->runs()[2]->glyph_count); + int difference = (lines[0].length() - lines[1].length()) * kGlyphSize; EXPECT_EQ(test_api()->GetAlignmentOffset(0).x() + difference, test_api()->GetAlignmentOffset(1).x()); @@ -3434,14 +3504,12 @@ TEST_P(RenderTextHarfBuzzTest, NewlineWithoutMultilineFlag) { TEST_P(RenderTextHarfBuzzTest, HarfBuzz_HorizontalPositions) { const struct { const wchar_t* const text; - const Range first_run_char_range; - const Range second_run_char_range; - bool is_rtl; + const char* expected_runs; } kTestStrings[] = { - { L"abc\x3042\x3044\x3046\x3048\x304A", Range(0, 3), Range(3, 8), false }, - { L"\x062A\x0641\x0627\x062D" - L"\x05EA\x05E4\x05D5\x05D6\x05D9\x05DA\x05DB\x05DD", - Range(0, 4), Range(4, 12), true }, + {L"abc\x3042\x3044\x3046\x3048\x304A", "[0->2][3->7]"}, + {L"\x062A\x0641\x0627\x062D" + L"\x05EA\x05E4\x05D5\x05D6\x05D9\x05DA\x05DB\x05DD", + "[11<-4][3<-0]"}, }; RenderTextHarfBuzz* render_text = GetRenderTextHarfBuzz(); @@ -3451,26 +3519,16 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_HorizontalPositions) { render_text->SetText(WideToUTF16(kTestStrings[i].text)); test_api()->EnsureLayout(); - const internal::TextRunList* run_list = GetHarfBuzzRunList(); - ASSERT_EQ(2U, run_list->runs().size()); - EXPECT_EQ(kTestStrings[i].first_run_char_range, run_list->runs()[0]->range); - EXPECT_EQ(kTestStrings[i].second_run_char_range, - run_list->runs()[1]->range); - // If it's RTL, the visual order is reversed. - if (kTestStrings[i].is_rtl) { - EXPECT_EQ(1U, run_list->logical_to_visual(0)); - EXPECT_EQ(0U, run_list->logical_to_visual(1)); - } else { - EXPECT_EQ(0U, run_list->logical_to_visual(0)); - EXPECT_EQ(1U, run_list->logical_to_visual(1)); - } + EXPECT_EQ(kTestStrings[i].expected_runs, GetRunListStructureString()); DrawVisualText(); std::vector<TestSkiaTextRenderer::TextLog> text_log; renderer()->GetTextLogAndReset(&text_log); - EXPECT_EQ(2U, text_log.size()); + const internal::TextRunList* run_list = GetHarfBuzzRunList(); + ASSERT_EQ(2U, run_list->size()); + ASSERT_EQ(2U, text_log.size()); // Verifies the DrawText happens in the visual order and left-to-right. // If the text is RTL, the logically first run should be drawn at last. @@ -3538,6 +3596,19 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_Clusters) { } } +// Ensures GetClusterAt does not crash on invalid conditions. crbug.com/724880 +TEST_P(RenderTextHarfBuzzTest, HarfBuzz_NoCrashOnTextRunGetClusterAt) { + internal::TextRunHarfBuzz run((Font())); + run.range = Range(0, 4); + run.glyph_count = 4; + // Construct a |glyph_to_char| map where no glyph maps to the first character. + run.glyph_to_char = {1u, 1u, 2u, 3u}; + + Range chars, glyphs; + // GetClusterAt should not crash asking for the cluster at position 0. + ASSERT_NO_FATAL_FAILURE(run.GetClusterAt(0, &chars, &glyphs)); +} + // Ensure that graphemes with multiple code points do not get split. TEST_P(RenderTextHarfBuzzTest, HarfBuzz_SubglyphGraphemeCases) { const wchar_t* cases[] = { @@ -3558,7 +3629,7 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_SubglyphGraphemeCases) { base::string16 text = WideToUTF16(cases[i]); render_text->SetText(text); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); + const internal::TextRunList* run_list = GetHarfBuzzRunList(); ASSERT_EQ(1U, run_list->size()); internal::TextRunHarfBuzz* run = run_list->runs()[0].get(); @@ -3635,33 +3706,11 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_RunDirection) { // Get the run list for both display directions. render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_LTR); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); - ASSERT_EQ(4U, run_list->size()); - EXPECT_TRUE(run_list->runs()[0]->is_rtl); - EXPECT_FALSE(run_list->runs()[1]->is_rtl); - EXPECT_TRUE(run_list->runs()[2]->is_rtl); - EXPECT_FALSE(run_list->runs()[3]->is_rtl); - - // The Latin letters should appear to the right of the other runs. - EXPECT_EQ(2U, run_list->logical_to_visual(0)); - EXPECT_EQ(1U, run_list->logical_to_visual(1)); - EXPECT_EQ(0U, run_list->logical_to_visual(2)); - EXPECT_EQ(3U, run_list->logical_to_visual(3)); + EXPECT_EQ("[7<-6][2->5][1<-0][8->10]", GetRunListStructureString()); render_text->SetDirectionalityMode(DIRECTIONALITY_FORCE_RTL); test_api()->EnsureLayout(); - run_list = GetHarfBuzzRunList(); - ASSERT_EQ(4U, run_list->size()); - EXPECT_TRUE(run_list->runs()[0]->is_rtl); - EXPECT_FALSE(run_list->runs()[1]->is_rtl); - EXPECT_TRUE(run_list->runs()[2]->is_rtl); - EXPECT_FALSE(run_list->runs()[3]->is_rtl); - - // The Latin letters should appear to the left of the other runs. - EXPECT_EQ(3U, run_list->logical_to_visual(0)); - EXPECT_EQ(2U, run_list->logical_to_visual(1)); - EXPECT_EQ(1U, run_list->logical_to_visual(2)); - EXPECT_EQ(0U, run_list->logical_to_visual(3)); + EXPECT_EQ("[8->10][7<-6][2->5][1<-0]", GetRunListStructureString()); } TEST_P(RenderTextHarfBuzzTest, HarfBuzz_BreakRunsByUnicodeBlocks) { @@ -3670,23 +3719,13 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_BreakRunsByUnicodeBlocks) { // The '\x25B6' "play character" should break runs. http://crbug.com/278913 render_text->SetText(WideToUTF16(L"x\x25B6y")); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); - EXPECT_EQ(ToString16Vec({"x", "▶", "y"}), GetRuns()); - ASSERT_EQ(3U, run_list->size()); - EXPECT_EQ(Range(0, 1), run_list->runs()[0]->range); - EXPECT_EQ(Range(1, 2), run_list->runs()[1]->range); - EXPECT_EQ(Range(2, 3), run_list->runs()[2]->range); + EXPECT_EQ(ToString16Vec({"x", "▶", "y"}), GetRunListStrings()); + EXPECT_EQ("[0][1][2]", GetRunListStructureString()); render_text->SetText(WideToUTF16(L"x \x25B6 y")); test_api()->EnsureLayout(); - run_list = GetHarfBuzzRunList(); - EXPECT_EQ(ToString16Vec({"x", " ", "▶", " ", "y"}), GetRuns()); - ASSERT_EQ(5U, run_list->size()); - EXPECT_EQ(Range(0, 1), run_list->runs()[0]->range); - EXPECT_EQ(Range(1, 2), run_list->runs()[1]->range); - EXPECT_EQ(Range(2, 3), run_list->runs()[2]->range); - EXPECT_EQ(Range(3, 4), run_list->runs()[3]->range); - EXPECT_EQ(Range(4, 5), run_list->runs()[4]->range); + EXPECT_EQ(ToString16Vec({"x", " ", "▶", " ", "y"}), GetRunListStrings()); + EXPECT_EQ("[0][1][2][3][4]", GetRunListStructureString()); } TEST_P(RenderTextHarfBuzzTest, HarfBuzz_BreakRunsByEmoji) { @@ -3697,13 +3736,9 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_BreakRunsByEmoji) { // separated. See crbug.com/448909 render_text->SetText(UTF8ToUTF16("x\xF0\x9F\x98\x81y\xE2\x9C\xA8")); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); - ASSERT_EQ(4U, run_list->size()); - EXPECT_EQ(Range(0, 1), run_list->runs()[0]->range); - // The length is 2 since U+1F601 is represented as a surrogate pair in UTF16. - EXPECT_EQ(Range(1, 3), run_list->runs()[1]->range); - EXPECT_EQ(Range(3, 4), run_list->runs()[2]->range); - EXPECT_EQ(Range(4, 5), run_list->runs()[3]->range); + EXPECT_EQ(ToString16Vec({"x", "😁", "y", "✨"}), GetRunListStrings()); + // U+1F601 is represented as a surrogate pair in UTF-16. + EXPECT_EQ("[0][1->2][3][4]", GetRunListStructureString()); } TEST_P(RenderTextHarfBuzzTest, HarfBuzz_BreakRunsByAscii) { @@ -3713,11 +3748,9 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_BreakRunsByAscii) { // run from the ASCII period character. render_text->SetText(UTF8ToUTF16("\xF0\x9F\x90\xB1.")); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); - ASSERT_EQ(2U, run_list->size()); - // U+1F431 is represented as a surrogate pair in UTF16. - EXPECT_EQ(Range(0, 2), run_list->runs()[0]->range); - EXPECT_EQ(Range(2, 3), run_list->runs()[1]->range); + EXPECT_EQ(ToString16Vec({"🐱", "."}), GetRunListStrings()); + // U+1F431 is represented as a surrogate pair in UTF-16. + EXPECT_EQ("[0->1][2]", GetRunListStructureString()); } TEST_P(RenderTextHarfBuzzTest, GlyphBounds) { @@ -3740,7 +3773,7 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_NonExistentFont) { RenderTextHarfBuzz* render_text = GetRenderTextHarfBuzz(); render_text->SetText(ASCIIToUTF16("test")); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); + const internal::TextRunList* run_list = GetHarfBuzzRunList(); ASSERT_EQ(1U, run_list->size()); internal::TextRunHarfBuzz* run = run_list->runs()[0].get(); ShapeRunWithFont(render_text->text(), Font("TheFontThatDoesntExist", 13), @@ -3836,7 +3869,7 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_UniscribeFallback) { // Korean character "han". render_text->SetText(WideToUTF16(L"\xd55c")); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); + const internal::TextRunList* run_list = GetHarfBuzzRunList(); ASSERT_EQ(1U, run_list->size()); EXPECT_EQ(0U, run_list->runs()[0]->CountMissingGlyphs()); } @@ -3855,7 +3888,7 @@ TEST_P(RenderTextHarfBuzzTest, HarfBuzz_UnicodeFallback) { // Korean character "han". render_text->SetText(WideToUTF16(L"\xd55c")); test_api()->EnsureLayout(); - internal::TextRunList* run_list = GetHarfBuzzRunList(); + const internal::TextRunList* run_list = GetHarfBuzzRunList(); ASSERT_EQ(1U, run_list->size()); EXPECT_EQ(0U, run_list->runs()[0]->CountMissingGlyphs()); } @@ -3921,11 +3954,11 @@ TEST_P(RenderTextTest, TextDoesntClip) { { #if !defined(OS_CHROMEOS) int top_test_height = kTestSize; -#if defined(OS_WIN) - // Windows 8+ draws 1 pixel above the display rect. - if (base::win::GetVersion() >= base::win::VERSION_WIN8) + // Windows 8+ and RenderTextMac (since 10.13) draw 1 pixel above the + // display rect. + if (IsWin8Plus() || GetParam() == RENDER_TEXT_MAC) top_test_height = kTestSize - 1; -#endif // OS_WIN + // TODO(dschuyler): On ChromeOS text draws above the GetStringSize rect. SCOPED_TRACE("TextDoesntClip Top Side"); rect_buffer.EnsureSolidRect(SK_ColorWHITE, 0, 0, kCanvasSize.width(), @@ -3935,13 +3968,11 @@ TEST_P(RenderTextTest, TextDoesntClip) { { int bottom_test_y = kTestSize + string_size.height(); int bottom_test_height = kTestSize; -#if defined(OS_WIN) // Windows 8+ draws 1 pixel below the display rect. - if (base::win::GetVersion() >= base::win::VERSION_WIN8) { + if (IsWin8Plus()) { bottom_test_y = kTestSize + string_size.height() + 1; bottom_test_height = kTestSize - 1; } -#endif // OS_WIN SCOPED_TRACE("TextDoesntClip Bottom Side"); rect_buffer.EnsureSolidRect(SK_ColorWHITE, 0, bottom_test_y, kCanvasSize.width(), bottom_test_height); @@ -4094,19 +4125,22 @@ TEST_P(RenderTextTest, StylePropagated) { render_text->SetFontList(font_list); DrawVisualText(); - EXPECT_EQ(SkTypeface::kNormal, GetRendererPaint().getTypeface()->style()); + EXPECT_EQ(SkFontStyle::Normal(), + GetRendererPaint().getTypeface()->fontStyle()); render_text->SetWeight(Font::Weight::BOLD); DrawVisualText(); - EXPECT_EQ(SkTypeface::kBold, GetRendererPaint().getTypeface()->style()); + EXPECT_EQ(SkFontStyle::Bold(), GetRendererPaint().getTypeface()->fontStyle()); render_text->SetStyle(TextStyle::ITALIC, true); DrawVisualText(); - EXPECT_EQ(SkTypeface::kBoldItalic, GetRendererPaint().getTypeface()->style()); + EXPECT_EQ(SkFontStyle::BoldItalic(), + GetRendererPaint().getTypeface()->fontStyle()); render_text->SetWeight(Font::Weight::NORMAL); DrawVisualText(); - EXPECT_EQ(SkTypeface::kItalic, GetRendererPaint().getTypeface()->style()); + EXPECT_EQ(SkFontStyle::Italic(), + GetRendererPaint().getTypeface()->fontStyle()); } // Ensure the painter adheres to RenderText::subpixel_rendering_suppressed(). diff --git a/chromium/ui/gfx/sequential_id_generator.cc b/chromium/ui/gfx/sequential_id_generator.cc index cdfc62c5b21..5a1fb1fcd00 100644 --- a/chromium/ui/gfx/sequential_id_generator.cc +++ b/chromium/ui/gfx/sequential_id_generator.cc @@ -48,6 +48,11 @@ bool SequentialIDGenerator::HasGeneratedIDFor(uint32_t number) const { return number_to_id_.find(number) != number_to_id_.end(); } +void SequentialIDGenerator::MaybeReleaseNumber(uint32_t number) { + if (HasGeneratedIDFor(number)) + ReleaseNumber(number); +} + void SequentialIDGenerator::ReleaseGeneratedID(uint32_t id) { UpdateNextAvailableIDAfterRelease(id); Remove(id, &id_to_number_, &number_to_id_); diff --git a/chromium/ui/gfx/sequential_id_generator.h b/chromium/ui/gfx/sequential_id_generator.h index 53c36859cd4..7e247d3c981 100644 --- a/chromium/ui/gfx/sequential_id_generator.h +++ b/chromium/ui/gfx/sequential_id_generator.h @@ -32,6 +32,9 @@ class GFX_EXPORT SequentialIDGenerator { // |number|. bool HasGeneratedIDFor(uint32_t number) const; + // Removes the ID previously generated for |number| if necessary. + void MaybeReleaseNumber(uint32_t number); + // Removes the generated ID |id| from the internal mapping. Since the ID is // no longer mapped to any number, subsequent calls to |GetGeneratedID()| can // use this ID. diff --git a/chromium/ui/gfx/sequential_id_generator_unittest.cc b/chromium/ui/gfx/sequential_id_generator_unittest.cc index ee0aac61bfa..c728f96927e 100644 --- a/chromium/ui/gfx/sequential_id_generator_unittest.cc +++ b/chromium/ui/gfx/sequential_id_generator_unittest.cc @@ -61,4 +61,14 @@ TEST(SequentialIDGeneratorTest, RemoveMultipleNumbers) { EXPECT_EQ(4U, generator.GetGeneratedID(0)); } +TEST(SequentialIDGeneratorTest, MaybeRemoveNumbers) { + const uint32_t kMinID = 0; + SequentialIDGenerator generator(kMinID); + + EXPECT_EQ(0U, generator.GetGeneratedID(42)); + + generator.MaybeReleaseNumber(42); + EXPECT_FALSE(generator.HasGeneratedIDFor(42)); + generator.MaybeReleaseNumber(42); +} } // namespace ui diff --git a/chromium/ui/gfx/shadow_util.cc b/chromium/ui/gfx/shadow_util.cc index cc1d7ad6042..10fc06b3185 100644 --- a/chromium/ui/gfx/shadow_util.cc +++ b/chromium/ui/gfx/shadow_util.cc @@ -8,6 +8,7 @@ #include <vector> #include "base/lazy_instance.h" +#include "base/memory/ptr_util.h" #include "third_party/skia/include/core/SkDrawLooper.h" #include "third_party/skia/include/core/SkRRect.h" #include "ui/gfx/canvas.h" @@ -95,7 +96,7 @@ const ShadowDetails& ShadowDetails::Get(int elevation, int corner_radius) { ShadowDetails* shadow = &insertion.first->second; shadow->values = ShadowValue::MakeMdShadowValues(elevation); auto* source = new ShadowNineboxSource(shadow->values, corner_radius); - shadow->ninebox_image = ImageSkia(source, source->size()); + shadow->ninebox_image = ImageSkia(base::WrapUnique(source), source->size()); return *shadow; } diff --git a/chromium/ui/gfx/skia_color_space_util.cc b/chromium/ui/gfx/skia_color_space_util.cc index 47f1da259d5..b840f7e62cc 100644 --- a/chromium/ui/gfx/skia_color_space_util.cc +++ b/chromium/ui/gfx/skia_color_space_util.cc @@ -14,94 +14,6 @@ namespace gfx { namespace { -// Evaluate the gradient of the linear component of fn -void SkTransferFnEvalGradientLinear(const SkColorSpaceTransferFn& fn, - float x, - float* d_fn_d_fC_at_x, - float* d_fn_d_fF_at_x) { - *d_fn_d_fC_at_x = x; - *d_fn_d_fF_at_x = 1.f; -} - -// Solve for the parameters fC and fF, given the parameter fD. -void SkTransferFnSolveLinear(SkColorSpaceTransferFn* fn, - const float* x, - const float* t, - size_t n) { - // If this has no linear segment, don't try to solve for one. - if (fn->fD <= 0) { - fn->fC = 1; - fn->fF = 0; - return; - } - - // Let ne_lhs be the left hand side of the normal equations, and let ne_rhs - // be the right hand side. This is a 4x4 matrix, but we will only update the - // upper-left 2x2 sub-matrix. - SkMatrix44 ne_lhs; - SkVector4 ne_rhs; - for (int row = 0; row < 2; ++row) { - for (int col = 0; col < 2; ++col) { - ne_lhs.set(row, col, 0); - } - } - for (int row = 0; row < 4; ++row) - ne_rhs.fData[row] = 0; - - // Add the contributions from each sample to the normal equations. - float x_linear_max = 0; - for (size_t i = 0; i < n; ++i) { - // Ignore points in the nonlinear segment. - if (x[i] >= fn->fD) - continue; - x_linear_max = std::max(x_linear_max, x[i]); - - // Let J be the gradient of fn with respect to parameters C and F, evaluated - // at this point. - float J[2]; - SkTransferFnEvalGradientLinear(*fn, x[i], &J[0], &J[1]); - - // Let r be the residual at this point. - float r = t[i]; - - // Update the normal equations left hand side with the outer product of J - // with itself. - for (int row = 0; row < 2; ++row) { - for (int col = 0; col < 2; ++col) { - ne_lhs.set(row, col, ne_lhs.get(row, col) + J[row] * J[col]); - } - // Update the normal equations right hand side the product of J with the - // residual - ne_rhs.fData[row] += J[row] * r; - } - } - - // If we only have a single x point at 0, that isn't enough to construct a - // linear segment, so add an additional point connecting to the nonlinear - // segment. - if (x_linear_max == 0) { - float J[2]; - SkTransferFnEvalGradientLinear(*fn, fn->fD, &J[0], &J[1]); - float r = SkTransferFnEval(*fn, fn->fD); - for (int row = 0; row < 2; ++row) { - for (int col = 0; col < 2; ++col) { - ne_lhs.set(row, col, ne_lhs.get(row, col) + J[row] * J[col]); - } - ne_rhs.fData[row] += J[row] * r; - } - } - - // Solve the normal equations. - SkMatrix44 ne_lhs_inv; - bool invert_result = ne_lhs.invert(&ne_lhs_inv); - DCHECK(invert_result); - SkVector4 solution = ne_lhs_inv * ne_rhs; - - // Update the transfer function. - fn->fC = solution.fData[0]; - fn->fF = solution.fData[1]; -} - // Evaluate the gradient of the nonlinear component of fn void SkTransferFnEvalGradientNonlinear(const SkColorSpaceTransferFn& fn, float x, @@ -282,9 +194,10 @@ bool SkApproximateTransferFnInternal(const float* x, size_t n, SkColorSpaceTransferFn* fn) { // First, guess at a value of fD. Assume that the nonlinear segment applies - // to all x >= 0.25. This is generally a safe assumption (fD is usually less + // to all x >= 0.15. This is generally a safe assumption (fD is usually less // than 0.1). - fn->fD = 0.25f; + const float kLinearSegmentMaximum = 0.15f; + fn->fD = kLinearSegmentMaximum; // Do a nonlinear regression on the nonlinear segment. Use a number of guesses // for the initial value of fG, because not all values will converge. @@ -322,21 +235,32 @@ bool SkApproximateTransferFnInternal(const float* x, } // Now find the maximum x value where this nonlinear fit is no longer - // accurate or no longer defined. + // accurate, no longer defined, or no longer nonnegative. fn->fD = 0.f; float max_x_where_nonlinear_does_not_fit = -1.f; for (size_t i = 0; i < n; ++i) { + if (x[i] >= kLinearSegmentMaximum) + continue; + + // The nonlinear segment is only undefined when fA * x + fB is + // nonnegative. + float fn_at_xi = -1; + if (fn->fA * x[i] + fn->fB >= 0) + fn_at_xi = SkTransferFnEvalUnclamped(*fn, x[i]); + + // If the value is negative (or undefined), say that the fit was bad. bool nonlinear_fits_xi = true; - if (fn->fA * x[i] + fn->fB < 0) { - // The nonlinear segment is undefined when fA * x + fB is less than 0. + if (fn_at_xi < 0) nonlinear_fits_xi = false; - } else { - // Define "no longer accurate" as "has more than 10% more error than - // the maximum error in the fit segment". - float error_at_xi = std::abs(t[i] - SkTransferFnEval(*fn, x[i])); + + // Compute the error, and define "no longer accurate" as "has more than + // 10% more error than the maximum error in the fit segment". + if (nonlinear_fits_xi) { + float error_at_xi = std::abs(t[i] - fn_at_xi); if (error_at_xi > 1.1f * max_error_in_nonlinear_fit) nonlinear_fits_xi = false; } + if (!nonlinear_fits_xi) { max_x_where_nonlinear_does_not_fit = std::max(max_x_where_nonlinear_does_not_fit, x[i]); @@ -353,7 +277,17 @@ bool SkApproximateTransferFnInternal(const float* x, } // Compute the linear segment, now that we have our definitive fD. - SkTransferFnSolveLinear(fn, x, t, n); + if (fn->fD <= 0) { + // If this has no linear segment, don't try to solve for one. + fn->fC = 1; + fn->fF = 0; + } else { + // Set the linear portion such that it go through the origin and be + // continuous with the nonlinear segment. + float fn_at_fD = SkTransferFnEval(*fn, fn->fD); + fn->fC = fn_at_fD / fn->fD; + fn->fF = 0; + } return true; } diff --git a/chromium/ui/gfx/skia_paint_util.cc b/chromium/ui/gfx/skia_paint_util.cc index c18fbd1002e..2f3e2957232 100644 --- a/chromium/ui/gfx/skia_paint_util.cc +++ b/chromium/ui/gfx/skia_paint_util.cc @@ -5,6 +5,7 @@ #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/effects/SkGradientShader.h" @@ -38,8 +39,11 @@ sk_sp<cc::PaintShader> CreateImageRepShaderForScale( shader_scale.setScaleY(local_matrix.getScaleY() / scale); return cc::PaintShader::MakeImage( - SkImage::MakeFromBitmap(image_rep.sk_bitmap()), tile_mode, tile_mode, - &shader_scale); + cc::PaintImageBuilder() + .set_id(cc::PaintImage::kNonLazyStableId) + .set_image(SkImage::MakeFromBitmap(image_rep.sk_bitmap())) + .TakePaintImage(), + tile_mode, tile_mode, &shader_scale); } sk_sp<cc::PaintShader> CreateGradientShader(int start_point, diff --git a/chromium/ui/gfx/vector_icon_types.h b/chromium/ui/gfx/vector_icon_types.h index facc054f7eb..cb6f36d7e9e 100644 --- a/chromium/ui/gfx/vector_icon_types.h +++ b/chromium/ui/gfx/vector_icon_types.h @@ -21,6 +21,8 @@ const int kReferenceSizeDip = 48; enum CommandType { // A new <path> element. For the first path, this is assumed. NEW_PATH, + // Sets the alpha for the current path. + PATH_COLOR_ALPHA, // Sets the color for the current path. PATH_COLOR_ARGB, // Sets the path to clear mode (Skia's kClear_Mode). diff --git a/chromium/ui/gl/BUILD.gn b/chromium/ui/gl/BUILD.gn index e2dac179caf..760c94378f2 100644 --- a/chromium/ui/gl/BUILD.gn +++ b/chromium/ui/gl/BUILD.gn @@ -45,8 +45,6 @@ component("gl") { output_name = "gl_wrapper" # Avoid colliding with OS X"s libGL.dylib. sources = [ - "android/gl_jni_registrar.cc", - "android/gl_jni_registrar.h", "android/scoped_java_surface.cc", "android/scoped_java_surface.h", "android/surface_texture.cc", @@ -57,6 +55,8 @@ component("gl") { "ca_renderer_layer_params.h", "dc_renderer_layer_params.cc", "dc_renderer_layer_params.h", + "extension_set.cc", + "extension_set.h", "gl_bindings.cc", "gl_bindings.h", "gl_bindings_autogen_gl.cc", @@ -262,6 +262,7 @@ component("gl") { "//third_party/angle:libEGL", "//third_party/angle:libGLESv2", "//third_party/mesa:osmesa", + "//third_party/swiftshader", ] deps += [ "//third_party/angle:includes" ] @@ -375,7 +376,11 @@ source_set("run_all_unittests") { ] if (use_ozone) { - deps += [ "//ui/ozone" ] + deps += [ + "//mojo/edk/system", + "//services/service_manager/public/cpp/test:test_support", + "//ui/ozone", + ] } } diff --git a/chromium/ui/gl/android/scoped_java_surface.cc b/chromium/ui/gl/android/scoped_java_surface.cc index 1f0501bd687..bbe2d396891 100644 --- a/chromium/ui/gl/android/scoped_java_surface.cc +++ b/chromium/ui/gl/android/scoped_java_surface.cc @@ -20,7 +20,7 @@ ScopedJavaSurface::ScopedJavaSurface( : auto_release_(true), is_protected_(false) { JNIEnv* env = base::android::AttachCurrentThread(); - DCHECK(env->IsInstanceOf(surface.obj(), Surface_clazz(env))); + DCHECK(env->IsInstanceOf(surface.obj(), android_view_Surface_clazz(env))); j_surface_.Reset(surface); } diff --git a/chromium/ui/gl/android/surface_texture_listener.cc b/chromium/ui/gl/android/surface_texture_listener.cc index 5b2c713aeae..091facced5e 100644 --- a/chromium/ui/gl/android/surface_texture_listener.cc +++ b/chromium/ui/gl/android/surface_texture_listener.cc @@ -38,9 +38,4 @@ void SurfaceTextureListener::FrameAvailable(JNIEnv* env, } } -// static -bool SurfaceTextureListener::RegisterSurfaceTextureListener(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace gl diff --git a/chromium/ui/gl/android/surface_texture_listener.h b/chromium/ui/gl/android/surface_texture_listener.h index 6e2080daf35..d0221858778 100644 --- a/chromium/ui/gl/android/surface_texture_listener.h +++ b/chromium/ui/gl/android/surface_texture_listener.h @@ -30,8 +30,6 @@ class GL_EXPORT SurfaceTextureListener { void FrameAvailable(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); - static bool RegisterSurfaceTextureListener(JNIEnv* env); - private: friend class base::DeleteHelper<SurfaceTextureListener>; diff --git a/chromium/ui/gl/angle_platform_impl.cc b/chromium/ui/gl/angle_platform_impl.cc index 1363fb97b4e..3e6f4903b6e 100644 --- a/chromium/ui/gl/angle_platform_impl.cc +++ b/chromium/ui/gl/angle_platform_impl.cc @@ -4,45 +4,55 @@ #include "ui/gl/angle_platform_impl.h" +#include "base/base64.h" +#include "base/callback.h" +#include "base/lazy_instance.h" #include "base/metrics/histogram.h" #include "base/metrics/histogram_macros.h" #include "base/trace_event/trace_event.h" #include "third_party/angle/include/platform/Platform.h" #include "ui/gl/gl_bindings.h" -namespace gl { +namespace angle { namespace { -angle::ResetDisplayPlatformFunc g_angle_reset_platform = nullptr; +// This platform context stores user data accessible inside the impl methods. +struct PlatformContext { + CacheProgramCallback cache_program_callback; +}; -double ANGLEPlatformImpl_currentTime(angle::PlatformMethods* platform) { +base::LazyInstance<PlatformContext>::DestructorAtExit g_platform_context = + LAZY_INSTANCE_INITIALIZER; +ResetDisplayPlatformFunc g_angle_reset_platform = nullptr; + +double ANGLEPlatformImpl_currentTime(PlatformMethods* platform) { return base::Time::Now().ToDoubleT(); } double ANGLEPlatformImpl_monotonicallyIncreasingTime( - angle::PlatformMethods* platform) { + PlatformMethods* platform) { return (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); } const unsigned char* ANGLEPlatformImpl_getTraceCategoryEnabledFlag( - angle::PlatformMethods* platform, + PlatformMethods* platform, const char* category_group) { return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); } -void ANGLEPlatformImpl_logError(angle::PlatformMethods* platform, +void ANGLEPlatformImpl_logError(PlatformMethods* platform, const char* errorMessage) { LOG(ERROR) << errorMessage; } -void ANGLEPlatformImpl_logWarning(angle::PlatformMethods* platform, +void ANGLEPlatformImpl_logWarning(PlatformMethods* platform, const char* warningMessage) { LOG(WARNING) << warningMessage; } -angle::TraceEventHandle ANGLEPlatformImpl_addTraceEvent( - angle::PlatformMethods* platform, +TraceEventHandle ANGLEPlatformImpl_addTraceEvent( + PlatformMethods* platform, char phase, const unsigned char* category_group_enabled, const char* name, @@ -61,23 +71,23 @@ angle::TraceEventHandle ANGLEPlatformImpl_addTraceEvent( trace_event_internal::kGlobalScope, id, trace_event_internal::kNoId, base::PlatformThread::CurrentId(), timestamp_tt, num_args, arg_names, arg_types, arg_values, nullptr, flags); - angle::TraceEventHandle result; + TraceEventHandle result; memcpy(&result, &handle, sizeof(result)); return result; } void ANGLEPlatformImpl_updateTraceEventDuration( - angle::PlatformMethods* platform, + PlatformMethods* platform, const unsigned char* category_group_enabled, const char* name, - angle::TraceEventHandle handle) { + TraceEventHandle handle) { base::trace_event::TraceEventHandle trace_event_handle; memcpy(&trace_event_handle, &handle, sizeof(handle)); TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_group_enabled, name, trace_event_handle); } -void ANGLEPlatformImpl_histogramCustomCounts(angle::PlatformMethods* platform, +void ANGLEPlatformImpl_histogramCustomCounts(PlatformMethods* platform, const char* name, int sample, int min, @@ -92,7 +102,7 @@ void ANGLEPlatformImpl_histogramCustomCounts(angle::PlatformMethods* platform, counter->Add(sample); } -void ANGLEPlatformImpl_histogramEnumeration(angle::PlatformMethods* platform, +void ANGLEPlatformImpl_histogramEnumeration(PlatformMethods* platform, const char* name, int sample, int boundary_value) { @@ -105,7 +115,7 @@ void ANGLEPlatformImpl_histogramEnumeration(angle::PlatformMethods* platform, counter->Add(sample); } -void ANGLEPlatformImpl_histogramSparse(angle::PlatformMethods* platform, +void ANGLEPlatformImpl_histogramSparse(PlatformMethods* platform, const char* name, int sample) { // For sparse histograms, we can use the macro, as it does not incorporate a @@ -113,30 +123,49 @@ void ANGLEPlatformImpl_histogramSparse(angle::PlatformMethods* platform, UMA_HISTOGRAM_SPARSE_SLOWLY(name, sample); } -void ANGLEPlatformImpl_histogramBoolean(angle::PlatformMethods* platform, +void ANGLEPlatformImpl_histogramBoolean(PlatformMethods* platform, const char* name, bool sample) { ANGLEPlatformImpl_histogramEnumeration(platform, name, sample ? 1 : 0, 2); } +void ANGLEPlatformImpl_cacheProgram(PlatformMethods* platform, + const ProgramKeyType& key, + size_t program_size, + const uint8_t* program_bytes) { + PlatformContext* context = + reinterpret_cast<PlatformContext*>(platform->context); + if (context && context->cache_program_callback) { + // Convert the key and binary to string form. + std::string key_string(reinterpret_cast<const char*>(&key[0]), + sizeof(ProgramKeyType)); + std::string value_string(reinterpret_cast<const char*>(program_bytes), + program_size); + std::string key_string_64; + std::string value_string_64; + base::Base64Encode(key_string, &key_string_64); + base::Base64Encode(value_string, &value_string_64); + context->cache_program_callback.Run(key_string_64, value_string_64); + } +} + } // anonymous namespace -bool InitializeANGLEPlatform(EGLDisplay display) { - angle::GetDisplayPlatformFunc angle_get_platform = - reinterpret_cast<angle::GetDisplayPlatformFunc>( +bool InitializePlatform(EGLDisplay display) { + GetDisplayPlatformFunc angle_get_platform = + reinterpret_cast<GetDisplayPlatformFunc>( eglGetProcAddress("ANGLEGetDisplayPlatform")); if (!angle_get_platform) return false; // Save the pointer to the destroy function here to avoid crash. - g_angle_reset_platform = reinterpret_cast<angle::ResetDisplayPlatformFunc>( + g_angle_reset_platform = reinterpret_cast<ResetDisplayPlatformFunc>( eglGetProcAddress("ANGLEResetDisplayPlatform")); - angle::PlatformMethods* platformMethods = nullptr; - if (!angle_get_platform(static_cast<angle::EGLDisplayType>(display), - angle::g_PlatformMethodNames, - angle::g_NumPlatformMethods, nullptr, - &platformMethods)) + PlatformMethods* platformMethods = nullptr; + if (!angle_get_platform(static_cast<EGLDisplayType>(display), + g_PlatformMethodNames, g_NumPlatformMethods, + &g_platform_context.Get(), &platformMethods)) return false; platformMethods->currentTime = ANGLEPlatformImpl_currentTime; platformMethods->addTraceEvent = ANGLEPlatformImpl_addTraceEvent; @@ -155,13 +184,23 @@ bool InitializeANGLEPlatform(EGLDisplay display) { ANGLEPlatformImpl_monotonicallyIncreasingTime; platformMethods->updateTraceEventDuration = ANGLEPlatformImpl_updateTraceEventDuration; + platformMethods->cacheProgram = ANGLEPlatformImpl_cacheProgram; return true; } -void ResetANGLEPlatform(EGLDisplay display) { +void ResetPlatform(EGLDisplay display) { if (!g_angle_reset_platform) return; - g_angle_reset_platform(static_cast<angle::EGLDisplayType>(display)); + g_angle_reset_platform(static_cast<EGLDisplayType>(display)); + ResetCacheProgramCallback(); +} + +void SetCacheProgramCallback(CacheProgramCallback callback) { + g_platform_context.Get().cache_program_callback = callback; +} + +void ResetCacheProgramCallback() { + g_platform_context.Get().cache_program_callback.Reset(); } -} // namespace gl +} // namespace angle diff --git a/chromium/ui/gl/angle_platform_impl.h b/chromium/ui/gl/angle_platform_impl.h index 38c93ab678a..927850cc7f0 100644 --- a/chromium/ui/gl/angle_platform_impl.h +++ b/chromium/ui/gl/angle_platform_impl.h @@ -8,13 +8,20 @@ // Implements the ANGLE platform interface, for functionality like // histograms and trace profiling. +#include "base/callback_forward.h" #include "ui/gl/gl_context_egl.h" +#include "ui/gl/gl_export.h" -namespace gl { +namespace angle { -bool InitializeANGLEPlatform(EGLDisplay display); -void ResetANGLEPlatform(EGLDisplay display); +GL_EXPORT bool InitializePlatform(EGLDisplay display); +GL_EXPORT void ResetPlatform(EGLDisplay display); -} // namespace gl +using CacheProgramCallback = + base::RepeatingCallback<void(const std::string&, const std::string&)>; +GL_EXPORT void SetCacheProgramCallback(CacheProgramCallback callback); +GL_EXPORT void ResetCacheProgramCallback(); + +} // namespace angle #endif // UI_GL_ANGLE_PLATFORM_IMPL_H_ diff --git a/chromium/ui/gl/egl_api_unittest.cc b/chromium/ui/gl/egl_api_unittest.cc index 772cf0646a1..1d8babc927b 100644 --- a/chromium/ui/gl/egl_api_unittest.cc +++ b/chromium/ui/gl/egl_api_unittest.cc @@ -4,7 +4,6 @@ #include <memory> -#include "base/command_line.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_egl_api_implementation.h" #include "ui/gl/gl_surface_egl.h" @@ -26,6 +25,8 @@ class EGLApiTest : public testing::Test { g_driver_egl.fn.eglGetDisplayFn = &FakeGetDisplay; g_driver_egl.fn.eglGetErrorFn = &FakeGetError; g_driver_egl.fn.eglGetProcAddressFn = &FakeGetProcAddress; + + SetGLImplementation(kGLImplementationEGLGLES2); } void TearDown() override { @@ -37,13 +38,13 @@ class EGLApiTest : public testing::Test { fake_extension_string_ = ""; } - void InitializeAPI(base::CommandLine* command_line) { + void InitializeAPI(const char* disabled_extensions) { api_.reset(new RealEGLApi()); g_current_egl_context = api_.get(); - if (command_line) - api_->InitializeWithCommandLine(&g_driver_egl, command_line); - else - api_->Initialize(&g_driver_egl); + api_->Initialize(&g_driver_egl); + if (disabled_extensions) { + SetDisabledExtensionsEGL(disabled_extensions); + } g_driver_egl.InitializeClientExtensionBindings(); GLSurfaceEGL::InitializeDisplay(EGL_DEFAULT_DISPLAY); g_driver_egl.InitializeExtensionBindings(); @@ -114,23 +115,21 @@ TEST_F(EGLApiTest, DisabledExtensionBitTest) { EXPECT_TRUE(g_driver_egl.ext.b_EGL_KHR_fence_sync); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - InitializeAPI(&command_line); + InitializeAPI(kFakeDisabledExtensions); EXPECT_FALSE(g_driver_egl.ext.b_EGL_KHR_fence_sync); } TEST_F(EGLApiTest, DisabledExtensionStringTest) { - static const char* kFakeExtensions = "EGL_EXT_1 EGL_EXT_2" - " EGL_EXT_3 EGL_EXT_4"; + static const char* kFakeExtensions = + "EGL_EXT_1 EGL_KHR_fence_sync EGL_EXT_3 EGL_KHR_wait_sync"; static const char* kFakeClientExtensions = "EGL_CLIENT_EXT_1 EGL_CLIENT_EXT_2"; static const char* kFakeDisabledExtensions = - "EGL_EXT_1,EGL_EXT_2,EGL_FAKE,EGL_CLIENT_EXT_1"; - static const char* kFilteredExtensions = "EGL_EXT_3 EGL_EXT_4"; - static const char* kFilteredClientExtensions = "EGL_CLIENT_EXT_2"; + "EGL_KHR_fence_sync,EGL_KHR_wait_sync"; + static const char* kFilteredExtensions = "EGL_EXT_1 EGL_EXT_3"; + static const char* kFilteredClientExtensions = + "EGL_CLIENT_EXT_1 EGL_CLIENT_EXT_2"; SetFakeExtensionString(kFakeExtensions, kFakeClientExtensions); InitializeAPI(nullptr); @@ -138,10 +137,7 @@ TEST_F(EGLApiTest, DisabledExtensionStringTest) { EXPECT_STREQ(kFakeClientExtensions, GetExtensions().first); EXPECT_STREQ(kFakeExtensions, GetExtensions().second); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - InitializeAPI(&command_line); + InitializeAPI(kFakeDisabledExtensions); EXPECT_STREQ(kFilteredClientExtensions, GetExtensions().first); EXPECT_STREQ(kFilteredExtensions, GetExtensions().second); diff --git a/chromium/ui/gl/extension_set.cc b/chromium/ui/gl/extension_set.cc new file mode 100644 index 00000000000..f1b0154e37b --- /dev/null +++ b/chromium/ui/gl/extension_set.cc @@ -0,0 +1,20 @@ +// 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/gl/extension_set.h" +#include "base/strings/string_split.h" + +namespace gl { + +ExtensionSet MakeExtensionSet(const base::StringPiece& extensions_string) { + return ExtensionSet(SplitStringPiece( + extensions_string, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)); +} + +bool HasExtension(const ExtensionSet& extension_set, + const base::StringPiece& extension) { + return extension_set.find(extension) != extension_set.end(); +} + +} // namespace gl diff --git a/chromium/ui/gl/extension_set.h b/chromium/ui/gl/extension_set.h new file mode 100644 index 00000000000..e8d36227c1a --- /dev/null +++ b/chromium/ui/gl/extension_set.h @@ -0,0 +1,30 @@ +// 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_GL_EXTENSION_SET_H_ +#define UI_GL_EXTENSION_SET_H_ + +#include "base/containers/flat_set.h" +#include "base/strings/string_piece.h" +#include "ui/gl/gl_export.h" + +namespace gl { + +using ExtensionSet = base::flat_set<base::StringPiece>; + +GL_EXPORT ExtensionSet +MakeExtensionSet(const base::StringPiece& extensions_string); + +GL_EXPORT bool HasExtension(const ExtensionSet& extension_set, + const base::StringPiece& extension); + +template <size_t N> +inline bool HasExtension(const ExtensionSet& extension_set, + const char (&extension)[N]) { + return HasExtension(extension_set, base::StringPiece(extension, N - 1)); +} + +} // namespace gl + +#endif // UI_GL_EXTENSION_SET_H_ diff --git a/chromium/ui/gl/generate_bindings.py b/chromium/ui/gl/generate_bindings.py index 5dbc228dbaa..7c20a09eb51 100755 --- a/chromium/ui/gl/generate_bindings.py +++ b/chromium/ui/gl/generate_bindings.py @@ -760,7 +760,8 @@ GL_FUNCTIONS = [ 'arguments': 'GLenum pname, GLsizei bufSize, GLsizei* length, GLint* data', }, { 'return_type': 'void', - 'versions': [{ 'name': 'glGetInternalformativ' }], + 'versions': [{'name': 'glGetInternalformativ', + 'extensions': ['GL_ARB_internalformat_query']}], 'arguments': 'GLenum target, GLenum internalformat, GLenum pname, ' 'GLsizei bufSize, GLint* params', }, { 'return_type': 'void', @@ -770,6 +771,10 @@ GL_FUNCTIONS = [ 'GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, ' 'GLsizei* length, GLint* params', }, { 'return_type': 'void', + 'versions': [{'name': 'glGetMultisamplefv', + 'extensions': ['GL_ARB_texture_multisample']}], + 'arguments': 'GLenum pname, GLuint index, GLfloat* val', }, +{ 'return_type': 'void', 'versions': [{'name': 'glGetMultisamplefvRobustANGLE', 'extensions': ['GL_ANGLE_robust_client_memory']}], 'arguments': @@ -1273,6 +1278,9 @@ GL_FUNCTIONS = [ 'names': ['glPointParameteri'], 'arguments': 'GLenum pname, GLint param', }, { 'return_type': 'void', + 'names': ['glPolygonMode'], + 'arguments': 'GLenum face, GLenum mode', }, +{ 'return_type': 'void', 'names': ['glPolygonOffset'], 'arguments': 'GLfloat factor, GLfloat units', }, { 'return_type': 'void', @@ -1506,6 +1514,14 @@ GL_FUNCTIONS = [ 'names': ['glTestFenceNV'], 'arguments': 'GLuint fence', }, { 'return_type': 'void', + 'names': ['glTexBuffer', 'glTexBufferOES', 'glTexBufferEXT'], + 'arguments': 'GLenum target, GLenum internalformat, GLuint buffer', } , +{ 'return_type': 'void', + 'names': ['glTexBufferRange', 'glTexBufferRangeOES', 'glTexBufferRangeEXT'], + 'arguments': + 'GLenum target, GLenum internalformat, GLuint buffer, ' + 'GLintptr offset, GLsizeiptr size', }, +{ 'return_type': 'void', 'names': ['glTexImage2D'], 'arguments': 'GLenum target, GLint level, GLint internalformat, GLsizei width, ' @@ -1757,7 +1773,7 @@ GL_FUNCTIONS = [ { 'return_type': 'void', 'known_as': 'glVertexAttribDivisorANGLE', 'names': ['glVertexAttribDivisorARB', 'glVertexAttribDivisorANGLE', - 'glVertexAttribDivisor'], + 'glVertexAttribDivisorEXT', 'glVertexAttribDivisor'], 'arguments': 'GLuint index, GLuint divisor', }, { 'return_type': 'void', @@ -1783,7 +1799,7 @@ GL_FUNCTIONS = [ { 'return_type': 'void', 'names': ['glViewport'], 'arguments': 'GLint x, GLint y, GLsizei width, GLsizei height', }, -{ 'return_type': 'GLenum', +{ 'return_type': 'void', 'versions': [{ 'name': 'glWaitSync', 'extensions': ['GL_ARB_sync'] }], 'arguments': @@ -2438,6 +2454,8 @@ def GenerateHeader(file, functions, set_name, #ifndef UI_GL_GL_BINDINGS_AUTOGEN_%(name)s_H_ #define UI_GL_GL_BINDINGS_AUTOGEN_%(name)s_H_ +#include <string> + namespace gl { class GLContext; @@ -2475,6 +2493,9 @@ class GLContext; %(name)sApi(); virtual ~%(name)sApi(); + virtual void SetDisabledExtensions( + const std::string& disabled_extensions) {} + """ % {'name': set_name.upper()}) for func in functions: file.write(' virtual %s %sFn(%s) = 0;\n' % @@ -2703,25 +2724,22 @@ namespace gl { if set_name == 'gl': file.write("""\ -void DriverGL::InitializeDynamicBindings( - const GLVersionInfo* ver, const std::string& context_extensions) { - std::string extensions = context_extensions + " "; - ALLOW_UNUSED_LOCAL(extensions); - +void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver, + const ExtensionSet& extensions) { """) elif set_name == 'egl': file.write("""\ void DriverEGL::InitializeClientExtensionBindings() { std::string client_extensions(GetClientExtensions()); - client_extensions += " "; - ALLOW_UNUSED_LOCAL(client_extensions); + ExtensionSet extensions(MakeExtensionSet(client_extensions)); + ALLOW_UNUSED_LOCAL(extensions); """) else: file.write("""\ void Driver%s::InitializeExtensionBindings() { - std::string extensions(GetPlatformExtensions()); - extensions += " "; + std::string platform_extensions(GetPlatformExtensions()); + ExtensionSet extensions(MakeExtensionSet(platform_extensions)); ALLOW_UNUSED_LOCAL(extensions); """ % (set_name.upper(),)) @@ -2730,7 +2748,7 @@ void Driver%s::InitializeExtensionBindings() { # Extra space at the end of the extension name is intentional, # it is used as a separator for extension in extensions: - file.write(' ext.b_%s = %s.find("%s ") != std::string::npos;\n' % + file.write(' ext.b_%s = HasExtension(%s, "%s");\n' % (extension, extension_var, extension)) for func in extension_funcs: @@ -2739,7 +2757,7 @@ void Driver%s::InitializeExtensionBindings() { WriteConditionalFuncBinding(file, func) OutputExtensionBindings( - 'client_extensions', + 'extensions', sorted(used_client_extensions), [ f for f in functions if IsClientExtensionFunc(f) ]) @@ -2748,8 +2766,8 @@ void Driver%s::InitializeExtensionBindings() { } void DriverEGL::InitializeExtensionBindings() { - std::string extensions(GetPlatformExtensions()); - extensions += " "; + std::string platform_extensions(GetPlatformExtensions()); + ExtensionSet extensions(MakeExtensionSet(platform_extensions)); ALLOW_UNUSED_LOCAL(extensions); """) @@ -3306,7 +3324,8 @@ def main(argv): parser.add_option('--verify-order', action='store_true') parser.add_option('--generate-dchecks', action='store_true', help='Generates DCHECKs into the logging functions ' - 'asserting no GL errors (useful for debugging)') + 'asserting no GL errors (useful for debugging with ' + '--enable-gpu-service-logging)') parser.add_option('--validate-bindings', action='store_true', help='Generate DCHECKs to validate function bindings ' ' were correctly supplied (useful for debugging)') diff --git a/chromium/ui/gl/gl_angle_util_win.cc b/chromium/ui/gl/gl_angle_util_win.cc index 45e37ab423b..291ba107eee 100644 --- a/chromium/ui/gl/gl_angle_util_win.cc +++ b/chromium/ui/gl/gl_angle_util_win.cc @@ -101,7 +101,7 @@ base::win::ScopedComPtr<IDCompositionDevice2> QueryDirectCompositionDevice( UINT data_size = sizeof(dcomp_device.Get()); HRESULT hr = d3d11_device->GetPrivateData(kDirectCompositionGUID, &data_size, - &dcomp_device); + dcomp_device.GetAddressOf()); if (SUCCEEDED(hr) && dcomp_device) return dcomp_device; diff --git a/chromium/ui/gl/gl_api_unittest.cc b/chromium/ui/gl/gl_api_unittest.cc index f0185c670e1..b57f54f2355 100644 --- a/chromium/ui/gl/gl_api_unittest.cc +++ b/chromium/ui/gl/gl_api_unittest.cc @@ -46,23 +46,28 @@ class GLApiTest : public testing::Test { fake_extension_strings_ = nullptr; } - void InitializeAPI(base::CommandLine* command_line) { + void InitializeAPI(const char* disabled_extensions) { driver_.reset(new DriverGL()); driver_->fn.glGetStringFn = &FakeGetString; driver_->fn.glGetStringiFn = &FakeGetStringi; driver_->fn.glGetIntegervFn = &FakeGetIntegervFn; api_.reset(new RealGLApi()); - if (command_line) - api_->InitializeWithCommandLine(driver_.get(), command_line); - else - api_->Initialize(driver_.get()); - - api_->InitializeFilteredExtensions(); - std::unique_ptr<GLVersionInfo> version = - GetVersionInfoFromContext(api_.get()); - driver_->InitializeDynamicBindings( - version.get(), GetGLExtensionsFromCurrentContext(api_.get())); + if (disabled_extensions) { + api_->SetDisabledExtensions(disabled_extensions); + } + api_->Initialize(driver_.get()); + + std::string extensions_string = + GetGLExtensionsFromCurrentContext(api_.get()); + ExtensionSet extension_set = MakeExtensionSet(extensions_string); + + auto version = std::make_unique<GLVersionInfo>( + reinterpret_cast<const char*>(api_->glGetStringFn(GL_VERSION)), + reinterpret_cast<const char*>(api_->glGetStringFn(GL_RENDERER)), + extension_set); + + driver_->InitializeDynamicBindings(version.get(), extension_set); api_->set_version(std::move(version)); } @@ -135,14 +140,9 @@ TEST_F(GLApiTest, DisabledExtensionStringTest) { SetFakeExtensionString(kFakeExtensions); InitializeAPI(nullptr); - EXPECT_STREQ(kFakeExtensions, GetExtensions()); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - InitializeAPI(&command_line); - + InitializeAPI(kFakeDisabledExtensions); EXPECT_STREQ(kFilteredExtensions, GetExtensions()); } @@ -154,14 +154,9 @@ TEST_F(GLApiTest, DisabledExtensionBitTest) { SetFakeExtensionStrings(kFakeExtensions, arraysize(kFakeExtensions)); InitializeAPI(nullptr); - EXPECT_TRUE(driver_->ext.b_GL_ARB_timer_query); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - InitializeAPI(&command_line); - + InitializeAPI(kFakeDisabledExtensions); EXPECT_FALSE(driver_->ext.b_GL_ARB_timer_query); } @@ -186,11 +181,7 @@ TEST_F(GLApiTest, DisabledExtensionStringIndexTest) { EXPECT_STREQ(kFakeExtensions[i], GetExtensioni(i)); } - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - InitializeAPI(&command_line); - + InitializeAPI(kFakeDisabledExtensions); EXPECT_EQ(arraysize(kFilteredExtensions), GetNumExtensions()); for (uint32_t i = 0; i < arraysize(kFilteredExtensions); ++i) { EXPECT_STREQ(kFilteredExtensions[i], GetExtensioni(i)); diff --git a/chromium/ui/gl/gl_bindings.cc b/chromium/ui/gl/gl_bindings.cc index 9e7bcfea0d6..05c45495fa0 100644 --- a/chromium/ui/gl/gl_bindings.cc +++ b/chromium/ui/gl/gl_bindings.cc @@ -47,6 +47,27 @@ std::string DriverEGL::GetPlatformExtensions() { return str ? std::string(str) : ""; } +void DriverEGL::UpdateConditionalExtensionBindings() { + // For the moment, only two extensions can be conditionally disabled + // through GPU driver bug workarounds mechanism: + // EGL_KHR_fence_sync + // EGL_KHR_wait_sync + + // In theory it's OK to allow disabling other EGL extensions, as far as they + // are not the ones used in GLSurfaceEGL::InitializeOneOff(). + + std::string extensions(GetPlatformExtensions()); + extensions += " "; + + ext.b_EGL_KHR_fence_sync = + extensions.find("EGL_KHR_fence_sync ") != std::string::npos; + ext.b_EGL_KHR_wait_sync = + extensions.find("EGL_KHR_wait_sync ") != std::string::npos; + if (!ext.b_EGL_KHR_wait_sync) { + fn.eglWaitSyncKHRFn = nullptr; + } +} + // static std::string DriverEGL::GetClientExtensions() { const char* str = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); diff --git a/chromium/ui/gl/gl_bindings.h b/chromium/ui/gl/gl_bindings.h index f0e7f583c31..df7895eed20 100644 --- a/chromium/ui/gl/gl_bindings.h +++ b/chromium/ui/gl/gl_bindings.h @@ -29,6 +29,7 @@ #include "base/logging.h" #include "base/threading/thread_local.h" #include "build/build_config.h" +#include "ui/gl/extension_set.h" #include "ui/gl/gl_export.h" // The standard OpenGL native extension headers are also included. @@ -163,6 +164,15 @@ #define GL_SUBSCRIBED_VALUES_BUFFER_CHROMIUM 0x924B #define GL_MOUSE_POSITION_CHROMIUM 0x924C +// GL_CHROMIUM_texture_filtering_hint +#define GL_TEXTURE_FILTERING_HINT_CHROMIUM 0x8AF0 + +// GL_CHROMIUM_resize +#define GL_COLOR_SPACE_UNSPECIFIED_CHROMIUM 0x8AF1 +#define GL_COLOR_SPACE_SCRGB_LINEAR_CHROMIUM 0x8AF2 +#define GL_COLOR_SPACE_SRGB_CHROMIUM 0x8AF3 +#define GL_COLOR_SPACE_DISPLAY_P3_CHROMIUM 0x8AF4 + // GL_OES_texure_3D #define GL_SAMPLER_3D_OES 0x8B5F @@ -415,7 +425,7 @@ struct GLVersionInfo; struct GL_EXPORT DriverGL { void InitializeStaticBindings(); void InitializeDynamicBindings(const GLVersionInfo* ver, - const std::string& context_extensions); + const ExtensionSet& extensions); void ClearBindings(); ProcsGL fn; @@ -460,6 +470,7 @@ struct GL_EXPORT DriverEGL { void InitializeClientExtensionBindings(); void InitializeExtensionBindings(); void ClearBindings(); + void UpdateConditionalExtensionBindings(); ProcsEGL fn; ExtensionsEGL ext; diff --git a/chromium/ui/gl/gl_bindings_api_autogen_gl.h b/chromium/ui/gl/gl_bindings_api_autogen_gl.h index 5403786bb53..1df021c8718 100644 --- a/chromium/ui/gl/gl_bindings_api_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_api_autogen_gl.h @@ -528,6 +528,7 @@ void glGetInternalformativRobustANGLEFn(GLenum target, GLsizei bufSize, GLsizei* length, GLint* params) override; +void glGetMultisamplefvFn(GLenum pname, GLuint index, GLfloat* val) override; void glGetMultisamplefvRobustANGLEFn(GLenum pname, GLuint index, GLsizei bufSize, @@ -861,6 +862,7 @@ void glPathStencilFuncNVFn(GLenum func, GLint ref, GLuint mask) override; void glPauseTransformFeedbackFn(void) override; void glPixelStoreiFn(GLenum pname, GLint param) override; void glPointParameteriFn(GLenum pname, GLint param) override; +void glPolygonModeFn(GLenum face, GLenum mode) override; void glPolygonOffsetFn(GLfloat factor, GLfloat units) override; void glPopDebugGroupFn() override; void glPopGroupMarkerEXTFn(void) override; @@ -1039,6 +1041,14 @@ void glStencilThenCoverStrokePathNVFn(GLuint path, GLenum coverMode) override; GLboolean glTestFenceAPPLEFn(GLuint fence) override; GLboolean glTestFenceNVFn(GLuint fence) override; +void glTexBufferFn(GLenum target, + GLenum internalformat, + GLuint buffer) override; +void glTexBufferRangeFn(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) override; void glTexImage2DFn(GLenum target, GLint level, GLint internalformat, @@ -1271,4 +1281,4 @@ void glVertexAttribPointerFn(GLuint indx, GLsizei stride, const void* ptr) override; void glViewportFn(GLint x, GLint y, GLsizei width, GLsizei height) override; -GLenum glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) override; +void glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) override; diff --git a/chromium/ui/gl/gl_bindings_autogen_egl.cc b/chromium/ui/gl/gl_bindings_autogen_egl.cc index 637996d7ec3..52c8aafc841 100644 --- a/chromium/ui/gl/gl_bindings_autogen_egl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_egl.cc @@ -128,11 +128,11 @@ void DriverEGL::InitializeStaticBindings() { void DriverEGL::InitializeClientExtensionBindings() { std::string client_extensions(GetClientExtensions()); - client_extensions += " "; - ALLOW_UNUSED_LOCAL(client_extensions); + ExtensionSet extensions(MakeExtensionSet(client_extensions)); + ALLOW_UNUSED_LOCAL(extensions); ext.b_EGL_EXT_platform_base = - client_extensions.find("EGL_EXT_platform_base ") != std::string::npos; + HasExtension(extensions, "EGL_EXT_platform_base"); if (ext.b_EGL_EXT_platform_base) { fn.eglGetPlatformDisplayEXTFn = @@ -142,51 +142,41 @@ void DriverEGL::InitializeClientExtensionBindings() { } void DriverEGL::InitializeExtensionBindings() { - std::string extensions(GetPlatformExtensions()); - extensions += " "; + std::string platform_extensions(GetPlatformExtensions()); + ExtensionSet extensions(MakeExtensionSet(platform_extensions)); ALLOW_UNUSED_LOCAL(extensions); ext.b_EGL_ANGLE_d3d_share_handle_client_buffer = - extensions.find("EGL_ANGLE_d3d_share_handle_client_buffer ") != - std::string::npos; + HasExtension(extensions, "EGL_ANGLE_d3d_share_handle_client_buffer"); ext.b_EGL_ANGLE_program_cache_control = - extensions.find("EGL_ANGLE_program_cache_control ") != std::string::npos; + HasExtension(extensions, "EGL_ANGLE_program_cache_control"); ext.b_EGL_ANGLE_query_surface_pointer = - extensions.find("EGL_ANGLE_query_surface_pointer ") != std::string::npos; + HasExtension(extensions, "EGL_ANGLE_query_surface_pointer"); ext.b_EGL_ANGLE_stream_producer_d3d_texture_nv12 = - extensions.find("EGL_ANGLE_stream_producer_d3d_texture_nv12 ") != - std::string::npos; + HasExtension(extensions, "EGL_ANGLE_stream_producer_d3d_texture_nv12"); ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle = - extensions.find("EGL_ANGLE_surface_d3d_texture_2d_share_handle ") != - std::string::npos; + HasExtension(extensions, "EGL_ANGLE_surface_d3d_texture_2d_share_handle"); ext.b_EGL_CHROMIUM_sync_control = - extensions.find("EGL_CHROMIUM_sync_control ") != std::string::npos; + HasExtension(extensions, "EGL_CHROMIUM_sync_control"); ext.b_EGL_EXT_image_flush_external = - extensions.find("EGL_EXT_image_flush_external ") != std::string::npos; - ext.b_EGL_KHR_fence_sync = - extensions.find("EGL_KHR_fence_sync ") != std::string::npos; + HasExtension(extensions, "EGL_EXT_image_flush_external"); + ext.b_EGL_KHR_fence_sync = HasExtension(extensions, "EGL_KHR_fence_sync"); ext.b_EGL_KHR_gl_texture_2D_image = - extensions.find("EGL_KHR_gl_texture_2D_image ") != std::string::npos; - ext.b_EGL_KHR_image = extensions.find("EGL_KHR_image ") != std::string::npos; - ext.b_EGL_KHR_image_base = - extensions.find("EGL_KHR_image_base ") != std::string::npos; - ext.b_EGL_KHR_stream = - extensions.find("EGL_KHR_stream ") != std::string::npos; + HasExtension(extensions, "EGL_KHR_gl_texture_2D_image"); + ext.b_EGL_KHR_image = HasExtension(extensions, "EGL_KHR_image"); + ext.b_EGL_KHR_image_base = HasExtension(extensions, "EGL_KHR_image_base"); + ext.b_EGL_KHR_stream = HasExtension(extensions, "EGL_KHR_stream"); ext.b_EGL_KHR_stream_consumer_gltexture = - extensions.find("EGL_KHR_stream_consumer_gltexture ") != - std::string::npos; + HasExtension(extensions, "EGL_KHR_stream_consumer_gltexture"); ext.b_EGL_KHR_swap_buffers_with_damage = - extensions.find("EGL_KHR_swap_buffers_with_damage ") != std::string::npos; - ext.b_EGL_KHR_wait_sync = - extensions.find("EGL_KHR_wait_sync ") != std::string::npos; + HasExtension(extensions, "EGL_KHR_swap_buffers_with_damage"); + ext.b_EGL_KHR_wait_sync = HasExtension(extensions, "EGL_KHR_wait_sync"); ext.b_EGL_NV_post_sub_buffer = - extensions.find("EGL_NV_post_sub_buffer ") != std::string::npos; + HasExtension(extensions, "EGL_NV_post_sub_buffer"); ext.b_EGL_NV_stream_consumer_gltexture_yuv = - extensions.find("EGL_NV_stream_consumer_gltexture_yuv ") != - std::string::npos; + HasExtension(extensions, "EGL_NV_stream_consumer_gltexture_yuv"); ext.b_GL_CHROMIUM_egl_khr_fence_sync_hack = - extensions.find("GL_CHROMIUM_egl_khr_fence_sync_hack ") != - std::string::npos; + HasExtension(extensions, "GL_CHROMIUM_egl_khr_fence_sync_hack"); if (ext.b_EGL_KHR_image || ext.b_EGL_KHR_image_base || ext.b_EGL_KHR_gl_texture_2D_image) { diff --git a/chromium/ui/gl/gl_bindings_autogen_egl.h b/chromium/ui/gl/gl_bindings_autogen_egl.h index f054e576db0..834d697eb79 100644 --- a/chromium/ui/gl/gl_bindings_autogen_egl.h +++ b/chromium/ui/gl/gl_bindings_autogen_egl.h @@ -11,6 +11,8 @@ #ifndef UI_GL_GL_BINDINGS_AUTOGEN_EGL_H_ #define UI_GL_GL_BINDINGS_AUTOGEN_EGL_H_ +#include <string> + namespace gl { class GLContext; @@ -321,6 +323,8 @@ class GL_EXPORT EGLApi { EGLApi(); virtual ~EGLApi(); + virtual void SetDisabledExtensions(const std::string& disabled_extensions) {} + virtual EGLBoolean eglBindAPIFn(EGLenum api) = 0; virtual EGLBoolean eglBindTexImageFn(EGLDisplay dpy, EGLSurface surface, diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.cc b/chromium/ui/gl/gl_bindings_autogen_gl.cc index 95faee85992..fadb5c0288e 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_gl.cc @@ -237,6 +237,7 @@ void DriverGL::InitializeStaticBindings() { fn.glGetIntegervRobustANGLEFn = 0; fn.glGetInternalformativFn = 0; fn.glGetInternalformativRobustANGLEFn = 0; + fn.glGetMultisamplefvFn = 0; fn.glGetMultisamplefvRobustANGLEFn = 0; fn.glGetnUniformfvRobustANGLEFn = 0; fn.glGetnUniformivRobustANGLEFn = 0; @@ -368,6 +369,7 @@ void DriverGL::InitializeStaticBindings() { fn.glPixelStoreiFn = reinterpret_cast<glPixelStoreiProc>(GetGLProcAddress("glPixelStorei")); fn.glPointParameteriFn = 0; + fn.glPolygonModeFn = 0; fn.glPolygonOffsetFn = reinterpret_cast<glPolygonOffsetProc>( GetGLProcAddress("glPolygonOffset")); fn.glPopDebugGroupFn = 0; @@ -431,6 +433,8 @@ void DriverGL::InitializeStaticBindings() { fn.glStencilThenCoverStrokePathNVFn = 0; fn.glTestFenceAPPLEFn = 0; fn.glTestFenceNVFn = 0; + fn.glTexBufferFn = 0; + fn.glTexBufferRangeFn = 0; fn.glTexImage2DFn = reinterpret_cast<glTexImage2DProc>(GetGLProcAddress("glTexImage2D")); fn.glTexImage2DRobustANGLEFn = 0; @@ -543,140 +547,128 @@ void DriverGL::InitializeStaticBindings() { fn.glWaitSyncFn = 0; } -void DriverGL::InitializeDynamicBindings( - const GLVersionInfo* ver, - const std::string& context_extensions) { - std::string extensions = context_extensions + " "; - ALLOW_UNUSED_LOCAL(extensions); - +void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver, + const ExtensionSet& extensions) { ext.b_GL_ANGLE_framebuffer_blit = - extensions.find("GL_ANGLE_framebuffer_blit ") != std::string::npos; + HasExtension(extensions, "GL_ANGLE_framebuffer_blit"); ext.b_GL_ANGLE_framebuffer_multisample = - extensions.find("GL_ANGLE_framebuffer_multisample ") != std::string::npos; + HasExtension(extensions, "GL_ANGLE_framebuffer_multisample"); ext.b_GL_ANGLE_instanced_arrays = - extensions.find("GL_ANGLE_instanced_arrays ") != std::string::npos; + HasExtension(extensions, "GL_ANGLE_instanced_arrays"); ext.b_GL_ANGLE_request_extension = - extensions.find("GL_ANGLE_request_extension ") != std::string::npos; + HasExtension(extensions, "GL_ANGLE_request_extension"); ext.b_GL_ANGLE_robust_client_memory = - extensions.find("GL_ANGLE_robust_client_memory ") != std::string::npos; + HasExtension(extensions, "GL_ANGLE_robust_client_memory"); ext.b_GL_ANGLE_translated_shader_source = - extensions.find("GL_ANGLE_translated_shader_source ") != - std::string::npos; - ext.b_GL_APPLE_fence = - extensions.find("GL_APPLE_fence ") != std::string::npos; + HasExtension(extensions, "GL_ANGLE_translated_shader_source"); + ext.b_GL_APPLE_fence = HasExtension(extensions, "GL_APPLE_fence"); ext.b_GL_APPLE_vertex_array_object = - extensions.find("GL_APPLE_vertex_array_object ") != std::string::npos; + HasExtension(extensions, "GL_APPLE_vertex_array_object"); ext.b_GL_ARB_blend_func_extended = - extensions.find("GL_ARB_blend_func_extended ") != std::string::npos; - ext.b_GL_ARB_draw_buffers = - extensions.find("GL_ARB_draw_buffers ") != std::string::npos; + HasExtension(extensions, "GL_ARB_blend_func_extended"); + ext.b_GL_ARB_draw_buffers = HasExtension(extensions, "GL_ARB_draw_buffers"); ext.b_GL_ARB_draw_instanced = - extensions.find("GL_ARB_draw_instanced ") != std::string::npos; + HasExtension(extensions, "GL_ARB_draw_instanced"); ext.b_GL_ARB_get_program_binary = - extensions.find("GL_ARB_get_program_binary ") != std::string::npos; + HasExtension(extensions, "GL_ARB_get_program_binary"); ext.b_GL_ARB_instanced_arrays = - extensions.find("GL_ARB_instanced_arrays ") != std::string::npos; + HasExtension(extensions, "GL_ARB_instanced_arrays"); + ext.b_GL_ARB_internalformat_query = + HasExtension(extensions, "GL_ARB_internalformat_query"); ext.b_GL_ARB_map_buffer_range = - extensions.find("GL_ARB_map_buffer_range ") != std::string::npos; + HasExtension(extensions, "GL_ARB_map_buffer_range"); ext.b_GL_ARB_occlusion_query = - extensions.find("GL_ARB_occlusion_query ") != std::string::npos; + HasExtension(extensions, "GL_ARB_occlusion_query"); ext.b_GL_ARB_program_interface_query = - extensions.find("GL_ARB_program_interface_query ") != std::string::npos; - ext.b_GL_ARB_robustness = - extensions.find("GL_ARB_robustness ") != std::string::npos; + HasExtension(extensions, "GL_ARB_program_interface_query"); + ext.b_GL_ARB_robustness = HasExtension(extensions, "GL_ARB_robustness"); ext.b_GL_ARB_sampler_objects = - extensions.find("GL_ARB_sampler_objects ") != std::string::npos; + HasExtension(extensions, "GL_ARB_sampler_objects"); ext.b_GL_ARB_shader_image_load_store = - extensions.find("GL_ARB_shader_image_load_store ") != std::string::npos; - ext.b_GL_ARB_sync = extensions.find("GL_ARB_sync ") != std::string::npos; + HasExtension(extensions, "GL_ARB_shader_image_load_store"); + ext.b_GL_ARB_sync = HasExtension(extensions, "GL_ARB_sync"); + ext.b_GL_ARB_texture_multisample = + HasExtension(extensions, "GL_ARB_texture_multisample"); ext.b_GL_ARB_texture_storage = - extensions.find("GL_ARB_texture_storage ") != std::string::npos; - ext.b_GL_ARB_timer_query = - extensions.find("GL_ARB_timer_query ") != std::string::npos; + HasExtension(extensions, "GL_ARB_texture_storage"); + ext.b_GL_ARB_timer_query = HasExtension(extensions, "GL_ARB_timer_query"); ext.b_GL_ARB_transform_feedback2 = - extensions.find("GL_ARB_transform_feedback2 ") != std::string::npos; + HasExtension(extensions, "GL_ARB_transform_feedback2"); ext.b_GL_ARB_vertex_array_object = - extensions.find("GL_ARB_vertex_array_object ") != std::string::npos; + HasExtension(extensions, "GL_ARB_vertex_array_object"); ext.b_GL_CHROMIUM_bind_uniform_location = - extensions.find("GL_CHROMIUM_bind_uniform_location ") != - std::string::npos; + HasExtension(extensions, "GL_CHROMIUM_bind_uniform_location"); ext.b_GL_CHROMIUM_compressed_copy_texture = - extensions.find("GL_CHROMIUM_compressed_copy_texture ") != - std::string::npos; + HasExtension(extensions, "GL_CHROMIUM_compressed_copy_texture"); ext.b_GL_CHROMIUM_copy_compressed_texture = - extensions.find("GL_CHROMIUM_copy_compressed_texture ") != - std::string::npos; + HasExtension(extensions, "GL_CHROMIUM_copy_compressed_texture"); ext.b_GL_CHROMIUM_copy_texture = - extensions.find("GL_CHROMIUM_copy_texture ") != std::string::npos; + HasExtension(extensions, "GL_CHROMIUM_copy_texture"); ext.b_GL_CHROMIUM_gles_depth_binding_hack = - extensions.find("GL_CHROMIUM_gles_depth_binding_hack ") != - std::string::npos; + HasExtension(extensions, "GL_CHROMIUM_gles_depth_binding_hack"); ext.b_GL_CHROMIUM_glgetstringi_hack = - extensions.find("GL_CHROMIUM_glgetstringi_hack ") != std::string::npos; + HasExtension(extensions, "GL_CHROMIUM_glgetstringi_hack"); ext.b_GL_EXT_blend_func_extended = - extensions.find("GL_EXT_blend_func_extended ") != std::string::npos; - ext.b_GL_EXT_debug_marker = - extensions.find("GL_EXT_debug_marker ") != std::string::npos; + HasExtension(extensions, "GL_EXT_blend_func_extended"); + ext.b_GL_EXT_debug_marker = HasExtension(extensions, "GL_EXT_debug_marker"); ext.b_GL_EXT_direct_state_access = - extensions.find("GL_EXT_direct_state_access ") != std::string::npos; + HasExtension(extensions, "GL_EXT_direct_state_access"); ext.b_GL_EXT_discard_framebuffer = - extensions.find("GL_EXT_discard_framebuffer ") != std::string::npos; + HasExtension(extensions, "GL_EXT_discard_framebuffer"); ext.b_GL_EXT_disjoint_timer_query = - extensions.find("GL_EXT_disjoint_timer_query ") != std::string::npos; - ext.b_GL_EXT_draw_buffers = - extensions.find("GL_EXT_draw_buffers ") != std::string::npos; + HasExtension(extensions, "GL_EXT_disjoint_timer_query"); + ext.b_GL_EXT_draw_buffers = HasExtension(extensions, "GL_EXT_draw_buffers"); ext.b_GL_EXT_framebuffer_blit = - extensions.find("GL_EXT_framebuffer_blit ") != std::string::npos; + HasExtension(extensions, "GL_EXT_framebuffer_blit"); ext.b_GL_EXT_framebuffer_multisample = - extensions.find("GL_EXT_framebuffer_multisample ") != std::string::npos; + HasExtension(extensions, "GL_EXT_framebuffer_multisample"); ext.b_GL_EXT_framebuffer_object = - extensions.find("GL_EXT_framebuffer_object ") != std::string::npos; - ext.b_GL_EXT_gpu_shader4 = - extensions.find("GL_EXT_gpu_shader4 ") != std::string::npos; + HasExtension(extensions, "GL_EXT_framebuffer_object"); + ext.b_GL_EXT_gpu_shader4 = HasExtension(extensions, "GL_EXT_gpu_shader4"); + ext.b_GL_EXT_instanced_arrays = + HasExtension(extensions, "GL_EXT_instanced_arrays"); ext.b_GL_EXT_map_buffer_range = - extensions.find("GL_EXT_map_buffer_range ") != std::string::npos; + HasExtension(extensions, "GL_EXT_map_buffer_range"); ext.b_GL_EXT_multisampled_render_to_texture = - extensions.find("GL_EXT_multisampled_render_to_texture ") != - std::string::npos; + HasExtension(extensions, "GL_EXT_multisampled_render_to_texture"); ext.b_GL_EXT_occlusion_query_boolean = - extensions.find("GL_EXT_occlusion_query_boolean ") != std::string::npos; - ext.b_GL_EXT_robustness = - extensions.find("GL_EXT_robustness ") != std::string::npos; + HasExtension(extensions, "GL_EXT_occlusion_query_boolean"); + ext.b_GL_EXT_robustness = HasExtension(extensions, "GL_EXT_robustness"); ext.b_GL_EXT_shader_image_load_store = - extensions.find("GL_EXT_shader_image_load_store ") != std::string::npos; + HasExtension(extensions, "GL_EXT_shader_image_load_store"); + ext.b_GL_EXT_texture_buffer = + HasExtension(extensions, "GL_EXT_texture_buffer"); + ext.b_GL_EXT_texture_buffer_object = + HasExtension(extensions, "GL_EXT_texture_buffer_object"); ext.b_GL_EXT_texture_storage = - extensions.find("GL_EXT_texture_storage ") != std::string::npos; - ext.b_GL_EXT_timer_query = - extensions.find("GL_EXT_timer_query ") != std::string::npos; + HasExtension(extensions, "GL_EXT_texture_storage"); + ext.b_GL_EXT_timer_query = HasExtension(extensions, "GL_EXT_timer_query"); ext.b_GL_EXT_transform_feedback = - extensions.find("GL_EXT_transform_feedback ") != std::string::npos; + HasExtension(extensions, "GL_EXT_transform_feedback"); ext.b_GL_EXT_unpack_subimage = - extensions.find("GL_EXT_unpack_subimage ") != std::string::npos; + HasExtension(extensions, "GL_EXT_unpack_subimage"); ext.b_GL_IMG_multisampled_render_to_texture = - extensions.find("GL_IMG_multisampled_render_to_texture ") != - std::string::npos; + HasExtension(extensions, "GL_IMG_multisampled_render_to_texture"); ext.b_GL_INTEL_framebuffer_CMAA = - extensions.find("GL_INTEL_framebuffer_CMAA ") != std::string::npos; + HasExtension(extensions, "GL_INTEL_framebuffer_CMAA"); ext.b_GL_KHR_blend_equation_advanced = - extensions.find("GL_KHR_blend_equation_advanced ") != std::string::npos; - ext.b_GL_KHR_debug = extensions.find("GL_KHR_debug ") != std::string::npos; - ext.b_GL_KHR_robustness = - extensions.find("GL_KHR_robustness ") != std::string::npos; + HasExtension(extensions, "GL_KHR_blend_equation_advanced"); + ext.b_GL_KHR_debug = HasExtension(extensions, "GL_KHR_debug"); + ext.b_GL_KHR_robustness = HasExtension(extensions, "GL_KHR_robustness"); ext.b_GL_NV_blend_equation_advanced = - extensions.find("GL_NV_blend_equation_advanced ") != std::string::npos; - ext.b_GL_NV_fence = extensions.find("GL_NV_fence ") != std::string::npos; + HasExtension(extensions, "GL_NV_blend_equation_advanced"); + ext.b_GL_NV_fence = HasExtension(extensions, "GL_NV_fence"); ext.b_GL_NV_framebuffer_mixed_samples = - extensions.find("GL_NV_framebuffer_mixed_samples ") != std::string::npos; - ext.b_GL_NV_path_rendering = - extensions.find("GL_NV_path_rendering ") != std::string::npos; - ext.b_GL_OES_EGL_image = - extensions.find("GL_OES_EGL_image ") != std::string::npos; + HasExtension(extensions, "GL_NV_framebuffer_mixed_samples"); + ext.b_GL_NV_path_rendering = HasExtension(extensions, "GL_NV_path_rendering"); + ext.b_GL_OES_EGL_image = HasExtension(extensions, "GL_OES_EGL_image"); ext.b_GL_OES_get_program_binary = - extensions.find("GL_OES_get_program_binary ") != std::string::npos; - ext.b_GL_OES_mapbuffer = - extensions.find("GL_OES_mapbuffer ") != std::string::npos; + HasExtension(extensions, "GL_OES_get_program_binary"); + ext.b_GL_OES_mapbuffer = HasExtension(extensions, "GL_OES_mapbuffer"); + ext.b_GL_OES_texture_buffer = + HasExtension(extensions, "GL_OES_texture_buffer"); ext.b_GL_OES_vertex_array_object = - extensions.find("GL_OES_vertex_array_object ") != std::string::npos; + HasExtension(extensions, "GL_OES_vertex_array_object"); if (ext.b_GL_INTEL_framebuffer_CMAA) { fn.glApplyFramebufferAttachmentCMAAINTELFn = @@ -1453,7 +1445,8 @@ void DriverGL::InitializeDynamicBindings( GetGLProcAddress("glGetIntegervRobustANGLE")); } - if (ver->IsAtLeastGL(4u, 2u) || ver->IsAtLeastGLES(3u, 0u)) { + if (ver->IsAtLeastGL(4u, 2u) || ver->IsAtLeastGLES(3u, 0u) || + ext.b_GL_ARB_internalformat_query) { fn.glGetInternalformativFn = reinterpret_cast<glGetInternalformativProc>( GetGLProcAddress("glGetInternalformativ")); } @@ -1464,6 +1457,12 @@ void DriverGL::InitializeDynamicBindings( GetGLProcAddress("glGetInternalformativRobustANGLE")); } + if (ver->IsAtLeastGL(3u, 2u) || ver->IsAtLeastGLES(3u, 1u) || + ext.b_GL_ARB_texture_multisample) { + fn.glGetMultisamplefvFn = reinterpret_cast<glGetMultisamplefvProc>( + GetGLProcAddress("glGetMultisamplefv")); + } + if (ext.b_GL_ANGLE_robust_client_memory) { fn.glGetMultisamplefvRobustANGLEFn = reinterpret_cast<glGetMultisamplefvRobustANGLEProc>( @@ -2020,6 +2019,11 @@ void DriverGL::InitializeDynamicBindings( GetGLProcAddress("glPointParameteri")); } + if (!ver->is_es) { + fn.glPolygonModeFn = + reinterpret_cast<glPolygonModeProc>(GetGLProcAddress("glPolygonMode")); + } + if (ver->IsAtLeastGL(4u, 3u) || ver->IsAtLeastGLES(3u, 2u)) { fn.glPopDebugGroupFn = reinterpret_cast<glPopDebugGroupProc>( GetGLProcAddress("glPopDebugGroup")); @@ -2271,6 +2275,29 @@ void DriverGL::InitializeDynamicBindings( reinterpret_cast<glTestFenceNVProc>(GetGLProcAddress("glTestFenceNV")); } + if (ver->IsAtLeastGLES(3u, 2u) || ver->IsAtLeastGL(3u, 1u)) { + fn.glTexBufferFn = + reinterpret_cast<glTexBufferProc>(GetGLProcAddress("glTexBuffer")); + } else if (ext.b_GL_OES_texture_buffer) { + fn.glTexBufferFn = + reinterpret_cast<glTexBufferProc>(GetGLProcAddress("glTexBufferOES")); + } else if (ext.b_GL_EXT_texture_buffer_object || + ext.b_GL_EXT_texture_buffer) { + fn.glTexBufferFn = + reinterpret_cast<glTexBufferProc>(GetGLProcAddress("glTexBufferEXT")); + } + + if (ver->IsAtLeastGL(4u, 3u) || ver->IsAtLeastGLES(3u, 2u)) { + fn.glTexBufferRangeFn = reinterpret_cast<glTexBufferRangeProc>( + GetGLProcAddress("glTexBufferRange")); + } else if (ext.b_GL_OES_texture_buffer) { + fn.glTexBufferRangeFn = reinterpret_cast<glTexBufferRangeProc>( + GetGLProcAddress("glTexBufferRangeOES")); + } else if (ext.b_GL_EXT_texture_buffer) { + fn.glTexBufferRangeFn = reinterpret_cast<glTexBufferRangeProc>( + GetGLProcAddress("glTexBufferRangeEXT")); + } + if (ext.b_GL_ANGLE_robust_client_memory) { fn.glTexImage2DRobustANGLEFn = reinterpret_cast<glTexImage2DRobustANGLEProc>( @@ -2449,6 +2476,10 @@ void DriverGL::InitializeDynamicBindings( fn.glVertexAttribDivisorANGLEFn = reinterpret_cast<glVertexAttribDivisorANGLEProc>( GetGLProcAddress("glVertexAttribDivisorANGLE")); + } else if (ext.b_GL_EXT_instanced_arrays) { + fn.glVertexAttribDivisorANGLEFn = + reinterpret_cast<glVertexAttribDivisorANGLEProc>( + GetGLProcAddress("glVertexAttribDivisorEXT")); } if (ver->IsAtLeastGL(3u, 0u) || ver->IsAtLeastGLES(3u, 0u)) { @@ -3560,6 +3591,10 @@ void GLApiBase::glGetInternalformativRobustANGLEFn(GLenum target, bufSize, length, params); } +void GLApiBase::glGetMultisamplefvFn(GLenum pname, GLuint index, GLfloat* val) { + driver_->fn.glGetMultisamplefvFn(pname, index, val); +} + void GLApiBase::glGetMultisamplefvRobustANGLEFn(GLenum pname, GLuint index, GLsizei bufSize, @@ -4271,6 +4306,10 @@ void GLApiBase::glPointParameteriFn(GLenum pname, GLint param) { driver_->fn.glPointParameteriFn(pname, param); } +void GLApiBase::glPolygonModeFn(GLenum face, GLenum mode) { + driver_->fn.glPolygonModeFn(face, mode); +} + void GLApiBase::glPolygonOffsetFn(GLfloat factor, GLfloat units) { driver_->fn.glPolygonOffsetFn(factor, units); } @@ -4630,6 +4669,20 @@ GLboolean GLApiBase::glTestFenceNVFn(GLuint fence) { return driver_->fn.glTestFenceNVFn(fence); } +void GLApiBase::glTexBufferFn(GLenum target, + GLenum internalformat, + GLuint buffer) { + driver_->fn.glTexBufferFn(target, internalformat, buffer); +} + +void GLApiBase::glTexBufferRangeFn(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) { + driver_->fn.glTexBufferRangeFn(target, internalformat, buffer, offset, size); +} + void GLApiBase::glTexImage2DFn(GLenum target, GLint level, GLint internalformat, @@ -5121,10 +5174,8 @@ void GLApiBase::glViewportFn(GLint x, GLint y, GLsizei width, GLsizei height) { driver_->fn.glViewportFn(x, y, width, height); } -GLenum GLApiBase::glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) { - return driver_->fn.glWaitSyncFn(sync, flags, timeout); +void GLApiBase::glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) { + driver_->fn.glWaitSyncFn(sync, flags, timeout); } void TraceGLApi::glActiveTextureFn(GLenum texture) { @@ -6390,6 +6441,13 @@ void TraceGLApi::glGetInternalformativRobustANGLEFn(GLenum target, bufSize, length, params); } +void TraceGLApi::glGetMultisamplefvFn(GLenum pname, + GLuint index, + GLfloat* val) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glGetMultisamplefv") + gl_api_->glGetMultisamplefvFn(pname, index, val); +} + void TraceGLApi::glGetMultisamplefvRobustANGLEFn(GLenum pname, GLuint index, GLsizei bufSize, @@ -7231,6 +7289,11 @@ void TraceGLApi::glPointParameteriFn(GLenum pname, GLint param) { gl_api_->glPointParameteriFn(pname, param); } +void TraceGLApi::glPolygonModeFn(GLenum face, GLenum mode) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glPolygonMode") + gl_api_->glPolygonModeFn(face, mode); +} + void TraceGLApi::glPolygonOffsetFn(GLfloat factor, GLfloat units) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glPolygonOffset") gl_api_->glPolygonOffsetFn(factor, units); @@ -7655,6 +7718,22 @@ GLboolean TraceGLApi::glTestFenceNVFn(GLuint fence) { return gl_api_->glTestFenceNVFn(fence); } +void TraceGLApi::glTexBufferFn(GLenum target, + GLenum internalformat, + GLuint buffer) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glTexBuffer") + gl_api_->glTexBufferFn(target, internalformat, buffer); +} + +void TraceGLApi::glTexBufferRangeFn(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glTexBufferRange") + gl_api_->glTexBufferRangeFn(target, internalformat, buffer, offset, size); +} + void TraceGLApi::glTexImage2DFn(GLenum target, GLint level, GLint internalformat, @@ -8222,11 +8301,9 @@ void TraceGLApi::glViewportFn(GLint x, GLint y, GLsizei width, GLsizei height) { gl_api_->glViewportFn(x, y, width, height); } -GLenum TraceGLApi::glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) { +void TraceGLApi::glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glWaitSync") - return gl_api_->glWaitSyncFn(sync, flags, timeout); + gl_api_->glWaitSyncFn(sync, flags, timeout); } void DebugGLApi::glActiveTextureFn(GLenum texture) { @@ -9891,6 +9968,15 @@ void DebugGLApi::glGetInternalformativRobustANGLEFn(GLenum target, bufSize, length, params); } +void DebugGLApi::glGetMultisamplefvFn(GLenum pname, + GLuint index, + GLfloat* val) { + GL_SERVICE_LOG("glGetMultisamplefv" + << "(" << GLEnums::GetStringEnum(pname) << ", " << index + << ", " << static_cast<const void*>(val) << ")"); + gl_api_->glGetMultisamplefvFn(pname, index, val); +} + void DebugGLApi::glGetMultisamplefvRobustANGLEFn(GLenum pname, GLuint index, GLsizei bufSize, @@ -11015,6 +11101,13 @@ void DebugGLApi::glPointParameteriFn(GLenum pname, GLint param) { gl_api_->glPointParameteriFn(pname, param); } +void DebugGLApi::glPolygonModeFn(GLenum face, GLenum mode) { + GL_SERVICE_LOG("glPolygonMode" + << "(" << GLEnums::GetStringEnum(face) << ", " + << GLEnums::GetStringEnum(mode) << ")"); + gl_api_->glPolygonModeFn(face, mode); +} + void DebugGLApi::glPolygonOffsetFn(GLfloat factor, GLfloat units) { GL_SERVICE_LOG("glPolygonOffset" << "(" << factor << ", " << units << ")"); @@ -11581,6 +11674,28 @@ GLboolean DebugGLApi::glTestFenceNVFn(GLuint fence) { return result; } +void DebugGLApi::glTexBufferFn(GLenum target, + GLenum internalformat, + GLuint buffer) { + GL_SERVICE_LOG("glTexBuffer" + << "(" << GLEnums::GetStringEnum(target) << ", " + << GLEnums::GetStringEnum(internalformat) << ", " << buffer + << ")"); + gl_api_->glTexBufferFn(target, internalformat, buffer); +} + +void DebugGLApi::glTexBufferRangeFn(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) { + GL_SERVICE_LOG("glTexBufferRange" + << "(" << GLEnums::GetStringEnum(target) << ", " + << GLEnums::GetStringEnum(internalformat) << ", " << buffer + << ", " << offset << ", " << size << ")"); + gl_api_->glTexBufferRangeFn(target, internalformat, buffer, offset, size); +} + void DebugGLApi::glTexImage2DFn(GLenum target, GLint level, GLint internalformat, @@ -12325,14 +12440,10 @@ void DebugGLApi::glViewportFn(GLint x, GLint y, GLsizei width, GLsizei height) { gl_api_->glViewportFn(x, y, width, height); } -GLenum DebugGLApi::glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) { +void DebugGLApi::glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) { GL_SERVICE_LOG("glWaitSync" << "(" << sync << ", " << flags << ", " << timeout << ")"); - GLenum result = gl_api_->glWaitSyncFn(sync, flags, timeout); - GL_SERVICE_LOG("GL_RESULT: " << result); - return result; + gl_api_->glWaitSyncFn(sync, flags, timeout); } void NoContextGLApi::glActiveTextureFn(GLenum texture) { @@ -13743,6 +13854,15 @@ void NoContextGLApi::glGetInternalformativRobustANGLEFn(GLenum target, "current GL context"; } +void NoContextGLApi::glGetMultisamplefvFn(GLenum pname, + GLuint index, + GLfloat* val) { + NOTREACHED() + << "Trying to call glGetMultisamplefv() without current GL context"; + LOG(ERROR) + << "Trying to call glGetMultisamplefv() without current GL context"; +} + void NoContextGLApi::glGetMultisamplefvRobustANGLEFn(GLenum pname, GLuint index, GLsizei bufSize, @@ -14711,6 +14831,11 @@ void NoContextGLApi::glPointParameteriFn(GLenum pname, GLint param) { LOG(ERROR) << "Trying to call glPointParameteri() without current GL context"; } +void NoContextGLApi::glPolygonModeFn(GLenum face, GLenum mode) { + NOTREACHED() << "Trying to call glPolygonMode() without current GL context"; + LOG(ERROR) << "Trying to call glPolygonMode() without current GL context"; +} + void NoContextGLApi::glPolygonOffsetFn(GLfloat factor, GLfloat units) { NOTREACHED() << "Trying to call glPolygonOffset() without current GL context"; LOG(ERROR) << "Trying to call glPolygonOffset() without current GL context"; @@ -15185,6 +15310,23 @@ GLboolean NoContextGLApi::glTestFenceNVFn(GLuint fence) { return GL_FALSE; } +void NoContextGLApi::glTexBufferFn(GLenum target, + GLenum internalformat, + GLuint buffer) { + NOTREACHED() << "Trying to call glTexBuffer() without current GL context"; + LOG(ERROR) << "Trying to call glTexBuffer() without current GL context"; +} + +void NoContextGLApi::glTexBufferRangeFn(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) { + NOTREACHED() + << "Trying to call glTexBufferRange() without current GL context"; + LOG(ERROR) << "Trying to call glTexBufferRange() without current GL context"; +} + void NoContextGLApi::glTexImage2DFn(GLenum target, GLint level, GLint internalformat, @@ -15814,12 +15956,11 @@ void NoContextGLApi::glViewportFn(GLint x, LOG(ERROR) << "Trying to call glViewport() without current GL context"; } -GLenum NoContextGLApi::glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) { +void NoContextGLApi::glWaitSyncFn(GLsync sync, + GLbitfield flags, + GLuint64 timeout) { NOTREACHED() << "Trying to call glWaitSync() without current GL context"; LOG(ERROR) << "Trying to call glWaitSync() without current GL context"; - return static_cast<GLenum>(0); } } // namespace gl diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.h b/chromium/ui/gl/gl_bindings_autogen_gl.h index db088a34b09..816a0c5dda1 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_autogen_gl.h @@ -11,6 +11,8 @@ #ifndef UI_GL_GL_BINDINGS_AUTOGEN_GL_H_ #define UI_GL_GL_BINDINGS_AUTOGEN_GL_H_ +#include <string> + namespace gl { class GLContext; @@ -611,6 +613,9 @@ typedef void(GL_BINDING_CALL* glGetInternalformativRobustANGLEProc)( GLsizei bufSize, GLsizei* length, GLint* params); +typedef void(GL_BINDING_CALL* glGetMultisamplefvProc)(GLenum pname, + GLuint index, + GLfloat* val); typedef void(GL_BINDING_CALL* glGetMultisamplefvRobustANGLEProc)( GLenum pname, GLuint index, @@ -1014,6 +1019,7 @@ typedef void(GL_BINDING_CALL* glPathStencilFuncNVProc)(GLenum func, typedef void(GL_BINDING_CALL* glPauseTransformFeedbackProc)(void); typedef void(GL_BINDING_CALL* glPixelStoreiProc)(GLenum pname, GLint param); typedef void(GL_BINDING_CALL* glPointParameteriProc)(GLenum pname, GLint param); +typedef void(GL_BINDING_CALL* glPolygonModeProc)(GLenum face, GLenum mode); typedef void(GL_BINDING_CALL* glPolygonOffsetProc)(GLfloat factor, GLfloat units); typedef void(GL_BINDING_CALL* glPopDebugGroupProc)(); @@ -1223,6 +1229,14 @@ typedef void(GL_BINDING_CALL* glStencilThenCoverStrokePathNVProc)( GLenum coverMode); typedef GLboolean(GL_BINDING_CALL* glTestFenceAPPLEProc)(GLuint fence); typedef GLboolean(GL_BINDING_CALL* glTestFenceNVProc)(GLuint fence); +typedef void(GL_BINDING_CALL* glTexBufferProc)(GLenum target, + GLenum internalformat, + GLuint buffer); +typedef void(GL_BINDING_CALL* glTexBufferRangeProc)(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size); typedef void(GL_BINDING_CALL* glTexImage2DProc)(GLenum target, GLint level, GLint internalformat, @@ -1525,9 +1539,9 @@ typedef void(GL_BINDING_CALL* glViewportProc)(GLint x, GLint y, GLsizei width, GLsizei height); -typedef GLenum(GL_BINDING_CALL* glWaitSyncProc)(GLsync sync, - GLbitfield flags, - GLuint64 timeout); +typedef void(GL_BINDING_CALL* glWaitSyncProc)(GLsync sync, + GLbitfield flags, + GLuint64 timeout); struct ExtensionsGL { bool b_GL_ANGLE_framebuffer_blit; @@ -1543,6 +1557,7 @@ struct ExtensionsGL { bool b_GL_ARB_draw_instanced; bool b_GL_ARB_get_program_binary; bool b_GL_ARB_instanced_arrays; + bool b_GL_ARB_internalformat_query; bool b_GL_ARB_map_buffer_range; bool b_GL_ARB_occlusion_query; bool b_GL_ARB_program_interface_query; @@ -1550,6 +1565,7 @@ struct ExtensionsGL { bool b_GL_ARB_sampler_objects; bool b_GL_ARB_shader_image_load_store; bool b_GL_ARB_sync; + bool b_GL_ARB_texture_multisample; bool b_GL_ARB_texture_storage; bool b_GL_ARB_timer_query; bool b_GL_ARB_transform_feedback2; @@ -1570,11 +1586,14 @@ struct ExtensionsGL { bool b_GL_EXT_framebuffer_multisample; bool b_GL_EXT_framebuffer_object; bool b_GL_EXT_gpu_shader4; + bool b_GL_EXT_instanced_arrays; bool b_GL_EXT_map_buffer_range; bool b_GL_EXT_multisampled_render_to_texture; bool b_GL_EXT_occlusion_query_boolean; bool b_GL_EXT_robustness; bool b_GL_EXT_shader_image_load_store; + bool b_GL_EXT_texture_buffer; + bool b_GL_EXT_texture_buffer_object; bool b_GL_EXT_texture_storage; bool b_GL_EXT_timer_query; bool b_GL_EXT_transform_feedback; @@ -1591,6 +1610,7 @@ struct ExtensionsGL { bool b_GL_OES_EGL_image; bool b_GL_OES_get_program_binary; bool b_GL_OES_mapbuffer; + bool b_GL_OES_texture_buffer; bool b_GL_OES_vertex_array_object; }; @@ -1769,6 +1789,7 @@ struct ProcsGL { glGetIntegervRobustANGLEProc glGetIntegervRobustANGLEFn; glGetInternalformativProc glGetInternalformativFn; glGetInternalformativRobustANGLEProc glGetInternalformativRobustANGLEFn; + glGetMultisamplefvProc glGetMultisamplefvFn; glGetMultisamplefvRobustANGLEProc glGetMultisamplefvRobustANGLEFn; glGetnUniformfvRobustANGLEProc glGetnUniformfvRobustANGLEFn; glGetnUniformivRobustANGLEProc glGetnUniformivRobustANGLEFn; @@ -1879,6 +1900,7 @@ struct ProcsGL { glPauseTransformFeedbackProc glPauseTransformFeedbackFn; glPixelStoreiProc glPixelStoreiFn; glPointParameteriProc glPointParameteriFn; + glPolygonModeProc glPolygonModeFn; glPolygonOffsetProc glPolygonOffsetFn; glPopDebugGroupProc glPopDebugGroupFn; glPopGroupMarkerEXTProc glPopGroupMarkerEXTFn; @@ -1934,6 +1956,8 @@ struct ProcsGL { glStencilThenCoverStrokePathNVProc glStencilThenCoverStrokePathNVFn; glTestFenceAPPLEProc glTestFenceAPPLEFn; glTestFenceNVProc glTestFenceNVFn; + glTexBufferProc glTexBufferFn; + glTexBufferRangeProc glTexBufferRangeFn; glTexImage2DProc glTexImage2DFn; glTexImage2DRobustANGLEProc glTexImage2DRobustANGLEFn; glTexImage3DProc glTexImage3DFn; @@ -2014,6 +2038,8 @@ class GL_EXPORT GLApi { GLApi(); virtual ~GLApi(); + virtual void SetDisabledExtensions(const std::string& disabled_extensions) {} + virtual void glActiveTextureFn(GLenum texture) = 0; virtual void glApplyFramebufferAttachmentCMAAINTELFn(void) = 0; virtual void glAttachShaderFn(GLuint program, GLuint shader) = 0; @@ -2543,6 +2569,9 @@ class GL_EXPORT GLApi { GLsizei bufSize, GLsizei* length, GLint* params) = 0; + virtual void glGetMultisamplefvFn(GLenum pname, + GLuint index, + GLfloat* val) = 0; virtual void glGetMultisamplefvRobustANGLEFn(GLenum pname, GLuint index, GLsizei bufSize, @@ -2896,6 +2925,7 @@ class GL_EXPORT GLApi { virtual void glPauseTransformFeedbackFn(void) = 0; virtual void glPixelStoreiFn(GLenum pname, GLint param) = 0; virtual void glPointParameteriFn(GLenum pname, GLint param) = 0; + virtual void glPolygonModeFn(GLenum face, GLenum mode) = 0; virtual void glPolygonOffsetFn(GLfloat factor, GLfloat units) = 0; virtual void glPopDebugGroupFn() = 0; virtual void glPopGroupMarkerEXTFn(void) = 0; @@ -3082,6 +3112,14 @@ class GL_EXPORT GLApi { GLenum coverMode) = 0; virtual GLboolean glTestFenceAPPLEFn(GLuint fence) = 0; virtual GLboolean glTestFenceNVFn(GLuint fence) = 0; + virtual void glTexBufferFn(GLenum target, + GLenum internalformat, + GLuint buffer) = 0; + virtual void glTexBufferRangeFn(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) = 0; virtual void glTexImage2DFn(GLenum target, GLint level, GLint internalformat, @@ -3356,9 +3394,9 @@ class GL_EXPORT GLApi { GLint y, GLsizei width, GLsizei height) = 0; - virtual GLenum glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) = 0; + virtual void glWaitSyncFn(GLsync sync, + GLbitfield flags, + GLuint64 timeout) = 0; }; } // namespace gl @@ -3596,6 +3634,7 @@ class GL_EXPORT GLApi { ::gl::g_current_gl_context->glGetInternalformativFn #define glGetInternalformativRobustANGLE \ ::gl::g_current_gl_context->glGetInternalformativRobustANGLEFn +#define glGetMultisamplefv ::gl::g_current_gl_context->glGetMultisamplefvFn #define glGetMultisamplefvRobustANGLE \ ::gl::g_current_gl_context->glGetMultisamplefvRobustANGLEFn #define glGetnUniformfvRobustANGLE \ @@ -3756,6 +3795,7 @@ class GL_EXPORT GLApi { ::gl::g_current_gl_context->glPauseTransformFeedbackFn #define glPixelStorei ::gl::g_current_gl_context->glPixelStoreiFn #define glPointParameteri ::gl::g_current_gl_context->glPointParameteriFn +#define glPolygonMode ::gl::g_current_gl_context->glPolygonModeFn #define glPolygonOffset ::gl::g_current_gl_context->glPolygonOffsetFn #define glPopDebugGroup ::gl::g_current_gl_context->glPopDebugGroupFn #define glPopGroupMarkerEXT ::gl::g_current_gl_context->glPopGroupMarkerEXTFn @@ -3833,6 +3873,8 @@ class GL_EXPORT GLApi { ::gl::g_current_gl_context->glStencilThenCoverStrokePathNVFn #define glTestFenceAPPLE ::gl::g_current_gl_context->glTestFenceAPPLEFn #define glTestFenceNV ::gl::g_current_gl_context->glTestFenceNVFn +#define glTexBuffer ::gl::g_current_gl_context->glTexBufferFn +#define glTexBufferRange ::gl::g_current_gl_context->glTexBufferRangeFn #define glTexImage2D ::gl::g_current_gl_context->glTexImage2DFn #define glTexImage2DRobustANGLE \ ::gl::g_current_gl_context->glTexImage2DRobustANGLEFn diff --git a/chromium/ui/gl/gl_bindings_autogen_glx.cc b/chromium/ui/gl/gl_bindings_autogen_glx.cc index ef9f9ef9152..ed79f1dd778 100644 --- a/chromium/ui/gl/gl_bindings_autogen_glx.cc +++ b/chromium/ui/gl/gl_bindings_autogen_glx.cc @@ -114,26 +114,22 @@ void DriverGLX::InitializeStaticBindings() { } void DriverGLX::InitializeExtensionBindings() { - std::string extensions(GetPlatformExtensions()); - extensions += " "; + std::string platform_extensions(GetPlatformExtensions()); + ExtensionSet extensions(MakeExtensionSet(platform_extensions)); ALLOW_UNUSED_LOCAL(extensions); ext.b_GLX_ARB_create_context = - extensions.find("GLX_ARB_create_context ") != std::string::npos; - ext.b_GLX_EXT_swap_control = - extensions.find("GLX_EXT_swap_control ") != std::string::npos; + HasExtension(extensions, "GLX_ARB_create_context"); + ext.b_GLX_EXT_swap_control = HasExtension(extensions, "GLX_EXT_swap_control"); ext.b_GLX_EXT_texture_from_pixmap = - extensions.find("GLX_EXT_texture_from_pixmap ") != std::string::npos; + HasExtension(extensions, "GLX_EXT_texture_from_pixmap"); ext.b_GLX_MESA_copy_sub_buffer = - extensions.find("GLX_MESA_copy_sub_buffer ") != std::string::npos; + HasExtension(extensions, "GLX_MESA_copy_sub_buffer"); ext.b_GLX_MESA_swap_control = - extensions.find("GLX_MESA_swap_control ") != std::string::npos; - ext.b_GLX_OML_sync_control = - extensions.find("GLX_OML_sync_control ") != std::string::npos; - ext.b_GLX_SGIX_fbconfig = - extensions.find("GLX_SGIX_fbconfig ") != std::string::npos; - ext.b_GLX_SGI_video_sync = - extensions.find("GLX_SGI_video_sync ") != std::string::npos; + HasExtension(extensions, "GLX_MESA_swap_control"); + ext.b_GLX_OML_sync_control = HasExtension(extensions, "GLX_OML_sync_control"); + ext.b_GLX_SGIX_fbconfig = HasExtension(extensions, "GLX_SGIX_fbconfig"); + ext.b_GLX_SGI_video_sync = HasExtension(extensions, "GLX_SGI_video_sync"); if (ext.b_GLX_EXT_texture_from_pixmap) { fn.glXBindTexImageEXTFn = reinterpret_cast<glXBindTexImageEXTProc>( diff --git a/chromium/ui/gl/gl_bindings_autogen_glx.h b/chromium/ui/gl/gl_bindings_autogen_glx.h index 2f75778150d..2cd87ed127d 100644 --- a/chromium/ui/gl/gl_bindings_autogen_glx.h +++ b/chromium/ui/gl/gl_bindings_autogen_glx.h @@ -11,6 +11,8 @@ #ifndef UI_GL_GL_BINDINGS_AUTOGEN_GLX_H_ #define UI_GL_GL_BINDINGS_AUTOGEN_GLX_H_ +#include <string> + namespace gl { class GLContext; @@ -229,6 +231,8 @@ class GL_EXPORT GLXApi { GLXApi(); virtual ~GLXApi(); + virtual void SetDisabledExtensions(const std::string& disabled_extensions) {} + virtual void glXBindTexImageEXTFn(Display* dpy, GLXDrawable drawable, int buffer, diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.cc b/chromium/ui/gl/gl_bindings_autogen_mock.cc index 616ac6918c8..812f9103eac 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.cc +++ b/chromium/ui/gl/gl_bindings_autogen_mock.cc @@ -1715,6 +1715,13 @@ MockGLInterface::Mock_glGetInternalformativRobustANGLE(GLenum target, bufSize, length, params); } +void GL_BINDING_CALL MockGLInterface::Mock_glGetMultisamplefv(GLenum pname, + GLuint index, + GLfloat* val) { + MakeFunctionUnique("glGetMultisamplefv"); + interface_->GetMultisamplefv(pname, index, val); +} + void GL_BINDING_CALL MockGLInterface::Mock_glGetMultisamplefvRobustANGLE(GLenum pname, GLuint index, @@ -2797,6 +2804,12 @@ void GL_BINDING_CALL MockGLInterface::Mock_glPointParameteri(GLenum pname, interface_->PointParameteri(pname, param); } +void GL_BINDING_CALL MockGLInterface::Mock_glPolygonMode(GLenum face, + GLenum mode) { + MakeFunctionUnique("glPolygonMode"); + interface_->PolygonMode(face, mode); +} + void GL_BINDING_CALL MockGLInterface::Mock_glPolygonOffset(GLfloat factor, GLfloat units) { MakeFunctionUnique("glPolygonOffset"); @@ -3281,6 +3294,57 @@ GLboolean GL_BINDING_CALL MockGLInterface::Mock_glTestFenceNV(GLuint fence) { return interface_->TestFenceNV(fence); } +void GL_BINDING_CALL MockGLInterface::Mock_glTexBuffer(GLenum target, + GLenum internalformat, + GLuint buffer) { + MakeFunctionUnique("glTexBuffer"); + interface_->TexBuffer(target, internalformat, buffer); +} + +void GL_BINDING_CALL MockGLInterface::Mock_glTexBufferEXT(GLenum target, + GLenum internalformat, + GLuint buffer) { + MakeFunctionUnique("glTexBufferEXT"); + interface_->TexBuffer(target, internalformat, buffer); +} + +void GL_BINDING_CALL MockGLInterface::Mock_glTexBufferOES(GLenum target, + GLenum internalformat, + GLuint buffer) { + MakeFunctionUnique("glTexBufferOES"); + interface_->TexBuffer(target, internalformat, buffer); +} + +void GL_BINDING_CALL +MockGLInterface::Mock_glTexBufferRange(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) { + MakeFunctionUnique("glTexBufferRange"); + interface_->TexBufferRange(target, internalformat, buffer, offset, size); +} + +void GL_BINDING_CALL +MockGLInterface::Mock_glTexBufferRangeEXT(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) { + MakeFunctionUnique("glTexBufferRangeEXT"); + interface_->TexBufferRange(target, internalformat, buffer, offset, size); +} + +void GL_BINDING_CALL +MockGLInterface::Mock_glTexBufferRangeOES(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) { + MakeFunctionUnique("glTexBufferRangeOES"); + interface_->TexBufferRange(target, internalformat, buffer, offset, size); +} + void GL_BINDING_CALL MockGLInterface::Mock_glTexImage2D(GLenum target, GLint level, GLint internalformat, @@ -3881,6 +3945,12 @@ MockGLInterface::Mock_glVertexAttribDivisorARB(GLuint index, GLuint divisor) { interface_->VertexAttribDivisorANGLE(index, divisor); } +void GL_BINDING_CALL +MockGLInterface::Mock_glVertexAttribDivisorEXT(GLuint index, GLuint divisor) { + MakeFunctionUnique("glVertexAttribDivisorEXT"); + interface_->VertexAttribDivisorANGLE(index, divisor); +} + void GL_BINDING_CALL MockGLInterface::Mock_glVertexAttribI4i(GLuint indx, GLint x, GLint y, @@ -3940,11 +4010,11 @@ void GL_BINDING_CALL MockGLInterface::Mock_glViewport(GLint x, interface_->Viewport(x, y, width, height); } -GLenum GL_BINDING_CALL MockGLInterface::Mock_glWaitSync(GLsync sync, - GLbitfield flags, - GLuint64 timeout) { +void GL_BINDING_CALL MockGLInterface::Mock_glWaitSync(GLsync sync, + GLbitfield flags, + GLuint64 timeout) { MakeFunctionUnique("glWaitSync"); - return interface_->WaitSync(sync, flags, timeout); + interface_->WaitSync(sync, flags, timeout); } static void MockInvalidFunction() { @@ -4445,6 +4515,8 @@ MockGLInterface::GetGLProcAddress(const char* name) { if (strcmp(name, "glGetInternalformativRobustANGLE") == 0) return reinterpret_cast<GLFunctionPointerType>( Mock_glGetInternalformativRobustANGLE); + if (strcmp(name, "glGetMultisamplefv") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glGetMultisamplefv); if (strcmp(name, "glGetMultisamplefvRobustANGLE") == 0) return reinterpret_cast<GLFunctionPointerType>( Mock_glGetMultisamplefvRobustANGLE); @@ -4761,6 +4833,8 @@ MockGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<GLFunctionPointerType>(Mock_glPixelStorei); if (strcmp(name, "glPointParameteri") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glPointParameteri); + if (strcmp(name, "glPolygonMode") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glPolygonMode); if (strcmp(name, "glPolygonOffset") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glPolygonOffset); if (strcmp(name, "glPopDebugGroup") == 0) @@ -4897,6 +4971,18 @@ MockGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<GLFunctionPointerType>(Mock_glTestFenceAPPLE); if (strcmp(name, "glTestFenceNV") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glTestFenceNV); + if (strcmp(name, "glTexBuffer") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glTexBuffer); + if (strcmp(name, "glTexBufferEXT") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glTexBufferEXT); + if (strcmp(name, "glTexBufferOES") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glTexBufferOES); + if (strcmp(name, "glTexBufferRange") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glTexBufferRange); + if (strcmp(name, "glTexBufferRangeEXT") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glTexBufferRangeEXT); + if (strcmp(name, "glTexBufferRangeOES") == 0) + return reinterpret_cast<GLFunctionPointerType>(Mock_glTexBufferRangeOES); if (strcmp(name, "glTexImage2D") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glTexImage2D); if (strcmp(name, "glTexImage2DRobustANGLE") == 0) @@ -5049,6 +5135,9 @@ MockGLInterface::GetGLProcAddress(const char* name) { if (strcmp(name, "glVertexAttribDivisorARB") == 0) return reinterpret_cast<GLFunctionPointerType>( Mock_glVertexAttribDivisorARB); + if (strcmp(name, "glVertexAttribDivisorEXT") == 0) + return reinterpret_cast<GLFunctionPointerType>( + Mock_glVertexAttribDivisorEXT); if (strcmp(name, "glVertexAttribI4i") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glVertexAttribI4i); if (strcmp(name, "glVertexAttribI4iv") == 0) diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.h b/chromium/ui/gl/gl_bindings_autogen_mock.h index 89ca6cd59de..6a2e2c6873d 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.h +++ b/chromium/ui/gl/gl_bindings_autogen_mock.h @@ -710,6 +710,9 @@ Mock_glGetInternalformativRobustANGLE(GLenum target, GLsizei bufSize, GLsizei* length, GLint* params); +static void GL_BINDING_CALL Mock_glGetMultisamplefv(GLenum pname, + GLuint index, + GLfloat* val); static void GL_BINDING_CALL Mock_glGetMultisamplefvRobustANGLE(GLenum pname, GLuint index, GLsizei bufSize, @@ -1172,6 +1175,7 @@ static void GL_BINDING_CALL Mock_glPathStencilFuncNV(GLenum func, static void GL_BINDING_CALL Mock_glPauseTransformFeedback(void); static void GL_BINDING_CALL Mock_glPixelStorei(GLenum pname, GLint param); static void GL_BINDING_CALL Mock_glPointParameteri(GLenum pname, GLint param); +static void GL_BINDING_CALL Mock_glPolygonMode(GLenum face, GLenum mode); static void GL_BINDING_CALL Mock_glPolygonOffset(GLfloat factor, GLfloat units); static void GL_BINDING_CALL Mock_glPopDebugGroup(); static void GL_BINDING_CALL Mock_glPopDebugGroupKHR(); @@ -1392,6 +1396,30 @@ Mock_glStencilThenCoverStrokePathNV(GLuint path, GLenum coverMode); static GLboolean GL_BINDING_CALL Mock_glTestFenceAPPLE(GLuint fence); static GLboolean GL_BINDING_CALL Mock_glTestFenceNV(GLuint fence); +static void GL_BINDING_CALL Mock_glTexBuffer(GLenum target, + GLenum internalformat, + GLuint buffer); +static void GL_BINDING_CALL Mock_glTexBufferEXT(GLenum target, + GLenum internalformat, + GLuint buffer); +static void GL_BINDING_CALL Mock_glTexBufferOES(GLenum target, + GLenum internalformat, + GLuint buffer); +static void GL_BINDING_CALL Mock_glTexBufferRange(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size); +static void GL_BINDING_CALL Mock_glTexBufferRangeEXT(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size); +static void GL_BINDING_CALL Mock_glTexBufferRangeOES(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size); static void GL_BINDING_CALL Mock_glTexImage2D(GLenum target, GLint level, GLint internalformat, @@ -1664,6 +1692,8 @@ static void GL_BINDING_CALL Mock_glVertexAttribDivisorANGLE(GLuint index, GLuint divisor); static void GL_BINDING_CALL Mock_glVertexAttribDivisorARB(GLuint index, GLuint divisor); +static void GL_BINDING_CALL Mock_glVertexAttribDivisorEXT(GLuint index, + GLuint divisor); static void GL_BINDING_CALL Mock_glVertexAttribI4i(GLuint indx, GLint x, GLint y, GLint z, GLint w); static void GL_BINDING_CALL Mock_glVertexAttribI4iv(GLuint indx, @@ -1687,6 +1717,6 @@ static void GL_BINDING_CALL Mock_glViewport(GLint x, GLint y, GLsizei width, GLsizei height); -static GLenum GL_BINDING_CALL Mock_glWaitSync(GLsync sync, - GLbitfield flags, - GLuint64 timeout); +static void GL_BINDING_CALL Mock_glWaitSync(GLsync sync, + GLbitfield flags, + GLuint64 timeout); diff --git a/chromium/ui/gl/gl_bindings_autogen_osmesa.cc b/chromium/ui/gl/gl_bindings_autogen_osmesa.cc index 27185e32f95..c629e834820 100644 --- a/chromium/ui/gl/gl_bindings_autogen_osmesa.cc +++ b/chromium/ui/gl/gl_bindings_autogen_osmesa.cc @@ -48,8 +48,8 @@ void DriverOSMESA::InitializeStaticBindings() { } void DriverOSMESA::InitializeExtensionBindings() { - std::string extensions(GetPlatformExtensions()); - extensions += " "; + std::string platform_extensions(GetPlatformExtensions()); + ExtensionSet extensions(MakeExtensionSet(platform_extensions)); ALLOW_UNUSED_LOCAL(extensions); } diff --git a/chromium/ui/gl/gl_bindings_autogen_osmesa.h b/chromium/ui/gl/gl_bindings_autogen_osmesa.h index a40bf7875bb..baa9587bc56 100644 --- a/chromium/ui/gl/gl_bindings_autogen_osmesa.h +++ b/chromium/ui/gl/gl_bindings_autogen_osmesa.h @@ -11,6 +11,8 @@ #ifndef UI_GL_GL_BINDINGS_AUTOGEN_OSMESA_H_ #define UI_GL_GL_BINDINGS_AUTOGEN_OSMESA_H_ +#include <string> + namespace gl { class GLContext; @@ -69,6 +71,8 @@ class GL_EXPORT OSMESAApi { OSMESAApi(); virtual ~OSMESAApi(); + virtual void SetDisabledExtensions(const std::string& disabled_extensions) {} + virtual void OSMesaColorClampFn(GLboolean enable) = 0; virtual OSMesaContext OSMesaCreateContextFn(GLenum format, OSMesaContext sharelist) = 0; diff --git a/chromium/ui/gl/gl_bindings_autogen_wgl.cc b/chromium/ui/gl/gl_bindings_autogen_wgl.cc index 465f13a2986..d6bdc1ea004 100644 --- a/chromium/ui/gl/gl_bindings_autogen_wgl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_wgl.cc @@ -58,22 +58,19 @@ void DriverWGL::InitializeStaticBindings() { } void DriverWGL::InitializeExtensionBindings() { - std::string extensions(GetPlatformExtensions()); - extensions += " "; + std::string platform_extensions(GetPlatformExtensions()); + ExtensionSet extensions(MakeExtensionSet(platform_extensions)); ALLOW_UNUSED_LOCAL(extensions); ext.b_WGL_ARB_create_context = - extensions.find("WGL_ARB_create_context ") != std::string::npos; + HasExtension(extensions, "WGL_ARB_create_context"); ext.b_WGL_ARB_extensions_string = - extensions.find("WGL_ARB_extensions_string ") != std::string::npos; - ext.b_WGL_ARB_pbuffer = - extensions.find("WGL_ARB_pbuffer ") != std::string::npos; - ext.b_WGL_ARB_pixel_format = - extensions.find("WGL_ARB_pixel_format ") != std::string::npos; + HasExtension(extensions, "WGL_ARB_extensions_string"); + ext.b_WGL_ARB_pbuffer = HasExtension(extensions, "WGL_ARB_pbuffer"); + ext.b_WGL_ARB_pixel_format = HasExtension(extensions, "WGL_ARB_pixel_format"); ext.b_WGL_EXT_extensions_string = - extensions.find("WGL_EXT_extensions_string ") != std::string::npos; - ext.b_WGL_EXT_swap_control = - extensions.find("WGL_EXT_swap_control ") != std::string::npos; + HasExtension(extensions, "WGL_EXT_extensions_string"); + ext.b_WGL_EXT_swap_control = HasExtension(extensions, "WGL_EXT_swap_control"); if (ext.b_WGL_ARB_pixel_format) { fn.wglChoosePixelFormatARBFn = diff --git a/chromium/ui/gl/gl_bindings_autogen_wgl.h b/chromium/ui/gl/gl_bindings_autogen_wgl.h index 2377f416585..c6b787d395f 100644 --- a/chromium/ui/gl/gl_bindings_autogen_wgl.h +++ b/chromium/ui/gl/gl_bindings_autogen_wgl.h @@ -11,6 +11,8 @@ #ifndef UI_GL_GL_BINDINGS_AUTOGEN_WGL_H_ #define UI_GL_GL_BINDINGS_AUTOGEN_WGL_H_ +#include <string> + namespace gl { class GLContext; @@ -91,6 +93,8 @@ class GL_EXPORT WGLApi { WGLApi(); virtual ~WGLApi(); + virtual void SetDisabledExtensions(const std::string& disabled_extensions) {} + virtual BOOL wglChoosePixelFormatARBFn(HDC dc, const int* int_attrib_list, const float* float_attrib_list, diff --git a/chromium/ui/gl/gl_context.cc b/chromium/ui/gl/gl_context.cc index 80622b2f6e1..9dd442dc457 100644 --- a/chromium/ui/gl/gl_context.cc +++ b/chromium/ui/gl/gl_context.cc @@ -32,6 +32,13 @@ base::LazyInstance<base::ThreadLocalPointer<GLContext>>::Leaky current_real_context_ = LAZY_INSTANCE_INITIALIZER; } // namespace +// static +base::subtle::Atomic32 GLContext::total_gl_contexts_ = 0; +// static +bool GLContext::switchable_gpus_supported_ = false; +// static +GpuPreference GLContext::forced_gpu_preference_ = GpuPreferenceNone; + GLContext::ScopedReleaseCurrent::ScopedReleaseCurrent() : canceled_(false) {} GLContext::ScopedReleaseCurrent::~ScopedReleaseCurrent() { @@ -48,6 +55,7 @@ GLContext::GLContext(GLShareGroup* share_group) : share_group_(share_group) { if (!share_group_.get()) share_group_ = new gl::GLShareGroup(); share_group_->AddContext(this); + base::subtle::NoBarrier_AtomicIncrement(&total_gl_contexts_, 1); } GLContext::~GLContext() { @@ -56,12 +64,53 @@ GLContext::~GLContext() { SetCurrent(nullptr); SetCurrentGL(nullptr); } + base::subtle::Atomic32 after_value = + base::subtle::NoBarrier_AtomicIncrement(&total_gl_contexts_, -1); + DCHECK(after_value >= 0); +} + +// static +int32_t GLContext::TotalGLContexts() { + return static_cast<int32_t>( + base::subtle::NoBarrier_Load(&total_gl_contexts_)); +} + +// static +bool GLContext::SwitchableGPUsSupported() { + return switchable_gpus_supported_; +} + +// static +void GLContext::SetSwitchableGPUsSupported() { + DCHECK(!switchable_gpus_supported_); + switchable_gpus_supported_ = true; +} + +// static +void GLContext::SetForcedGpuPreference(GpuPreference gpu_preference) { + DCHECK_EQ(GpuPreferenceNone, forced_gpu_preference_); + forced_gpu_preference_ = gpu_preference; +} + +// static +GpuPreference GLContext::AdjustGpuPreference(GpuPreference gpu_preference) { + switch (forced_gpu_preference_) { + case GpuPreferenceNone: + return gpu_preference; + case PreferIntegratedGpu: + case PreferDiscreteGpu: + return forced_gpu_preference_; + default: + NOTREACHED(); + return GpuPreferenceNone; + } } GLApi* GLContext::CreateGLApi(DriverGL* driver) { real_gl_api_ = new RealGLApi; - real_gl_api_->Initialize(driver); real_gl_api_->set_gl_workarounds(gl_workarounds_); + real_gl_api_->SetDisabledExtensions(disabled_gl_extensions_); + real_gl_api_->Initialize(driver); return real_gl_api_; } @@ -76,11 +125,6 @@ void GLContext::SetUnbindFboOnMakeCurrent() { NOTIMPLEMENTED(); } -std::string GLContext::GetExtensions() { - DCHECK(IsCurrent(nullptr)); - return GetGLExtensionsFromCurrentContext(gl_api_.get()); -} - std::string GLContext::GetGLVersion() { DCHECK(IsCurrent(nullptr)); DCHECK(gl_api_ != nullptr); @@ -97,7 +141,8 @@ std::string GLContext::GetGLRenderer() { return std::string(renderer ? renderer : ""); } -YUVToRGBConverter* GLContext::GetYUVToRGBConverter() { +YUVToRGBConverter* GLContext::GetYUVToRGBConverter( + const gfx::ColorSpace& color_space) { return nullptr; } @@ -134,6 +179,7 @@ CurrentGL* GLContext::GetCurrentGL() { void GLContext::ReinitializeDynamicBindings() { DCHECK(IsCurrent(nullptr)); dynamic_bindings_initialized_ = false; + ResetExtensions(); InitializeDynamicBindings(); } @@ -142,13 +188,7 @@ void GLContext::ForceReleaseVirtuallyCurrent() { } bool GLContext::HasExtension(const char* name) { - std::string extensions = GetExtensions(); - extensions += " "; - - std::string delimited_name(name); - delimited_name += " "; - - return extensions.find(delimited_name) != std::string::npos; + return gl::HasExtension(GetExtensions(), name); } const GLVersionInfo* GLContext::GetVersionInfo() { @@ -196,7 +236,7 @@ GLContext* GLContext::GetRealCurrent() { std::unique_ptr<gl::GLVersionInfo> GLContext::GenerateGLVersionInfo() { return base::MakeUnique<GLVersionInfo>( - GetGLVersion().c_str(), GetGLRenderer().c_str(), GetExtensions().c_str()); + GetGLVersion().c_str(), GetGLRenderer().c_str(), GetExtensions()); } void GLContext::SetCurrent(GLSurface* surface) { @@ -212,11 +252,14 @@ void GLContext::SetCurrent(GLSurface* surface) { } void GLContext::SetGLWorkarounds(const GLWorkarounds& workarounds) { - DCHECK(IsCurrent(nullptr)); + DCHECK(!real_gl_api_); gl_workarounds_ = workarounds; - if (real_gl_api_) { - real_gl_api_->set_gl_workarounds(gl_workarounds_); - } +} + +void GLContext::SetDisabledGLExtensions( + const std::string& disabled_extensions) { + DCHECK(!real_gl_api_); + disabled_gl_extensions_ = disabled_extensions; } GLStateRestorer* GLContext::GetGLStateRestorer() { @@ -250,7 +293,11 @@ void GLContext::InitializeDynamicBindings() { DCHECK(static_bindings_initialized_); if (!dynamic_bindings_initialized_) { if (real_gl_api_) { - real_gl_api_->InitializeFilteredExtensions(); + // This is called everytime DoRequestExtensionCHROMIUM() is called in + // passthrough command buffer. So the underlying ANGLE driver will have + // different GL extensions, therefore we need to clear the cache and + // recompute on demand later. + real_gl_api_->ClearCachedGLExtensions(); real_gl_api_->set_version(GenerateGLVersionInfo()); } @@ -336,6 +383,14 @@ scoped_refptr<GPUTimingClient> GLContextReal::CreateGPUTimingClient() { return gpu_timing_->CreateGPUTimingClient(); } +const ExtensionSet& GLContextReal::GetExtensions() { + DCHECK(IsCurrent(nullptr)); + if (!extensions_initialized_) { + SetExtensionsFromString(GetGLExtensionsFromCurrentContext(gl_api())); + } + return extensions_; +} + GLContextReal::~GLContextReal() { if (GetRealCurrent() == this) current_real_context_.Pointer()->Set(nullptr); @@ -354,4 +409,16 @@ scoped_refptr<GLContext> InitializeGLContext(scoped_refptr<GLContext> context, return context; } +void GLContextReal::SetExtensionsFromString(std::string extensions) { + extensions_string_ = std::move(extensions); + extensions_ = MakeExtensionSet(extensions_string_); + extensions_initialized_ = true; +} + +void GLContextReal::ResetExtensions() { + extensions_.clear(); + extensions_string_.clear(); + extensions_initialized_ = false; +} + } // namespace gl diff --git a/chromium/ui/gl/gl_context.h b/chromium/ui/gl/gl_context.h index ff9cfdbbb5d..5fe126a56a1 100644 --- a/chromium/ui/gl/gl_context.h +++ b/chromium/ui/gl/gl_context.h @@ -7,18 +7,23 @@ #include <memory> #include <string> -#include <vector> +#include "base/atomicops.h" #include "base/cancelable_callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/synchronization/cancellation_flag.h" +#include "ui/gl/extension_set.h" #include "ui/gl/gl_export.h" #include "ui/gl/gl_share_group.h" #include "ui/gl/gl_state_restorer.h" #include "ui/gl/gl_workarounds.h" #include "ui/gl/gpu_preference.h" +namespace gfx { +class ColorSpace; +} // namespace gfx + namespace gl { class YUVToRGBConverter; } // namespace gl @@ -74,6 +79,19 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> { public: explicit GLContext(GLShareGroup* share_group); + static int32_t TotalGLContexts(); + + static bool SwitchableGPUsSupported(); + // This should be called at most once at GPU process startup time. + // By default, GPU switching is not supported unless this is called. + static void SetSwitchableGPUsSupported(); + + // This should be called at most once at GPU process startup time. + static void SetForcedGpuPreference(GpuPreference gpu_preference); + // If a gpu preference is forced (by GPU driver bug workaround, etc), return + // it. Otherwise, return the original input preference. + static GpuPreference AdjustGpuPreference(GpuPreference gpu_preference); + // Initializes the GL context to be compatible with the given surface. The GL // context can be made with other surface's of the same type. The compatible // surface is only needed for certain platforms like WGL, OSMesa and GLX. It @@ -100,6 +118,8 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> { // Set the GL workarounds. void SetGLWorkarounds(const GLWorkarounds& workarounds); + void SetDisabledGLExtensions(const std::string& disabled_gl_extensions); + // Gets the GLStateRestorer for the context. GLStateRestorer* GetGLStateRestorer(); @@ -113,8 +133,8 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> { // passed to SetSwapInterval. void ForceSwapIntervalZero(bool force); - // Returns space separated list of extensions. The context must be current. - virtual std::string GetExtensions(); + // Returns set of extensions. The context must be current. + virtual const ExtensionSet& GetExtensions() = 0; // Indicate that it is safe to force this context to switch GPUs, since // transitioning can cause corruption and hangs (OS X only). @@ -167,8 +187,10 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> { // Returns the GL renderer string. The context must be current. virtual std::string GetGLRenderer(); - // Returns a helper structure to convert YUV textures to RGB textures. - virtual YUVToRGBConverter* GetYUVToRGBConverter(); + // Returns a helper structure to convert the YUV color space |color_space| + // to its associated full-range RGB color space. + virtual YUVToRGBConverter* GetYUVToRGBConverter( + const gfx::ColorSpace& color_space); // Get the CurrentGL object for this context containing the driver, version // and API. @@ -217,6 +239,9 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> { static GLContext* GetRealCurrent(); virtual void OnSetSwapInterval(int interval) = 0; + virtual void ResetExtensions() = 0; + + GLApi* gl_api() { return gl_api_.get(); } private: friend class base::RefCounted<GLContext>; @@ -226,7 +251,14 @@ class GL_EXPORT GLContext : public base::RefCounted<GLContext> { std::unique_ptr<GLVersionInfo> GenerateGLVersionInfo(); + static base::subtle::Atomic32 total_gl_contexts_; + + static bool switchable_gpus_supported_; + + static GpuPreference forced_gpu_preference_; + GLWorkarounds gl_workarounds_; + std::string disabled_gl_extensions_; bool static_bindings_initialized_ = false; bool dynamic_bindings_initialized_ = false; @@ -256,14 +288,22 @@ class GL_EXPORT GLContextReal : public GLContext { public: explicit GLContextReal(GLShareGroup* share_group); scoped_refptr<GPUTimingClient> CreateGPUTimingClient() override; + const ExtensionSet& GetExtensions() override; protected: ~GLContextReal() override; + void ResetExtensions() override; + void SetCurrent(GLSurface* surface) override; + void SetExtensionsFromString(std::string extensions); + const std::string& extension_string() { return extensions_string_; } private: std::unique_ptr<GPUTiming> gpu_timing_; + std::string extensions_string_; + ExtensionSet extensions_; + bool extensions_initialized_ = false; DISALLOW_COPY_AND_ASSIGN(GLContextReal); }; diff --git a/chromium/ui/gl/gl_context_cgl.cc b/chromium/ui/gl/gl_context_cgl.cc index 06e5e73b93a..6b85039c6dd 100644 --- a/chromium/ui/gl/gl_context_cgl.cc +++ b/chromium/ui/gl/gl_context_cgl.cc @@ -46,7 +46,7 @@ static CGLPixelFormatObj GetPixelFormat() { std::vector<CGLPixelFormatAttribute> attribs; // If the system supports dual gpus then allow offline renderers for every // context, so that they can all be in the same share group. - if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) { + if (GLContext::SwitchableGPUsSupported()) { attribs.push_back(kCGLPFAAllowOfflineRenderers); g_support_renderer_switching = true; } @@ -101,8 +101,7 @@ bool GLContextCGL::Initialize(GLSurface* compatible_surface, attribs.bind_generates_resource); GpuPreference gpu_preference = - ui::GpuSwitchingManager::GetInstance()->AdjustGpuPreference( - attribs.gpu_preference); + GLContext::AdjustGpuPreference(attribs.gpu_preference); GLContextCGL* share_context = share_group() ? static_cast<GLContextCGL*>(share_group()->GetContext()) : nullptr; @@ -113,7 +112,7 @@ bool GLContextCGL::Initialize(GLSurface* compatible_surface, // If using the discrete gpu, create a pixel format requiring it before we // create the context. - if (!ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus() || + if (!GLContext::SwitchableGPUsSupported() || gpu_preference == PreferDiscreteGpu) { std::vector<CGLPixelFormatAttribute> discrete_attribs; discrete_attribs.push_back((CGLPixelFormatAttribute) 0); @@ -149,7 +148,7 @@ bool GLContextCGL::Initialize(GLSurface* compatible_surface, } void GLContextCGL::Destroy() { - if (yuv_to_rgb_converter_) { + 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(); @@ -158,7 +157,7 @@ void GLContextCGL::Destroy() { } ScopedCGLSetCurrentContext(static_cast<CGLContextObj>(context_)); - yuv_to_rgb_converter_.reset(); + yuv_to_rgb_converters_.clear(); // Rebind the current context's API if needed. if (current_context && current_context != this) { @@ -224,10 +223,14 @@ bool GLContextCGL::ForceGpuSwitchIfNeeded() { return true; } -YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter() { - if (!yuv_to_rgb_converter_) - yuv_to_rgb_converter_.reset(new YUVToRGBConverter(*GetVersionInfo())); - return yuv_to_rgb_converter_.get(); +YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter( + const gfx::ColorSpace& color_space) { + 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)); + return yuv_to_rgb_converter.get(); } bool GLContextCGL::MakeCurrent(GLSurface* surface) { @@ -239,6 +242,16 @@ bool GLContextCGL::MakeCurrent(GLSurface* surface) { if (IsCurrent(surface)) return true; + // It's likely we're going to switch OpenGL contexts at this point. + // Before doing so, if there is a current context, flush it. There + // are many implicit assumptions of flush ordering between contexts + // at higher levels, and if a flush isn't performed, OpenGL commands + // may be issued in unexpected orders, causing flickering and other + // artifacts. + if (CGLGetCurrentContext() != nullptr) { + glFlush(); + } + ScopedReleaseCurrent release_current; TRACE_EVENT0("gpu", "GLContextCGL::MakeCurrent"); @@ -267,6 +280,12 @@ void GLContextCGL::ReleaseCurrent(GLSurface* surface) { if (!IsCurrent(surface)) return; + // Before releasing the current context, flush it. This ensures that + // all commands issued by higher levels will be seen by the OpenGL + // implementation, which is assumed throughout the code. See comment + // in MakeCurrent, above. + glFlush(); + SetCurrent(nullptr); CGLSetCurrentContext(nullptr); } diff --git a/chromium/ui/gl/gl_context_cgl.h b/chromium/ui/gl/gl_context_cgl.h index 72fbb4d6c8c..737f326c0f9 100644 --- a/chromium/ui/gl/gl_context_cgl.h +++ b/chromium/ui/gl/gl_context_cgl.h @@ -7,9 +7,11 @@ #include <OpenGL/CGLTypes.h> +#include <map> #include <memory> #include "base/macros.h" +#include "ui/gfx/color_space.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_export.h" @@ -32,7 +34,8 @@ class GL_EXPORT GLContextCGL : public GLContextReal { void OnSetSwapInterval(int interval) override; void SetSafeToForceGpuSwitch() override; bool ForceGpuSwitchIfNeeded() override; - YUVToRGBConverter* GetYUVToRGBConverter() override; + YUVToRGBConverter* GetYUVToRGBConverter( + const gfx::ColorSpace& color_space) override; protected: ~GLContextCGL() override; @@ -43,7 +46,8 @@ class GL_EXPORT GLContextCGL : public GLContextReal { void* context_; GpuPreference gpu_preference_; - std::unique_ptr<YUVToRGBConverter> yuv_to_rgb_converter_; + std::map<gfx::ColorSpace, std::unique_ptr<YUVToRGBConverter>> + yuv_to_rgb_converters_; CGLPixelFormatObj discrete_pixelformat_; diff --git a/chromium/ui/gl/gl_context_egl.cc b/chromium/ui/gl/gl_context_egl.cc index 2fbfe08205a..716c0ca6c1f 100644 --- a/chromium/ui/gl/gl_context_egl.cc +++ b/chromium/ui/gl/gl_context_egl.cc @@ -154,7 +154,7 @@ bool GLContextEGL::Initialize(GLSurface* compatible_surface, } } - if (GLSurfaceEGL::HasEGLExtension("EGL_ANGLE_display_texture_share_group")) { + if (GLSurfaceEGL::IsDisplayTextureShareGroupSupported()) { context_attributes.push_back(EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE); context_attributes.push_back( attribs.global_texture_share_group ? EGL_TRUE : EGL_FALSE); @@ -162,7 +162,7 @@ bool GLContextEGL::Initialize(GLSurface* compatible_surface, DCHECK(!attribs.global_texture_share_group); } - if (GLSurfaceEGL::HasEGLExtension("EGL_ANGLE_create_context_client_arrays")) { + if (GLSurfaceEGL::IsCreateContextClientArraysSupported()) { // Disable client arrays if the context supports it context_attributes.push_back(EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE); context_attributes.push_back(EGL_FALSE); @@ -291,15 +291,6 @@ void GLContextEGL::OnSetSwapInterval(int interval) { } } -std::string GLContextEGL::GetExtensions() { - const char* extensions = eglQueryString(display_, - EGL_EXTENSIONS); - if (!extensions) - return GLContext::GetExtensions(); - - return GLContext::GetExtensions() + " " + extensions; -} - bool GLContextEGL::WasAllocatedUsingRobustnessExtension() { return GLSurfaceEGL::IsCreateContextRobustnessSupported(); } diff --git a/chromium/ui/gl/gl_context_egl.h b/chromium/ui/gl/gl_context_egl.h index 6a436dc596d..75b2f3208c1 100644 --- a/chromium/ui/gl/gl_context_egl.h +++ b/chromium/ui/gl/gl_context_egl.h @@ -33,7 +33,6 @@ class GL_EXPORT GLContextEGL : public GLContextReal { bool IsCurrent(GLSurface* surface) override; void* GetHandle() override; void OnSetSwapInterval(int interval) override; - std::string GetExtensions() override; bool WasAllocatedUsingRobustnessExtension() override; void SetUnbindFboOnMakeCurrent() override; diff --git a/chromium/ui/gl/gl_context_glx.cc b/chromium/ui/gl/gl_context_glx.cc index a4493a29a3a..d66d3119382 100644 --- a/chromium/ui/gl/gl_context_glx.cc +++ b/chromium/ui/gl/gl_context_glx.cc @@ -172,16 +172,9 @@ bool GLContextGLX::Initialize(GLSurface* compatible_surface, if (GLSurfaceGLX::IsCreateContextSupported()) { DVLOG(1) << "GLX_ARB_create_context supported."; - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kCreateDefaultGLContext)) { - context_ = CreateContextAttribs( - display_, static_cast<GLXFBConfig>(compatible_surface->GetConfig()), - share_handle, GLVersion(0, 0), 0); - } else { - context_ = CreateHighestVersionContext( - display_, static_cast<GLXFBConfig>(compatible_surface->GetConfig()), - share_handle); - } + context_ = CreateHighestVersionContext( + display_, static_cast<GLXFBConfig>(compatible_surface->GetConfig()), + share_handle); if (!context_) { LOG(ERROR) << "Failed to create GL context with " << "glXCreateContextAttribsARB."; @@ -304,16 +297,6 @@ void GLContextGLX::OnSetSwapInterval(int interval) { } } -std::string GLContextGLX::GetExtensions() { - DCHECK(IsCurrent(nullptr)); - const char* extensions = GLSurfaceGLX::GetGLXExtensions(); - if (extensions) { - return GLContext::GetExtensions() + " " + extensions; - } - - return GLContext::GetExtensions(); -} - bool GLContextGLX::WasAllocatedUsingRobustnessExtension() { return GLSurfaceGLX::IsCreateContextRobustnessSupported(); } diff --git a/chromium/ui/gl/gl_context_glx.h b/chromium/ui/gl/gl_context_glx.h index 62c78e73327..fd5d8d082ec 100644 --- a/chromium/ui/gl/gl_context_glx.h +++ b/chromium/ui/gl/gl_context_glx.h @@ -32,7 +32,6 @@ class GL_EXPORT GLContextGLX : public GLContextReal { bool IsCurrent(GLSurface* surface) override; void* GetHandle() override; void OnSetSwapInterval(int interval) override; - std::string GetExtensions() override; bool WasAllocatedUsingRobustnessExtension() override; protected: diff --git a/chromium/ui/gl/gl_context_stub.cc b/chromium/ui/gl/gl_context_stub.cc index 1c54f9dd42c..7f60b288cd8 100644 --- a/chromium/ui/gl/gl_context_stub.cc +++ b/chromium/ui/gl/gl_context_stub.cc @@ -13,8 +13,9 @@ GLContextStub::GLContextStub() : GLContextStub(nullptr) {} GLContextStub::GLContextStub(GLShareGroup* share_group) : GLContextReal(share_group), use_stub_api_(false), - version_str_("OpenGL ES 3.0"), - extensions_("GL_EXT_framebuffer_object") {} + version_str_("OpenGL ES 3.0") { + SetExtensionsString("GL_EXT_framebuffer_object"); +} bool GLContextStub::Initialize(GLSurface* compatible_surface, const GLContextAttribs& attribs) { @@ -56,16 +57,12 @@ bool GLContextStub::WasAllocatedUsingRobustnessExtension() { HasExtension("GL_KHR_robustness") || HasExtension("GL_EXT_robustness"); } -std::string GLContextStub::GetExtensions() { - return extensions_; -} - void GLContextStub::SetUseStubApi(bool stub_api) { use_stub_api_ = stub_api; } void GLContextStub::SetExtensionsString(const char* extensions) { - extensions_ = extensions; + SetExtensionsFromString(extensions); } void GLContextStub::SetGLVersionString(const char* version_str) { @@ -80,8 +77,8 @@ GLApi* GLContextStub::CreateGLApi(DriverGL* driver) { if (!version_str_.empty()) { stub_api->set_version(version_str_); } - if (!extensions_.empty()) { - stub_api->set_extensions(extensions_); + if (!extension_string().empty()) { + stub_api->set_extensions(extension_string()); } return stub_api; } diff --git a/chromium/ui/gl/gl_context_stub.h b/chromium/ui/gl/gl_context_stub.h index 7ddc3d1f028..ca1514ca875 100644 --- a/chromium/ui/gl/gl_context_stub.h +++ b/chromium/ui/gl/gl_context_stub.h @@ -30,7 +30,6 @@ class GL_EXPORT GLContextStub : public GLContextReal { std::string GetGLVersion() override; std::string GetGLRenderer() override; bool WasAllocatedUsingRobustnessExtension() override; - std::string GetExtensions() override; void SetUseStubApi(bool stub_api); void SetExtensionsString(const char* extensions); @@ -44,7 +43,6 @@ class GL_EXPORT GLContextStub : public GLContextReal { private: bool use_stub_api_; std::string version_str_; - std::string extensions_; DISALLOW_COPY_AND_ASSIGN(GLContextStub); }; diff --git a/chromium/ui/gl/gl_context_wgl.cc b/chromium/ui/gl/gl_context_wgl.cc index 726fcb62496..520b4cac101 100644 --- a/chromium/ui/gl/gl_context_wgl.cc +++ b/chromium/ui/gl/gl_context_wgl.cc @@ -162,19 +162,6 @@ void GLContextWGL::OnSetSwapInterval(int interval) { } } -std::string GLContextWGL::GetExtensions() { - const char* extensions = nullptr; - if (g_driver_wgl.fn.wglGetExtensionsStringARBFn) - extensions = wglGetExtensionsStringARB(GLSurfaceWGL::GetDisplayDC()); - else if (g_driver_wgl.fn.wglGetExtensionsStringEXTFn) - extensions = wglGetExtensionsStringEXT(); - - if (extensions) - return GLContext::GetExtensions() + " " + extensions; - - return GLContext::GetExtensions(); -} - GLContextWGL::~GLContextWGL() { Destroy(); } diff --git a/chromium/ui/gl/gl_context_wgl.h b/chromium/ui/gl/gl_context_wgl.h index aed32873305..52653a08ce0 100644 --- a/chromium/ui/gl/gl_context_wgl.h +++ b/chromium/ui/gl/gl_context_wgl.h @@ -29,7 +29,6 @@ class GL_EXPORT GLContextWGL : public GLContextReal { bool IsCurrent(GLSurface* surface) override; void* GetHandle() override; void OnSetSwapInterval(int interval) override; - std::string GetExtensions() override; private: ~GLContextWGL() override; diff --git a/chromium/ui/gl/gl_egl_api_implementation.cc b/chromium/ui/gl/gl_egl_api_implementation.cc index e0520524b4b..f3bcf751041 100644 --- a/chromium/ui/gl/gl_egl_api_implementation.cc +++ b/chromium/ui/gl/gl_egl_api_implementation.cc @@ -9,6 +9,7 @@ #include "base/strings/string_util.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface_egl.h" namespace gl { @@ -68,22 +69,25 @@ RealEGLApi::~RealEGLApi() { } void RealEGLApi::Initialize(DriverEGL* driver) { - InitializeWithCommandLine(driver, base::CommandLine::ForCurrentProcess()); -} - -void RealEGLApi::InitializeWithCommandLine(DriverEGL* driver, - base::CommandLine* command_line) { - DCHECK(command_line); InitializeBase(driver); +} - const std::string disabled_extensions = command_line->GetSwitchValueASCII( - switches::kDisableGLExtensions); +void RealEGLApi::SetDisabledExtensions(const std::string& disabled_extensions) { + DCHECK(GLContext::TotalGLContexts() == 0); disabled_exts_.clear(); filtered_exts_.clear(); if (!disabled_extensions.empty()) { - disabled_exts_ = base::SplitString( - disabled_extensions, ", ;", - base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + std::vector<std::string> candidates = + base::SplitString(disabled_extensions, ", ;", base::KEEP_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + for (const auto& ext : candidates) { + if (!base::StartsWith(ext, "EGL_", base::CompareCase::SENSITIVE)) + continue; + // For the moment, only the following two extensions can be disabled. + // See DriverEGL::UpdateConditionalExtensionBindings(). + DCHECK(ext == "EGL_KHR_fence_sync" || ext == "EGL_KHR_wait_sync"); + disabled_exts_.push_back(ext); + } } } @@ -104,9 +108,23 @@ DebugEGLApi::DebugEGLApi(EGLApi* egl_api) : egl_api_(egl_api) {} DebugEGLApi::~DebugEGLApi() {} +void DebugEGLApi::SetDisabledExtensions( + const std::string& disabled_extensions) { + if (egl_api_) { + egl_api_->SetDisabledExtensions(disabled_extensions); + } +} + TraceEGLApi::~TraceEGLApi() { } +void TraceEGLApi::SetDisabledExtensions( + const std::string& disabled_extensions) { + if (egl_api_) { + egl_api_->SetDisabledExtensions(disabled_extensions); + } +} + bool GetGLWindowSystemBindingInfoEGL(GLWindowSystemBindingInfo* info) { EGLDisplay display = eglGetCurrentDisplay(); const char* vendor = eglQueryString(display, EGL_VENDOR); @@ -122,4 +140,14 @@ bool GetGLWindowSystemBindingInfoEGL(GLWindowSystemBindingInfo* info) { return true; } +void SetDisabledExtensionsEGL(const std::string& disabled_extensions) { + DCHECK(g_current_egl_context); + DCHECK(GLContext::TotalGLContexts() == 0); + g_current_egl_context->SetDisabledExtensions(disabled_extensions); +} + +bool InitializeExtensionSettingsOneOffEGL() { + return GLSurfaceEGL::InitializeExtensionSettingsOneOff(); +} + } // namespace gl diff --git a/chromium/ui/gl/gl_egl_api_implementation.h b/chromium/ui/gl/gl_egl_api_implementation.h index f120aab53fe..2c44df7d937 100644 --- a/chromium/ui/gl/gl_egl_api_implementation.h +++ b/chromium/ui/gl/gl_egl_api_implementation.h @@ -6,15 +6,13 @@ #define UI_GL_GL_EGL_API_IMPLEMENTATION_H_ #include <map> +#include <string> #include <vector> #include "base/compiler_specific.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_export.h" -namespace base { -class CommandLine; -} namespace gl { struct GLWindowSystemBindingInfo; @@ -23,6 +21,8 @@ GL_EXPORT void InitializeStaticGLBindingsEGL(); GL_EXPORT void InitializeDebugGLBindingsEGL(); GL_EXPORT void ClearBindingsEGL(); GL_EXPORT bool GetGLWindowSystemBindingInfoEGL(GLWindowSystemBindingInfo* info); +GL_EXPORT void SetDisabledExtensionsEGL(const std::string& disabled_extensions); +GL_EXPORT bool InitializeExtensionSettingsOneOffEGL(); class GL_EXPORT EGLApiBase : public EGLApi { public: @@ -44,8 +44,7 @@ class GL_EXPORT RealEGLApi : public EGLApiBase { RealEGLApi(); ~RealEGLApi() override; void Initialize(DriverEGL* driver); - void InitializeWithCommandLine(DriverEGL* driver, - base::CommandLine* command_line); + void SetDisabledExtensions(const std::string& disabled_extensions) override; const char* eglQueryStringFn(EGLDisplay dpy, EGLint name) override; @@ -60,6 +59,7 @@ class GL_EXPORT DebugEGLApi : public EGLApi { public: DebugEGLApi(EGLApi* egl_api); ~DebugEGLApi() override; + void SetDisabledExtensions(const std::string& disabled_extensions) override; // Include the auto-generated part of this class. We split this because // it means we can easily edit the non-auto generated parts right here in @@ -75,6 +75,7 @@ class GL_EXPORT TraceEGLApi : public EGLApi { public: TraceEGLApi(EGLApi* egl_api) : egl_api_(egl_api) { } ~TraceEGLApi() override; + void SetDisabledExtensions(const std::string& disabled_extensions) override; // Include the auto-generated part of this class. We split this because // it means we can easily edit the non-auto generated parts right here in @@ -88,6 +89,3 @@ class GL_EXPORT TraceEGLApi : public EGLApi { } // namespace gl #endif // UI_GL_GL_EGL_API_IMPLEMENTATION_H_ - - - diff --git a/chromium/ui/gl/gl_enums_implementation_autogen.h b/chromium/ui/gl/gl_enums_implementation_autogen.h index a7469082237..d9d51d8054f 100644 --- a/chromium/ui/gl/gl_enums_implementation_autogen.h +++ b/chromium/ui/gl/gl_enums_implementation_autogen.h @@ -1909,6 +1909,21 @@ static const GLEnums::EnumToString enum_to_string_table[] = { 0x8A57, "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT", }, { + 0x8AF0, "GL_TEXTURE_FILTERING_HINT_CHROMIUM", + }, + { + 0x8AF1, "GL_COLOR_SPACE_UNSPECIFIED_CHROMIUM", + }, + { + 0x8AF2, "GL_COLOR_SPACE_SCRGB_LINEAR_CHROMIUM", + }, + { + 0x8AF3, "GL_COLOR_SPACE_SRGB_CHROMIUM", + }, + { + 0x8AF4, "GL_COLOR_SPACE_DISPLAY_P3_CHROMIUM", + }, + { 0x8B30, "GL_FRAGMENT_SHADER", }, { diff --git a/chromium/ui/gl/gl_gl_api_implementation.cc b/chromium/ui/gl/gl_gl_api_implementation.cc index bc55bfb5433..592e1ac511f 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/command_line.h" #include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "base/strings/string_split.h" @@ -283,37 +282,18 @@ void GLApiBase::InitializeBase(DriverGL* driver) { } RealGLApi::RealGLApi() { -#if DCHECK_IS_ON() - filtered_exts_initialized_ = false; -#endif } RealGLApi::~RealGLApi() { } void RealGLApi::Initialize(DriverGL* driver) { - InitializeWithCommandLine(driver, base::CommandLine::ForCurrentProcess()); -} - -void RealGLApi::InitializeWithCommandLine(DriverGL* driver, - base::CommandLine* command_line) { - DCHECK(command_line); InitializeBase(driver); - - const std::string disabled_extensions = command_line->GetSwitchValueASCII( - switches::kDisableGLExtensions); - if (!disabled_extensions.empty()) { - disabled_exts_ = base::SplitString( - disabled_extensions, ", ;", - base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - } } void RealGLApi::glGetIntegervFn(GLenum pname, GLint* params) { if (pname == GL_NUM_EXTENSIONS && disabled_exts_.size()) { -#if DCHECK_IS_ON() - DCHECK(filtered_exts_initialized_); -#endif + InitializeFilteredExtensionsIfNeeded(); *params = static_cast<GLint>(filtered_exts_.size()); } else { GLApiBase::glGetIntegervFn(pname, params); @@ -322,9 +302,7 @@ void RealGLApi::glGetIntegervFn(GLenum pname, GLint* params) { const GLubyte* RealGLApi::glGetStringFn(GLenum name) { if (name == GL_EXTENSIONS && disabled_exts_.size()) { -#if DCHECK_IS_ON() - DCHECK(filtered_exts_initialized_); -#endif + InitializeFilteredExtensionsIfNeeded(); return reinterpret_cast<const GLubyte*>(filtered_exts_str_.c_str()); } return GLApiBase::glGetStringFn(name); @@ -332,11 +310,9 @@ const GLubyte* RealGLApi::glGetStringFn(GLenum name) { const GLubyte* RealGLApi::glGetStringiFn(GLenum name, GLuint index) { if (name == GL_EXTENSIONS && disabled_exts_.size()) { -#if DCHECK_IS_ON() - DCHECK(filtered_exts_initialized_); -#endif + InitializeFilteredExtensionsIfNeeded(); if (index >= filtered_exts_.size()) { - return NULL; + return nullptr; } return reinterpret_cast<const GLubyte*>(filtered_exts_[index].c_str()); } @@ -356,6 +332,21 @@ void RealGLApi::glTexImage2DFn(GLenum target, GetTexInternalFormat(version_.get(), internalformat, format, type); GLenum gl_format = GetTexFormat(version_.get(), format); GLenum gl_type = GetTexType(version_.get(), type); + + // TODO(yizhou): Check if cubemap, 3d texture or texture2d array has the same + // bug on intel mac. + if (gl_workarounds_.reset_teximage2d_base_level && target == GL_TEXTURE_2D) { + GLint base_level = 0; + GLApiBase::glGetTexParameterivFn(target, GL_TEXTURE_BASE_LEVEL, + &base_level); + if (base_level) { + GLApiBase::glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL, 0); + GLApiBase::glTexImage2DFn(target, level, gl_internal_format, width, + height, border, gl_format, gl_type, pixels); + GLApiBase::glTexParameteriFn(target, GL_TEXTURE_BASE_LEVEL, base_level); + return; + } + } GLApiBase::glTexImage2DFn(target, level, gl_internal_format, width, height, border, gl_format, gl_type, pixels); } @@ -478,34 +469,47 @@ void RealGLApi::glDepthRangeFn(GLclampd z_near, GLclampd z_far) { } } -void RealGLApi::InitializeFilteredExtensions() { - if (disabled_exts_.size()) { - filtered_exts_.clear(); - if (WillUseGLGetStringForExtensions(this)) { - filtered_exts_str_ = - FilterGLExtensionList(reinterpret_cast<const char*>( - GLApiBase::glGetStringFn(GL_EXTENSIONS)), - disabled_exts_); - filtered_exts_ = base::SplitString( - filtered_exts_str_, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - } else { - GLint num_extensions = 0; - GLApiBase::glGetIntegervFn(GL_NUM_EXTENSIONS, &num_extensions); - for (GLint i = 0; i < num_extensions; ++i) { - const char* gl_extension = reinterpret_cast<const char*>( - GLApiBase::glGetStringiFn(GL_EXTENSIONS, i)); - DCHECK(gl_extension != NULL); - if (!base::ContainsValue(disabled_exts_, gl_extension)) - filtered_exts_.push_back(gl_extension); - } - filtered_exts_str_ = base::JoinString(filtered_exts_, " "); +void RealGLApi::InitializeFilteredExtensionsIfNeeded() { + DCHECK(disabled_exts_.size()); + if (filtered_exts_.size()) + return; + DCHECK(filtered_exts_str_.empty()); + if (WillUseGLGetStringForExtensions(this)) { + filtered_exts_str_ = FilterGLExtensionList( + reinterpret_cast<const char*>(GLApiBase::glGetStringFn(GL_EXTENSIONS)), + disabled_exts_); + filtered_exts_ = base::SplitString( + filtered_exts_str_, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + } else { + GLint num_extensions = 0; + GLApiBase::glGetIntegervFn(GL_NUM_EXTENSIONS, &num_extensions); + for (GLint i = 0; i < num_extensions; ++i) { + const char* gl_extension = reinterpret_cast<const char*>( + GLApiBase::glGetStringiFn(GL_EXTENSIONS, i)); + DCHECK(gl_extension); + if (!base::ContainsValue(disabled_exts_, gl_extension)) + filtered_exts_.push_back(gl_extension); } -#if DCHECK_IS_ON() - filtered_exts_initialized_ = true; -#endif + filtered_exts_str_ = base::JoinString(filtered_exts_, " "); } } +void RealGLApi::SetDisabledExtensions(const std::string& disabled_extensions) { + ClearCachedGLExtensions(); + disabled_exts_.clear(); + if (disabled_extensions.empty()) + return; + disabled_exts_ = + base::SplitString(disabled_extensions, ", ;", base::KEEP_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + DCHECK(disabled_exts_.size()); +} + +void RealGLApi::ClearCachedGLExtensions() { + filtered_exts_.clear(); + filtered_exts_str_.clear(); +} + void RealGLApi::set_gl_workarounds(const GLWorkarounds& workarounds) { gl_workarounds_ = workarounds; } diff --git a/chromium/ui/gl/gl_gl_api_implementation.h b/chromium/ui/gl/gl_gl_api_implementation.h index db0fcc720a2..e6311517adf 100644 --- a/chromium/ui/gl/gl_gl_api_implementation.h +++ b/chromium/ui/gl/gl_gl_api_implementation.h @@ -13,10 +13,6 @@ #include "ui/gl/gl_export.h" #include "ui/gl/gl_workarounds.h" -namespace base { -class CommandLine; -} - namespace gl { struct GLVersionInfo; @@ -53,8 +49,7 @@ class GL_EXPORT RealGLApi : public GLApiBase { RealGLApi(); ~RealGLApi() override; void Initialize(DriverGL* driver); - void InitializeWithCommandLine(DriverGL* driver, - base::CommandLine* command_line); + void SetDisabledExtensions(const std::string& disabled_extensions) override; void glGetIntegervFn(GLenum pname, GLint* params) override; const GLubyte* glGetStringFn(GLenum name) override; @@ -117,22 +112,21 @@ class GL_EXPORT RealGLApi : public GLApiBase { void glClearDepthFn(GLclampd depth) override; void glDepthRangeFn(GLclampd z_near, GLclampd z_far) override; - void InitializeFilteredExtensions(); void set_gl_workarounds(const GLWorkarounds& workarounds); void set_version(std::unique_ptr<GLVersionInfo> version); + void ClearCachedGLExtensions(); private: - // Filtered GL_EXTENSIONS we return to glGetString(i) calls. + // Compute |filtered_exts_| & |filtered_exts_str_| from |disabled_ext_|. + void InitializeFilteredExtensionsIfNeeded(); + std::vector<std::string> disabled_exts_; + // Filtered GL_EXTENSIONS we return to glGetString(i) calls. std::vector<std::string> filtered_exts_; std::string filtered_exts_str_; GLWorkarounds gl_workarounds_; std::unique_ptr<GLVersionInfo> version_; - -#if DCHECK_IS_ON() - bool filtered_exts_initialized_; -#endif }; // Inserts a TRACE for every GL call. diff --git a/chromium/ui/gl/gl_glx_api_implementation.cc b/chromium/ui/gl/gl_glx_api_implementation.cc index 413a385f2e6..d7903a2654d 100644 --- a/chromium/ui/gl/gl_glx_api_implementation.cc +++ b/chromium/ui/gl/gl_glx_api_implementation.cc @@ -7,7 +7,9 @@ #include "base/command_line.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "ui/gl/gl_context.h" #include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface_glx.h" namespace gl { @@ -21,7 +23,6 @@ void InitializeStaticGLBindingsGLX() { } g_real_glx->Initialize(&g_driver_glx); g_current_glx_context = g_real_glx; - g_driver_glx.InitializeExtensionBindings(); } void InitializeDebugGLBindingsGLX() { @@ -68,16 +69,10 @@ RealGLXApi::~RealGLXApi() { } void RealGLXApi::Initialize(DriverGLX* driver) { - InitializeWithCommandLine(driver, base::CommandLine::ForCurrentProcess()); -} - -void RealGLXApi::InitializeWithCommandLine(DriverGLX* driver, - base::CommandLine* command_line) { - DCHECK(command_line); InitializeBase(driver); +} - const std::string disabled_extensions = command_line->GetSwitchValueASCII( - switches::kDisableGLExtensions); +void RealGLXApi::SetDisabledExtensions(const std::string& disabled_extensions) { disabled_exts_.clear(); filtered_exts_ = ""; if (!disabled_extensions.empty()) { @@ -105,11 +100,26 @@ const char* RealGLXApi::glXQueryExtensionsStringFn(Display* dpy, } DebugGLXApi::DebugGLXApi(GLXApi* glx_api) : glx_api_(glx_api) {} + DebugGLXApi::~DebugGLXApi() {} +void DebugGLXApi::SetDisabledExtensions( + const std::string& disabled_extensions) { + if (glx_api_) { + glx_api_->SetDisabledExtensions(disabled_extensions); + } +} + TraceGLXApi::~TraceGLXApi() { } +void TraceGLXApi::SetDisabledExtensions( + const std::string& disabled_extensions) { + if (glx_api_) { + glx_api_->SetDisabledExtensions(disabled_extensions); + } +} + bool GetGLWindowSystemBindingInfoGLX(GLWindowSystemBindingInfo* info) { Display* display = glXGetCurrentDisplay(); const int kDefaultScreen = 0; @@ -130,4 +140,14 @@ bool GetGLWindowSystemBindingInfoGLX(GLWindowSystemBindingInfo* info) { return true; } +void SetDisabledExtensionsGLX(const std::string& disabled_extensions) { + DCHECK(g_current_glx_context); + DCHECK(GLContext::TotalGLContexts() == 0); + g_current_glx_context->SetDisabledExtensions(disabled_extensions); +} + +bool InitializeExtensionSettingsOneOffGLX() { + return GLSurfaceGLX::InitializeExtensionSettingsOneOff(); +} + } // namespace gl diff --git a/chromium/ui/gl/gl_glx_api_implementation.h b/chromium/ui/gl/gl_glx_api_implementation.h index 2c1815757ca..f3ac47ec91c 100644 --- a/chromium/ui/gl/gl_glx_api_implementation.h +++ b/chromium/ui/gl/gl_glx_api_implementation.h @@ -5,15 +5,13 @@ #ifndef UI_GL_GL_GLX_API_IMPLEMENTATION_H_ #define UI_GL_GL_GLX_API_IMPLEMENTATION_H_ +#include <string> #include <vector> #include "base/compiler_specific.h" #include "gl_bindings.h" #include "ui/gl/gl_export.h" -namespace base { -class CommandLine; -} namespace gl { struct GLWindowSystemBindingInfo; @@ -22,6 +20,8 @@ GL_EXPORT void InitializeStaticGLBindingsGLX(); GL_EXPORT void InitializeDebugGLBindingsGLX(); GL_EXPORT void ClearBindingsGLX(); GL_EXPORT bool GetGLWindowSystemBindingInfoGLX(GLWindowSystemBindingInfo* info); +GL_EXPORT void SetDisabledExtensionsGLX(const std::string& disabled_extensions); +GL_EXPORT bool InitializeExtensionSettingsOneOffGLX(); class GL_EXPORT GLXApiBase : public GLXApi { public: @@ -43,8 +43,7 @@ class GL_EXPORT RealGLXApi : public GLXApiBase { RealGLXApi(); ~RealGLXApi() override; void Initialize(DriverGLX* driver); - void InitializeWithCommandLine(DriverGLX* driver, - base::CommandLine* command_line); + void SetDisabledExtensions(const std::string& disabled_extensions) override; const char* glXQueryExtensionsStringFn(Display* dpy, int screen) override; private: @@ -59,6 +58,8 @@ class GL_EXPORT DebugGLXApi : public GLXApi { DebugGLXApi(GLXApi* glx_api); ~DebugGLXApi() override; + void SetDisabledExtensions(const std::string& disabled_extensions) override; + // Include the auto-generated part of this class. We split this because // it means we can easily edit the non-auto generated parts right here in // this file instead of having to edit some template or the code generator. @@ -74,6 +75,8 @@ class GL_EXPORT TraceGLXApi : public GLXApi { TraceGLXApi(GLXApi* glx_api) : glx_api_(glx_api) { } ~TraceGLXApi() override; + void SetDisabledExtensions(const std::string& disabled_extensions) override; + // Include the auto-generated part of this class. We split this because // it means we can easily edit the non-auto generated parts right here in // this file instead of having to edit some template or the code generator. @@ -86,6 +89,3 @@ class GL_EXPORT TraceGLXApi : public GLXApi { } // namespace gl #endif // UI_GL_GL_GLX_API_IMPLEMENTATION_H_ - - - diff --git a/chromium/ui/gl/gl_image.h b/chromium/ui/gl/gl_image.h index 3e34bbf32b0..c29eff60d28 100644 --- a/chromium/ui/gl/gl_image.h +++ b/chromium/ui/gl/gl_image.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "ui/gfx/color_space.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" @@ -96,9 +97,17 @@ class GL_EXPORT GLImage : public base::RefCounted<GLImage> { enum class Type { NONE, MEMORY, IOSURFACE, DXGI_IMAGE }; virtual Type GetType() const; + void SetColorSpaceForScanout(const gfx::ColorSpace& color_space) { + color_space_ = color_space; + } + + const gfx::ColorSpace& color_space() const { return color_space_; } + protected: virtual ~GLImage() {} + gfx::ColorSpace color_space_; + private: friend class base::RefCounted<GLImage>; diff --git a/chromium/ui/gl/gl_image_io_surface.h b/chromium/ui/gl/gl_image_io_surface.h index 9cffcec6254..bf5f0699d96 100644 --- a/chromium/ui/gl/gl_image_io_surface.h +++ b/chromium/ui/gl/gl_image_io_surface.h @@ -71,6 +71,11 @@ class GL_EXPORT GLImageIOSurface : public GLImage { // signal about whether the Window Server is still using the IOSurface. bool CanCheckIOSurfaceIsInUse() const; + // For IOSurfaces that need manual conversion to a GL texture before being + // sampled from, specify the color space in which to do the required YUV to + // RGB transformation. + void SetColorSpaceForYUVToRGBConversion(const gfx::ColorSpace& color_space); + static unsigned GetInternalFormatForTesting(gfx::BufferFormat format); // Downcasts from |image|. Returns |nullptr| on failure. @@ -97,6 +102,8 @@ class GL_EXPORT GLImageIOSurface : public GLImage { base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer_; gfx::GenericSharedMemoryId io_surface_id_; base::ThreadChecker thread_checker_; + // The default value of Rec. 601 is based on historical shader code. + gfx::ColorSpace color_space_for_yuv_to_rgb_ = gfx::ColorSpace::CreateREC601(); DISALLOW_COPY_AND_ASSIGN(GLImageIOSurface); }; diff --git a/chromium/ui/gl/gl_image_io_surface.mm b/chromium/ui/gl/gl_image_io_surface.mm index c9fabee5a26..1dc5a6dbf1f 100644 --- a/chromium/ui/gl/gl_image_io_surface.mm +++ b/chromium/ui/gl/gl_image_io_surface.mm @@ -14,6 +14,7 @@ #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/process_memory_dump.h" #include "base/trace_event/trace_event.h" +#include "ui/gfx/color_space.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" #include "ui/gl/scoped_binders.h" @@ -298,7 +299,8 @@ bool GLImageIOSurface::CopyTexImage(unsigned target) { GLContext* gl_context = GLContext::GetCurrent(); DCHECK(gl_context); - YUVToRGBConverter* yuv_to_rgb_converter = gl_context->GetYUVToRGBConverter(); + 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 @@ -405,6 +407,13 @@ bool GLImageIOSurface::CanCheckIOSurfaceIsInUse() const { return !cv_pixel_buffer_; } +void GLImageIOSurface::SetColorSpaceForYUVToRGBConversion( + const gfx::ColorSpace& color_space) { + DCHECK(color_space.IsValid()); + DCHECK_NE(color_space, color_space.GetAsFullRangeRGB()); + color_space_for_yuv_to_rgb_ = color_space; +} + base::ScopedCFTypeRef<IOSurfaceRef> GLImageIOSurface::io_surface() { return io_surface_; } diff --git a/chromium/ui/gl/gl_image_io_surface_unittest.cc b/chromium/ui/gl/gl_image_io_surface_unittest.cc index 31dd78b8280..a55e63f1697 100644 --- a/chromium/ui/gl/gl_image_io_surface_unittest.cc +++ b/chromium/ui/gl/gl_image_io_surface_unittest.cc @@ -14,10 +14,10 @@ namespace gl { namespace { -// These values are picked so that RGB -> YUV on the CPU converted -// back to RGB on the GPU produces the original RGB values without -// any error. -const uint8_t kYuvImageColor[] = {0x10, 0x20, 0, 0xFF}; +// These values are picked so that RGB -> YUV on the CPU converted back to RGB +// on the GPU produces the original RGB values without any error. Note that +// some values will be off-by-one. +const uint8_t kYuvImageColor[] = {0x30, 0x40, 0x10, 0xFF}; template <gfx::BufferFormat format> class GLImageIOSurfaceTestDelegate { diff --git a/chromium/ui/gl/gl_image_shared_memory.cc b/chromium/ui/gl/gl_image_shared_memory.cc index cef422b4a16..3b832c9e04e 100644 --- a/chromium/ui/gl/gl_image_shared_memory.cc +++ b/chromium/ui/gl/gl_image_shared_memory.cc @@ -13,7 +13,6 @@ #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/process_memory_dump.h" #include "ui/gfx/buffer_format_util.h" -#include "ui/gfx/gpu_memory_buffer_tracing.h" namespace gl { @@ -87,11 +86,9 @@ void GLImageSharedMemory::OnMemoryDump( base::trace_event::MemoryAllocatorDump::kUnitsBytes, static_cast<uint64_t>(size_in_bytes)); - auto guid = - gfx::GetSharedMemoryGUIDForTracing(process_tracing_id, shared_memory_id_); auto shared_memory_guid = shared_memory_->mapped_id(); if (!shared_memory_guid.is_empty()) { - pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), guid, shared_memory_guid, + pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), shared_memory_guid, 0 /* importance */); } } diff --git a/chromium/ui/gl/gl_image_stub.cc b/chromium/ui/gl/gl_image_stub.cc index 5bfd62be2f7..3d99c4e7eec 100644 --- a/chromium/ui/gl/gl_image_stub.cc +++ b/chromium/ui/gl/gl_image_stub.cc @@ -21,13 +21,13 @@ unsigned GLImageStub::GetInternalFormat() { return GL_RGBA; } bool GLImageStub::BindTexImage(unsigned target) { return true; } bool GLImageStub::CopyTexImage(unsigned target) { - return true; + return false; } bool GLImageStub::CopyTexSubImage(unsigned target, const gfx::Point& offset, const gfx::Rect& rect) { - return true; + return false; } bool GLImageStub::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, diff --git a/chromium/ui/gl/gl_implementation.cc b/chromium/ui/gl/gl_implementation.cc index 8942d7e8733..c97e8f37bfd 100644 --- a/chromium/ui/gl/gl_implementation.cc +++ b/chromium/ui/gl/gl_implementation.cc @@ -82,7 +82,11 @@ GLImplementation GetNamedGLImplementation(const std::string& name) { } GLImplementation GetSoftwareGLImplementation() { +#if defined(OS_WIN) + return kGLImplementationSwiftShaderGL; +#else return kGLImplementationOSMesaGL; +#endif } const char* GetGLImplementationName(GLImplementation implementation) { @@ -225,14 +229,6 @@ bool WillUseGLGetStringForExtensions(GLApi* api) { return is_es || major_version < 3; } -std::unique_ptr<GLVersionInfo> GetVersionInfoFromContext(GLApi* api) { - std::string extensions = GetGLExtensionsFromCurrentContext(api); - return base::MakeUnique<GLVersionInfo>( - reinterpret_cast<const char*>(api->glGetStringFn(GL_VERSION)), - reinterpret_cast<const char*>(api->glGetStringFn(GL_RENDERER)), - extensions.c_str()); -} - base::NativeLibrary LoadLibraryAndPrintError( const base::FilePath::CharType* filename) { return LoadLibraryAndPrintError(base::FilePath(filename)); diff --git a/chromium/ui/gl/gl_implementation.h b/chromium/ui/gl/gl_implementation.h index 28cbde27188..507b39fbf10 100644 --- a/chromium/ui/gl/gl_implementation.h +++ b/chromium/ui/gl/gl_implementation.h @@ -18,7 +18,6 @@ namespace gl { class GLApi; -struct GLVersionInfo; // The GL implementation currently in use. enum GLImplementation { @@ -126,8 +125,6 @@ GL_EXPORT std::string GetGLExtensionsFromCurrentContext(GLApi* api); GL_EXPORT bool WillUseGLGetStringForExtensions(); GL_EXPORT bool WillUseGLGetStringForExtensions(GLApi* api); -GL_EXPORT std::unique_ptr<GLVersionInfo> GetVersionInfoFromContext(GLApi* api); - // Helpers to load a library and log error on failure. GL_EXPORT base::NativeLibrary LoadLibraryAndPrintError( const base::FilePath::CharType* filename); diff --git a/chromium/ui/gl/gl_mock_autogen_gl.h b/chromium/ui/gl/gl_mock_autogen_gl.h index 60f96b0b58a..4e89f056eee 100644 --- a/chromium/ui/gl/gl_mock_autogen_gl.h +++ b/chromium/ui/gl/gl_mock_autogen_gl.h @@ -515,6 +515,7 @@ MOCK_METHOD6(GetInternalformativRobustANGLE, GLsizei bufSize, GLsizei* length, GLint* params)); +MOCK_METHOD3(GetMultisamplefv, void(GLenum pname, GLuint index, GLfloat* val)); MOCK_METHOD5(GetMultisamplefvRobustANGLE, void(GLenum pname, GLuint index, @@ -876,6 +877,7 @@ MOCK_METHOD3(PathStencilFuncNV, void(GLenum func, GLint ref, GLuint mask)); MOCK_METHOD0(PauseTransformFeedback, void()); MOCK_METHOD2(PixelStorei, void(GLenum pname, GLint param)); MOCK_METHOD2(PointParameteri, void(GLenum pname, GLint param)); +MOCK_METHOD2(PolygonMode, void(GLenum face, GLenum mode)); MOCK_METHOD2(PolygonOffset, void(GLfloat factor, GLfloat units)); MOCK_METHOD0(PopDebugGroup, void()); MOCK_METHOD0(PopGroupMarkerEXT, void()); @@ -1032,6 +1034,14 @@ MOCK_METHOD4(StencilThenCoverStrokePathNV, void(GLuint path, GLint reference, GLuint mask, GLenum coverMode)); MOCK_METHOD1(TestFenceAPPLE, GLboolean(GLuint fence)); MOCK_METHOD1(TestFenceNV, GLboolean(GLuint fence)); +MOCK_METHOD3(TexBuffer, + void(GLenum target, GLenum internalformat, GLuint buffer)); +MOCK_METHOD5(TexBufferRange, + void(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size)); MOCK_METHOD9(TexImage2D, void(GLenum target, GLint level, @@ -1237,4 +1247,4 @@ MOCK_METHOD6(VertexAttribPointer, GLsizei stride, const void* ptr)); MOCK_METHOD4(Viewport, void(GLint x, GLint y, GLsizei width, GLsizei height)); -MOCK_METHOD3(WaitSync, GLenum(GLsync sync, GLbitfield flags, GLuint64 timeout)); +MOCK_METHOD3(WaitSync, void(GLsync sync, GLbitfield flags, GLuint64 timeout)); diff --git a/chromium/ui/gl/gl_stub_api.cc b/chromium/ui/gl/gl_stub_api.cc index c7ce0ba6f7b..e73f87a611d 100644 --- a/chromium/ui/gl/gl_stub_api.cc +++ b/chromium/ui/gl/gl_stub_api.cc @@ -336,10 +336,4 @@ GLboolean GLStubApi::glUnmapBufferFn(GLenum target) { return GL_TRUE; } -GLenum GLStubApi::glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) { - return GL_TRUE; -} - } // namespace gl diff --git a/chromium/ui/gl/gl_stub_api.h b/chromium/ui/gl/gl_stub_api.h index 4c9f8ad2d64..01bd331346f 100644 --- a/chromium/ui/gl/gl_stub_api.h +++ b/chromium/ui/gl/gl_stub_api.h @@ -70,9 +70,6 @@ class GL_EXPORT GLStubApi: public GLStubApiBase { GLboolean glTestFenceAPPLEFn(GLuint fence) override; GLboolean glTestFenceNVFn(GLuint fence) override; GLboolean glUnmapBufferFn(GLenum target) override; - GLenum glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) override; private: std::string version_; diff --git a/chromium/ui/gl/gl_stub_autogen_gl.cc b/chromium/ui/gl/gl_stub_autogen_gl.cc index 95f664301f6..e30dbfd7eaf 100644 --- a/chromium/ui/gl/gl_stub_autogen_gl.cc +++ b/chromium/ui/gl/gl_stub_autogen_gl.cc @@ -164,10 +164,4 @@ GLboolean GLStubApiBase::glUnmapBufferFn(GLenum target) { return 0; } -GLenum GLStubApiBase::glWaitSyncFn(GLsync sync, - GLbitfield flags, - GLuint64 timeout) { - return 0; -} - } // namespace gl diff --git a/chromium/ui/gl/gl_stub_autogen_gl.h b/chromium/ui/gl/gl_stub_autogen_gl.h index e14d946a831..c0097ebf94e 100644 --- a/chromium/ui/gl/gl_stub_autogen_gl.h +++ b/chromium/ui/gl/gl_stub_autogen_gl.h @@ -531,6 +531,7 @@ void glGetInternalformativRobustANGLEFn(GLenum target, GLsizei bufSize, GLsizei* length, GLint* params) override {} +void glGetMultisamplefvFn(GLenum pname, GLuint index, GLfloat* val) override {} void glGetMultisamplefvRobustANGLEFn(GLenum pname, GLuint index, GLsizei bufSize, @@ -873,6 +874,7 @@ void glPathStencilFuncNVFn(GLenum func, GLint ref, GLuint mask) override {} void glPauseTransformFeedbackFn() override {} void glPixelStoreiFn(GLenum pname, GLint param) override {} void glPointParameteriFn(GLenum pname, GLint param) override {} +void glPolygonModeFn(GLenum face, GLenum mode) override {} void glPolygonOffsetFn(GLfloat factor, GLfloat units) override {} void glPopDebugGroupFn() override {} void glPopGroupMarkerEXTFn() override {} @@ -1055,6 +1057,14 @@ void glStencilThenCoverStrokePathNVFn(GLuint path, GLenum coverMode) override {} GLboolean glTestFenceAPPLEFn(GLuint fence) override; GLboolean glTestFenceNVFn(GLuint fence) override; +void glTexBufferFn(GLenum target, + GLenum internalformat, + GLuint buffer) override {} +void glTexBufferRangeFn(GLenum target, + GLenum internalformat, + GLuint buffer, + GLintptr offset, + GLsizeiptr size) override {} void glTexImage2DFn(GLenum target, GLint level, GLint internalformat, @@ -1292,4 +1302,4 @@ void glVertexAttribPointerFn(GLuint indx, GLsizei stride, const void* ptr) override {} void glViewportFn(GLint x, GLint y, GLsizei width, GLsizei height) override {} -GLenum glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) override; +void glWaitSyncFn(GLsync sync, GLbitfield flags, GLuint64 timeout) override {} diff --git a/chromium/ui/gl/gl_surface.cc b/chromium/ui/gl/gl_surface.cc index 67671902974..7ed029bb8f7 100644 --- a/chromium/ui/gl/gl_surface.cc +++ b/chromium/ui/gl/gl_surface.cc @@ -38,6 +38,7 @@ bool GLSurface::Initialize(GLSurfaceFormat format) { bool GLSurface::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { NOTIMPLEMENTED(); return false; @@ -182,6 +183,10 @@ bool GLSurface::SupportsDCLayers() const { return false; } +bool GLSurface::UseOverlaysForVideo() const { + return false; +} + bool GLSurface::SetDrawRectangle(const gfx::Rect& rect) { return false; } @@ -238,8 +243,9 @@ void GLSurfaceAdapter::Destroy() { bool GLSurfaceAdapter::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { - return surface_->Resize(size, scale_factor, has_alpha); + return surface_->Resize(size, scale_factor, color_space, has_alpha); } bool GLSurfaceAdapter::Recreate() { @@ -391,6 +397,10 @@ bool GLSurfaceAdapter::SupportsDCLayers() const { return surface_->SupportsDCLayers(); } +bool GLSurfaceAdapter::UseOverlaysForVideo() const { + return surface_->UseOverlaysForVideo(); +} + bool GLSurfaceAdapter::SetDrawRectangle(const gfx::Rect& rect) { return surface_->SetDrawRectangle(rect); } diff --git a/chromium/ui/gl/gl_surface.h b/chromium/ui/gl/gl_surface.h index 5261d7c3b0b..5bc78d8a6cd 100644 --- a/chromium/ui/gl/gl_surface.h +++ b/chromium/ui/gl/gl_surface.h @@ -57,8 +57,17 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { // Destroys the surface. virtual void Destroy() = 0; + // Color spaces that can be dynamically specified to the surface when resized. + enum class ColorSpace { + UNSPECIFIED, + SRGB, + DISPLAY_P3, + SCRGB_LINEAR, + }; + virtual bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha); // Recreate the surface without changing the size. @@ -213,6 +222,8 @@ class GL_EXPORT GLSurface : public base::RefCounted<GLSurface> { virtual bool SupportsDCLayers() const; + virtual bool UseOverlaysForVideo() const; + // Set the rectangle that will be drawn into on the surface. virtual bool SetDrawRectangle(const gfx::Rect& rect); @@ -254,6 +265,7 @@ class GL_EXPORT GLSurfaceAdapter : public GLSurface { void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool Recreate() override; bool DeferDraws() override; @@ -298,6 +310,7 @@ class GL_EXPORT GLSurfaceAdapter : public GLSurface { bool FlipsVertically() const override; bool BuffersFlipped() const override; bool SupportsDCLayers() const override; + bool UseOverlaysForVideo() const override; bool SetDrawRectangle(const gfx::Rect& rect) override; gfx::Vector2d GetDrawOffset() const override; void WaitForSnapshotRendering() override; diff --git a/chromium/ui/gl/gl_surface_egl.cc b/chromium/ui/gl/gl_surface_egl.cc index e51132ae38f..17c46a68408 100644 --- a/chromium/ui/gl/gl_surface_egl.cc +++ b/chromium/ui/gl/gl_surface_egl.cc @@ -34,7 +34,7 @@ #include "ui/gl/scoped_make_current.h" #include "ui/gl/sync_control_vsync_provider.h" -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) extern "C" { #include <X11/Xlib.h> #define Status int @@ -144,6 +144,9 @@ bool g_egl_context_priority_supported = false; bool g_egl_khr_colorspace = false; bool g_egl_ext_colorspace_display_p3 = false; bool g_use_direct_composition = false; +bool g_egl_robust_resource_init_supported = false; +bool g_egl_display_texture_share_group_supported = false; +bool g_egl_create_context_client_arrays_supported = false; class EGLSyncControlVSyncProvider : public SyncControlVSyncProvider { public: @@ -204,7 +207,7 @@ EGLDisplay GetPlatformANGLEDisplay(EGLNativeDisplayType native_display, display_attribs.push_back(EGL_TRUE); } -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) // ANGLE_NULL doesn't use the visual, and may run without X11 where we can't // get it anyway. if (platform_type != EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE) { @@ -314,7 +317,7 @@ EGLConfig ChooseConfig(GLSurfaceFormat format, bool surfaceless) { EGLint stencil_size = format.GetStencilBits(); EGLint samples = format.GetSamples(); -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) // If we're using ANGLE_NULL, we may not have a display, in which case we // can't use XVisualManager. if (g_native_display) { @@ -575,6 +578,11 @@ bool GLSurfaceEGL::InitializeOneOff(EGLNativeDisplayType native_display) { !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableDirectComposition); + g_egl_display_texture_share_group_supported = + HasEGLExtension("EGL_ANGLE_display_texture_share_group"); + g_egl_create_context_client_arrays_supported = + HasEGLExtension("EGL_ANGLE_create_context_client_arrays"); + // TODO(oetuaho@nvidia.com): Surfaceless is disabled on Android as a temporary // workaround, since code written for Android WebView takes different paths // based on whether GL surface objects have underlying EGL surface handles, @@ -603,14 +611,24 @@ bool GLSurfaceEGL::InitializeOneOff(EGLNativeDisplayType native_display) { } } #endif + initialized_ = true; + return true; +} + +// static +bool GLSurfaceEGL::InitializeExtensionSettingsOneOff() { + if (!initialized_) + return false; + g_driver_egl.UpdateConditionalExtensionBindings(); + g_egl_extensions = eglQueryString(g_display, EGL_EXTENSIONS); return true; } // static void GLSurfaceEGL::ShutdownOneOff() { - ResetANGLEPlatform(g_display); + angle::ResetPlatform(g_display); if (g_display != EGL_NO_DISPLAY) eglTerminate(g_display); @@ -625,6 +643,9 @@ void GLSurfaceEGL::ShutdownOneOff() { g_egl_surface_orientation_supported = false; g_use_direct_composition = false; g_egl_surfaceless_context_supported = false; + g_egl_robust_resource_init_supported = false; + g_egl_display_texture_share_group_supported = false; + g_egl_create_context_client_arrays_supported = false; initialized_ = false; } @@ -677,6 +698,18 @@ bool GLSurfaceEGL::IsDirectCompositionSupported() { return g_use_direct_composition; } +bool GLSurfaceEGL::IsRobustResourceInitSupported() { + return g_egl_robust_resource_init_supported; +} + +bool GLSurfaceEGL::IsDisplayTextureShareGroupSupported() { + return g_egl_display_texture_share_group_supported; +} + +bool GLSurfaceEGL::IsCreateContextClientArraysSupported() { + return g_egl_create_context_client_arrays_supported; +} + GLSurfaceEGL::~GLSurfaceEGL() {} // InitializeDisplay is necessary because the static binding code @@ -709,12 +742,12 @@ EGLDisplay GLSurfaceEGL::InitializeDisplay( ExtensionsContain(client_extensions, "EGL_ANGLE_platform_angle_null"); } - bool supports_robust_resource_init = + g_egl_robust_resource_init_supported = client_extensions && ExtensionsContain(client_extensions, "EGL_ANGLE_display_robust_resource_initialization"); bool use_robust_resource_init = - supports_robust_resource_init && + g_egl_robust_resource_init_supported && UsePassthroughCommandDecoder(base::CommandLine::ForCurrentProcess()); std::vector<DisplayType> init_displays; @@ -733,7 +766,7 @@ EGLDisplay GLSurfaceEGL::InitializeDisplay( // Init ANGLE platform now that we have the global display. if (supports_angle_d3d || supports_angle_opengl || supports_angle_null) { - if (!InitializeANGLEPlatform(display)) { + if (!angle::InitializePlatform(display)) { LOG(ERROR) << "ANGLE Platform initialization failed."; } } @@ -941,6 +974,7 @@ gfx::Size NativeViewGLSurfaceEGL::GetSize() { bool NativeViewGLSurfaceEGL::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { if (size == GetSize()) return true; @@ -1177,6 +1211,7 @@ gfx::Size PbufferGLSurfaceEGL::GetSize() { bool PbufferGLSurfaceEGL::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { if (size == size_) return true; @@ -1260,6 +1295,7 @@ gfx::Size SurfacelessEGL::GetSize() { bool SurfacelessEGL::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { size_ = size; return true; diff --git a/chromium/ui/gl/gl_surface_egl.h b/chromium/ui/gl/gl_surface_egl.h index 779b5a6bfb8..042bf077d21 100644 --- a/chromium/ui/gl/gl_surface_egl.h +++ b/chromium/ui/gl/gl_surface_egl.h @@ -58,6 +58,7 @@ class GL_EXPORT GLSurfaceEGL : public GLSurface { GLSurfaceFormat GetFormat() override; static bool InitializeOneOff(EGLNativeDisplayType native_display); + static bool InitializeExtensionSettingsOneOff(); static void ShutdownOneOff(); static EGLDisplay GetHardwareDisplay(); static EGLDisplay InitializeDisplay(EGLNativeDisplayType native_display); @@ -74,6 +75,9 @@ class GL_EXPORT GLSurfaceEGL : public GLSurface { static bool IsEGLSurfacelessContextSupported(); static bool IsEGLContextPrioritySupported(); static bool IsDirectCompositionSupported(); + static bool IsRobustResourceInitSupported(); + static bool IsDisplayTextureShareGroupSupported(); + static bool IsCreateContextClientArraysSupported(); protected: ~GLSurfaceEGL() override; @@ -98,6 +102,7 @@ class GL_EXPORT NativeViewGLSurfaceEGL : public GLSurfaceEGL { void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool Recreate() override; bool IsOffscreen() override; @@ -160,6 +165,7 @@ class GL_EXPORT PbufferGLSurfaceEGL : public GLSurfaceEGL { gfx::Size GetSize() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; EGLSurface GetHandle() override; void* GetShareHandle() override; @@ -190,6 +196,7 @@ class GL_EXPORT SurfacelessEGL : public GLSurfaceEGL { gfx::Size GetSize() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; EGLSurface GetHandle() override; void* GetShareHandle() override; diff --git a/chromium/ui/gl/gl_surface_egl_unittest.cc b/chromium/ui/gl/gl_surface_egl_unittest.cc index 2bb9057e131..5d2dad9c0a5 100644 --- a/chromium/ui/gl/gl_surface_egl_unittest.cc +++ b/chromium/ui/gl/gl_surface_egl_unittest.cc @@ -77,7 +77,7 @@ TEST(GLSurfaceEGLTest, FixedSizeExtension) { EXPECT_EQ(window_size, surface->GetSize()); gfx::Size resize_size(200, 300); - surface->Resize(resize_size, 1.0, false); + surface->Resize(resize_size, 1.0, GLSurface::ColorSpace::UNSPECIFIED, false); EXPECT_EQ(resize_size, surface->GetSize()); } diff --git a/chromium/ui/gl/gl_surface_egl_x11.cc b/chromium/ui/gl/gl_surface_egl_x11.cc index a72b45bf0dc..4d0cd09010e 100644 --- a/chromium/ui/gl/gl_surface_egl_x11.cc +++ b/chromium/ui/gl/gl_surface_egl_x11.cc @@ -144,6 +144,7 @@ EGLConfig NativeViewGLSurfaceEGLX11::GetConfig() { bool NativeViewGLSurfaceEGLX11::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { if (size == GetSize()) return true; diff --git a/chromium/ui/gl/gl_surface_egl_x11.h b/chromium/ui/gl/gl_surface_egl_x11.h index f84ba41cab1..999d5b7ae79 100644 --- a/chromium/ui/gl/gl_surface_egl_x11.h +++ b/chromium/ui/gl/gl_surface_egl_x11.h @@ -27,6 +27,7 @@ class GL_EXPORT NativeViewGLSurfaceEGLX11 : public NativeViewGLSurfaceEGL, void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool InitializeNativeWindow() override; diff --git a/chromium/ui/gl/gl_surface_glx.cc b/chromium/ui/gl/gl_surface_glx.cc index 4c0afc9d2c1..c656c109161 100644 --- a/chromium/ui/gl/gl_surface_glx.cc +++ b/chromium/ui/gl/gl_surface_glx.cc @@ -383,25 +383,31 @@ GLXContext SGIVideoSyncProviderThreadShim::context_ = 0; } // namespace +bool GLSurfaceGLX::initialized_ = false; + GLSurfaceGLX::GLSurfaceGLX() {} bool GLSurfaceGLX::InitializeOneOff() { - static bool initialized = false; - if (initialized) + if (initialized_) return true; // http://crbug.com/245466 setenv("force_s3tc_enable", "true", 1); + { + // As a hack to avoid sandbox threading violation on certain NVidia drivers, + // this block of code needs to be put before gfx::InitializeThreadedX11(). + // See crbug.com/756885. + g_display = gfx::GetXDisplay(); + if (!g_display) { + LOG(ERROR) << "XOpenDisplay failed."; + return false; + } + glXQueryExtensionsString(g_display, 0); + } // SGIVideoSyncProviderShim (if instantiated) will issue X commands on // it's own thread. gfx::InitializeThreadedX11(); - g_display = gfx::GetXDisplay(); - - if (!g_display) { - LOG(ERROR) << "XOpenDisplay failed."; - return false; - } int major, minor; if (!glXQueryVersion(g_display, &major, &minor)) { @@ -414,21 +420,6 @@ bool GLSurfaceGLX::InitializeOneOff() { return false; } - g_glx_context_create = HasGLXExtension("GLX_ARB_create_context"); - g_glx_create_context_robustness_supported = - HasGLXExtension("GLX_ARB_create_context_robustness"); - g_glx_create_context_profile_supported = - HasGLXExtension("GLX_ARB_create_context_profile"); - g_glx_create_context_profile_es2_supported = - HasGLXExtension("GLX_ARB_create_context_es2_profile"); - g_glx_texture_from_pixmap_supported = - HasGLXExtension("GLX_EXT_texture_from_pixmap"); - g_glx_oml_sync_control_supported = HasGLXExtension("GLX_OML_sync_control"); - g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported; - g_glx_ext_swap_control_supported = HasGLXExtension("GLX_EXT_swap_control"); - g_glx_mesa_swap_control_supported = HasGLXExtension("GLX_MESA_swap_control"); - g_glx_sgi_video_sync_supported = HasGLXExtension("GLX_SGI_video_sync"); - const XVisualInfo& visual_info = gl::GLVisualPickerGLX::GetInstance()->system_visual(); g_visual = visual_info.visual; @@ -447,6 +438,32 @@ bool GLSurfaceGLX::InitializeOneOff() { return false; } + initialized_ = true; + return true; +} + +// static +bool GLSurfaceGLX::InitializeExtensionSettingsOneOff() { + if (!initialized_) + return false; + + g_driver_glx.InitializeExtensionBindings(); + + g_glx_context_create = HasGLXExtension("GLX_ARB_create_context"); + g_glx_create_context_robustness_supported = + HasGLXExtension("GLX_ARB_create_context_robustness"); + g_glx_create_context_profile_supported = + HasGLXExtension("GLX_ARB_create_context_profile"); + g_glx_create_context_profile_es2_supported = + HasGLXExtension("GLX_ARB_create_context_es2_profile"); + g_glx_texture_from_pixmap_supported = + HasGLXExtension("GLX_EXT_texture_from_pixmap"); + g_glx_oml_sync_control_supported = HasGLXExtension("GLX_OML_sync_control"); + g_glx_get_msc_rate_oml_supported = g_glx_oml_sync_control_supported; + g_glx_ext_swap_control_supported = HasGLXExtension("GLX_EXT_swap_control"); + g_glx_mesa_swap_control_supported = HasGLXExtension("GLX_MESA_swap_control"); + g_glx_sgi_video_sync_supported = HasGLXExtension("GLX_SGI_video_sync"); + if (!g_glx_get_msc_rate_oml_supported && g_glx_sgi_video_sync_supported) { Display* video_sync_display = gfx::OpenNewXDisplay(); if (!CreateDummyWindow(video_sync_display)) { @@ -455,8 +472,6 @@ bool GLSurfaceGLX::InitializeOneOff() { } SGIVideoSyncProviderThreadShim::display_ = video_sync_display; } - - initialized = true; return true; } @@ -592,6 +607,7 @@ void NativeViewGLSurfaceGLX::Destroy() { bool NativeViewGLSurfaceGLX::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { size_ = size; glXWaitGL(); diff --git a/chromium/ui/gl/gl_surface_glx.h b/chromium/ui/gl/gl_surface_glx.h index 10a213c9010..e67ed7ed132 100644 --- a/chromium/ui/gl/gl_surface_glx.h +++ b/chromium/ui/gl/gl_surface_glx.h @@ -27,6 +27,7 @@ class GL_EXPORT GLSurfaceGLX : public GLSurface { GLSurfaceGLX(); static bool InitializeOneOff(); + static bool InitializeExtensionSettingsOneOff(); // These aren't particularly tied to surfaces, but since we already // have the static InitializeOneOff here, it's easiest to reuse its @@ -55,6 +56,7 @@ class GL_EXPORT GLSurfaceGLX : public GLSurface { private: DISALLOW_COPY_AND_ASSIGN(GLSurfaceGLX); + static bool initialized_; }; // A surface used to render to a view. @@ -67,6 +69,7 @@ class GL_EXPORT NativeViewGLSurfaceGLX : public GLSurfaceGLX { void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; diff --git a/chromium/ui/gl/gl_surface_osmesa.cc b/chromium/ui/gl/gl_surface_osmesa.cc index 741f535696f..fd0347107e2 100644 --- a/chromium/ui/gl/gl_surface_osmesa.cc +++ b/chromium/ui/gl/gl_surface_osmesa.cc @@ -26,7 +26,7 @@ GLSurfaceOSMesa::GLSurfaceOSMesa(GLSurfaceFormat format, } bool GLSurfaceOSMesa::Initialize(GLSurfaceFormat format) { - return Resize(size_, 1.f, true); + return Resize(size_, 1.f, ColorSpace::UNSPECIFIED, true); } void GLSurfaceOSMesa::Destroy() { @@ -35,6 +35,7 @@ void GLSurfaceOSMesa::Destroy() { bool GLSurfaceOSMesa::Resize(const gfx::Size& new_size, float scale_factor, + ColorSpace color_space, bool has_alpha) { std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current; GLContext* current_context = GLContext::GetCurrent(); diff --git a/chromium/ui/gl/gl_surface_osmesa.h b/chromium/ui/gl/gl_surface_osmesa.h index 55e92957806..246bc2922e3 100644 --- a/chromium/ui/gl/gl_surface_osmesa.h +++ b/chromium/ui/gl/gl_surface_osmesa.h @@ -28,6 +28,7 @@ class GL_EXPORT GLSurfaceOSMesa : public GLSurface { void Destroy() override; bool Resize(const gfx::Size& new_size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; diff --git a/chromium/ui/gl/gl_surface_osmesa_x11.cc b/chromium/ui/gl/gl_surface_osmesa_x11.cc index 62141cdfae2..988d3c600a8 100644 --- a/chromium/ui/gl/gl_surface_osmesa_x11.cc +++ b/chromium/ui/gl/gl_surface_osmesa_x11.cc @@ -78,8 +78,9 @@ void GLSurfaceOSMesaX11::Destroy() { bool GLSurfaceOSMesaX11::Resize(const gfx::Size& new_size, float scale_factor, + ColorSpace color_space, bool alpha) { - if (!GLSurfaceOSMesa::Resize(new_size, scale_factor, alpha)) + if (!GLSurfaceOSMesa::Resize(new_size, scale_factor, color_space, alpha)) return false; XWindowAttributes attributes; diff --git a/chromium/ui/gl/gl_surface_osmesa_x11.h b/chromium/ui/gl/gl_surface_osmesa_x11.h index 96072c7f844..e51fbc8791e 100644 --- a/chromium/ui/gl/gl_surface_osmesa_x11.h +++ b/chromium/ui/gl/gl_surface_osmesa_x11.h @@ -28,6 +28,7 @@ class GL_EXPORT GLSurfaceOSMesaX11 : public GLSurfaceOSMesa { void Destroy() override; bool Resize(const gfx::Size& new_size, float scale_factor, + ColorSpace color_space, bool alpha) override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; diff --git a/chromium/ui/gl/gl_surface_stub.cc b/chromium/ui/gl/gl_surface_stub.cc index 6f93fa8c058..af66724a8c4 100644 --- a/chromium/ui/gl/gl_surface_stub.cc +++ b/chromium/ui/gl/gl_surface_stub.cc @@ -11,6 +11,7 @@ void GLSurfaceStub::Destroy() { bool GLSurfaceStub::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { return true; } diff --git a/chromium/ui/gl/gl_surface_stub.h b/chromium/ui/gl/gl_surface_stub.h index 7e11a9dfbae..c6fe52f8bbe 100644 --- a/chromium/ui/gl/gl_surface_stub.h +++ b/chromium/ui/gl/gl_surface_stub.h @@ -24,6 +24,7 @@ class GL_EXPORT GLSurfaceStub : public GLSurface { void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool IsOffscreen() override; gfx::SwapResult SwapBuffers() override; diff --git a/chromium/ui/gl/gl_surface_wgl.cc b/chromium/ui/gl/gl_surface_wgl.cc index eb48cc462e4..65b64a161ca 100644 --- a/chromium/ui/gl/gl_surface_wgl.cc +++ b/chromium/ui/gl/gl_surface_wgl.cc @@ -148,6 +148,9 @@ class DisplayWGL { DisplayWGL* g_display; } // namespace +// static +bool GLSurfaceWGL::initialized_ = false; + GLSurfaceWGL::GLSurfaceWGL() { } @@ -158,9 +161,9 @@ void* GLSurfaceWGL::GetDisplay() { return GetDisplayDC(); } +// static bool GLSurfaceWGL::InitializeOneOff() { - static bool initialized = false; - if (initialized) + if (initialized_) return true; DCHECK(g_display == NULL); @@ -169,7 +172,15 @@ bool GLSurfaceWGL::InitializeOneOff() { return false; g_display = wgl_display.release(); - initialized = true; + initialized_ = true; + return true; +} + +// static +bool GLSurfaceWGL::InitializeExtensionSettingsOneOff() { + if (!initialized_) + return false; + g_driver_wgl.InitializeExtensionBindings(); return true; } @@ -257,6 +268,7 @@ void NativeViewGLSurfaceWGL::Destroy() { bool NativeViewGLSurfaceWGL::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { RECT rect; if (!GetClientRect(window_, &rect)) { diff --git a/chromium/ui/gl/gl_surface_wgl.h b/chromium/ui/gl/gl_surface_wgl.h index 1396f059cef..ee4a67fc7d4 100644 --- a/chromium/ui/gl/gl_surface_wgl.h +++ b/chromium/ui/gl/gl_surface_wgl.h @@ -21,6 +21,7 @@ class GL_EXPORT GLSurfaceWGL : public GLSurface { void* GetDisplay() override; static bool InitializeOneOff(); + static bool InitializeExtensionSettingsOneOff(); static void InitializeOneOffForTesting(); static HDC GetDisplayDC(); @@ -29,6 +30,7 @@ class GL_EXPORT GLSurfaceWGL : public GLSurface { private: DISALLOW_COPY_AND_ASSIGN(GLSurfaceWGL); + static bool initialized_; }; // A surface used to render to a view. @@ -41,6 +43,7 @@ class GL_EXPORT NativeViewGLSurfaceWGL : public GLSurfaceWGL { void Destroy() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool Recreate() override; bool IsOffscreen() override; diff --git a/chromium/ui/gl/gl_switches.cc b/chromium/ui/gl/gl_switches.cc index a09d9592b63..f34a0b7eff2 100644 --- a/chromium/ui/gl/gl_switches.cc +++ b/chromium/ui/gl/gl_switches.cc @@ -28,10 +28,6 @@ const char kANGLEImplementationNullName[] = "null"; namespace switches { -// Ask the GLX driver for the default context instead of trying to get the -// highest version possible. -const char kCreateDefaultGLContext[] = "create-default-gl-context"; - // Disables use of D3D11. const char kDisableD3D11[] = "disable-d3d11"; @@ -79,9 +75,6 @@ const char kGpuNoContextLost[] = "gpu-no-context-lost"; // Disables the use of DirectComposition to draw to the screen. const char kDisableDirectComposition[] = "disable-direct-composition"; -// Indicates whether the dual GPU switching is supported or not. -const char kSupportsDualGpus[] = "supports-dual-gpus"; - // Flag used for Linux tests: for desktop GL bindings, try to load this GL // library first, but fall back to regular library if loading fails. const char kTestGLLib[] = "test-gl-lib"; @@ -129,9 +122,10 @@ const char kUsePassthroughCmdDecoder[] = "use-passthrough-cmd-decoder"; // This is the list of switches passed from this file that are passed from the // GpuProcessHost to the GPU Process. Add your switch to this list if you need // to read it in the GPU process, else don't add it. -const char* kGLSwitchesCopiedFromGpuProcessHost[] = { +const char* const kGLSwitchesCopiedFromGpuProcessHost[] = { kDisableGpuVsync, kDisableD3D11, + kDisableES3GLContext, kEnableGPUServiceLogging, kEnableGPUServiceTracing, kEnableSgiVideoSync, @@ -139,7 +133,6 @@ const char* kGLSwitchesCopiedFromGpuProcessHost[] = { kDisableGLDrawingForTests, kOverrideUseSoftwareGLForTests, kUseANGLE, - kDisableDirectComposition, kEnableSwapBuffersWithBounds, kEnableDirectCompositionLayers, kDisableDirectCompositionLayers, @@ -169,4 +162,8 @@ const base::Feature kDirectCompositionUnderlays{ const base::Feature kDirectCompositionComplexOverlays{ "DirectCompositionComplexOverlays", base::FEATURE_DISABLED_BY_DEFAULT}; +// Allow using overlays for non-root render passes. +const base::Feature kDirectCompositionNonrootOverlays{ + "DirectCompositionNonrootOverlays", base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features diff --git a/chromium/ui/gl/gl_switches.h b/chromium/ui/gl/gl_switches.h index 04d3bc7be57..75a8a24e03b 100644 --- a/chromium/ui/gl/gl_switches.h +++ b/chromium/ui/gl/gl_switches.h @@ -34,7 +34,6 @@ GL_EXPORT extern const char kANGLEImplementationNullName[]; namespace switches { -GL_EXPORT extern const char kCreateDefaultGLContext[]; GL_EXPORT extern const char kDisableD3D11[]; GL_EXPORT extern const char kDisableES3GLContext[]; GL_EXPORT extern const char kDisableGpuVsync[]; @@ -43,8 +42,6 @@ GL_EXPORT extern const char kEnableGPUServiceTracing[]; GL_EXPORT extern const char kGpuNoContextLost[]; GL_EXPORT extern const char kDisableDirectComposition[]; -GL_EXPORT extern const char kSupportsDualGpus[]; - GL_EXPORT extern const char kUseANGLE[]; GL_EXPORT extern const char kUseGL[]; GL_EXPORT extern const char kTestGLLib[]; @@ -63,7 +60,7 @@ GL_EXPORT extern const char kUsePassthroughCmdDecoder[]; GL_EXPORT extern const char kDisableGLDrawingForTests[]; GL_EXPORT extern const char kOverrideUseSoftwareGLForTests[]; -GL_EXPORT extern const char* kGLSwitchesCopiedFromGpuProcessHost[]; +GL_EXPORT extern const char* const kGLSwitchesCopiedFromGpuProcessHost[]; GL_EXPORT extern const int kGLSwitchesCopiedFromGpuProcessHostNumSwitches; } // namespace switches @@ -76,6 +73,7 @@ GL_EXPORT extern const base::Feature kD3DVsync; GL_EXPORT extern const base::Feature kDirectCompositionUnderlays; GL_EXPORT extern const base::Feature kDirectCompositionComplexOverlays; +GL_EXPORT extern const base::Feature kDirectCompositionNonrootOverlays; } // namespace features diff --git a/chromium/ui/gl/gl_utils.cc b/chromium/ui/gl/gl_utils.cc index a1ac0693d14..3cc2e6336d1 100644 --- a/chromium/ui/gl/gl_utils.cc +++ b/chromium/ui/gl/gl_utils.cc @@ -6,6 +6,8 @@ #include "ui/gl/gl_utils.h" +#include "ui/gfx/color_space.h" +#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_switches.h" namespace gl { @@ -13,4 +15,10 @@ namespace gl { bool UsePassthroughCommandDecoder(const base::CommandLine* command_line) { return command_line->HasSwitch(switches::kUsePassthroughCmdDecoder); } + +int GetGLColorSpace(const gfx::ColorSpace& color_space) { + if (color_space == gfx::ColorSpace::CreateSCRGBLinear()) + return GL_COLOR_SPACE_SCRGB_LINEAR_CHROMIUM; + return GL_COLOR_SPACE_UNSPECIFIED_CHROMIUM; +} } diff --git a/chromium/ui/gl/gl_utils.h b/chromium/ui/gl/gl_utils.h index db0bf3a9b52..7a362fe2991 100644 --- a/chromium/ui/gl/gl_utils.h +++ b/chromium/ui/gl/gl_utils.h @@ -10,9 +10,15 @@ #include "base/command_line.h" #include "ui/gl/gl_export.h" +namespace gfx { +class ColorSpace; +} // namespace gfx + namespace gl { GL_EXPORT bool UsePassthroughCommandDecoder( const base::CommandLine* command_line); -} + +GL_EXPORT int GetGLColorSpace(const gfx::ColorSpace& color_space); +} // namespace gl #endif // UI_GL_GL_UTILS_H_ diff --git a/chromium/ui/gl/gl_version_info.cc b/chromium/ui/gl/gl_version_info.cc index 5bf6a7f406b..9e1b1cc0492 100644 --- a/chromium/ui/gl/gl_version_info.cc +++ b/chromium/ui/gl/gl_version_info.cc @@ -24,25 +24,7 @@ namespace gl { GLVersionInfo::GLVersionInfo(const char* version_str, const char* renderer_str, - const char* extensions_str) - : GLVersionInfo() { - std::set<std::string> extensions; - if (extensions_str) { - auto split = base::SplitString(extensions_str, " ", base::KEEP_WHITESPACE, - base::SPLIT_WANT_NONEMPTY); - extensions.insert(split.begin(), split.end()); - } - Initialize(version_str, renderer_str, extensions); -} - -GLVersionInfo::GLVersionInfo(const char* version_str, - const char* renderer_str, - const std::set<std::string>& extensions) - : GLVersionInfo() { - Initialize(version_str, renderer_str, extensions); -} - -GLVersionInfo::GLVersionInfo() + const ExtensionSet& extensions) : is_es(false), is_angle(false), is_mesa(false), @@ -52,11 +34,13 @@ GLVersionInfo::GLVersionInfo() is_es2(false), is_es3(false), is_desktop_core_profile(false), - is_es3_capable(false) {} + is_es3_capable(false) { + Initialize(version_str, renderer_str, extensions); +} void GLVersionInfo::Initialize(const char* version_str, const char* renderer_str, - const std::set<std::string>& extensions) { + const ExtensionSet& extensions) { if (version_str) { ParseVersionString(version_str, &major_version, &minor_version, &is_es, &is_es2, &is_es3); @@ -71,16 +55,11 @@ void GLVersionInfo::Initialize(const char* version_str, } is_desktop_core_profile = DesktopCoreCommonCheck(is_es, major_version, minor_version) && - extensions.find("GL_ARB_compatibility") == extensions.end(); + !HasExtension(extensions, "GL_ARB_compatibility"); is_es3_capable = IsES3Capable(extensions); } -bool GLVersionInfo::IsES3Capable( - const std::set<std::string>& extensions) const { - auto has_extension = [&extensions](std::string extension) -> bool { - return extensions.find(extension) != extensions.end(); - }; - +bool GLVersionInfo::IsES3Capable(const ExtensionSet& extensions) const { // Version ES3 capable without extensions needed. if (IsAtLeastGLES(3, 0) || IsAtLeastGL(4, 2)) { return true; @@ -92,7 +71,8 @@ bool GLVersionInfo::IsES3Capable( } bool has_transform_feedback = - (IsAtLeastGL(4, 0) || has_extension("GL_ARB_transform_feedback2")); + (IsAtLeastGL(4, 0) || + HasExtension(extensions, "GL_ARB_transform_feedback2")); // This code used to require the GL_ARB_gpu_shader5 extension in order to // have support for dynamic indexing of sampler arrays, which was @@ -101,7 +81,7 @@ bool GLVersionInfo::IsES3Capable( // Mesa/Gallium on AMD GPUs) don't support it, we no longer require it. // tex storage is available in core spec since GL 4.2. - bool has_tex_storage = has_extension("GL_ARB_texture_storage"); + bool has_tex_storage = HasExtension(extensions, "GL_ARB_texture_storage"); // TODO(cwallez) check for texture related extensions. See crbug.com/623577 diff --git a/chromium/ui/gl/gl_version_info.h b/chromium/ui/gl/gl_version_info.h index 1775716c33b..a0278fbcfed 100644 --- a/chromium/ui/gl/gl_version_info.h +++ b/chromium/ui/gl/gl_version_info.h @@ -9,16 +9,15 @@ #include <string> #include "base/macros.h" #include "build/build_config.h" +#include "ui/gl/extension_set.h" #include "ui/gl/gl_export.h" namespace gl { struct GL_EXPORT GLVersionInfo { - GLVersionInfo(const char* version_str, const char* renderer_str, - const char* extensions_str); - - GLVersionInfo(const char* version_str, const char* renderer_str, - const std::set<std::string>& exts); + GLVersionInfo(const char* version_str, + const char* renderer_str, + const ExtensionSet& exts); bool IsAtLeastGL(unsigned major, unsigned minor) const { return !is_es && (major_version > major || @@ -62,11 +61,10 @@ struct GL_EXPORT GLVersionInfo { bool is_es3_capable; private: - GLVersionInfo(); void Initialize(const char* version_str, const char* renderer_str, - const std::set<std::string>& extensions); - bool IsES3Capable(const std::set<std::string>& extensions) const; + const ExtensionSet& extensions); + bool IsES3Capable(const ExtensionSet& extensions) const; DISALLOW_COPY_AND_ASSIGN(GLVersionInfo); }; diff --git a/chromium/ui/gl/gl_version_info_unittest.cc b/chromium/ui/gl/gl_version_info_unittest.cc index 2463e0eef00..fed30da954e 100644 --- a/chromium/ui/gl/gl_version_info_unittest.cc +++ b/chromium/ui/gl/gl_version_info_unittest.cc @@ -15,14 +15,13 @@ TEST(GLVersionInfoTest, MajorMinorVersionTest) { "OpenGL ES 2.0 (ANGLE 2.1.0.cd1b12260360)", "2.1 INTEL-10.6.33"}; const char* renderer_str[] = {NULL, NULL, NULL, NULL}; - const char* extensions_str[] = {"extensions", "extensions", - "extensions", "extensions"}; + ExtensionSet extensions = {"extensions"}; unsigned expected_major[] = {4, 4, 2, 2}; unsigned expected_minor[] = {3, 5, 0, 1}; std::unique_ptr<GLVersionInfo> version_info; for (unsigned i = 0; i < arraysize(version_str); i++) { - version_info.reset(new GLVersionInfo(version_str[i], - renderer_str[i], extensions_str[i])); + version_info.reset( + new GLVersionInfo(version_str[i], renderer_str[i], extensions)); unsigned major, minor; bool is_es, is_es2, is_es3; version_info->ParseVersionString(version_str[i], &major, &minor, diff --git a/chromium/ui/gl/gl_wgl_api_implementation.cc b/chromium/ui/gl/gl_wgl_api_implementation.cc index c6a500fc6d8..7cda14ed652 100644 --- a/chromium/ui/gl/gl_wgl_api_implementation.cc +++ b/chromium/ui/gl/gl_wgl_api_implementation.cc @@ -7,7 +7,9 @@ #include "base/command_line.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "ui/gl/gl_context.h" #include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface_wgl.h" namespace gl { @@ -21,7 +23,6 @@ void InitializeStaticGLBindingsWGL() { } g_real_wgl->Initialize(&g_driver_wgl); g_current_wgl_context = g_real_wgl; - g_driver_wgl.InitializeExtensionBindings(); } void InitializeDebugGLBindingsWGL() { @@ -68,16 +69,10 @@ RealWGLApi::~RealWGLApi() { } void RealWGLApi::Initialize(DriverWGL* driver) { - InitializeWithCommandLine(driver, base::CommandLine::ForCurrentProcess()); -} - -void RealWGLApi::InitializeWithCommandLine(DriverWGL* driver, - base::CommandLine* command_line) { - DCHECK(command_line); InitializeBase(driver); +} - const std::string disabled_extensions = command_line->GetSwitchValueASCII( - switches::kDisableGLExtensions); +void RealWGLApi::SetDisabledExtensions(const std::string& disabled_extensions) { disabled_exts_.clear(); filtered_ext_exts_ = ""; filtered_arb_exts_ = ""; @@ -123,9 +118,23 @@ DebugWGLApi::DebugWGLApi(WGLApi* wgl_api) : wgl_api_(wgl_api) {} DebugWGLApi::~DebugWGLApi() {} +void DebugWGLApi::SetDisabledExtensions( + const std::string& disabled_extensions) { + if (wgl_api_) { + wgl_api_->SetDisabledExtensions(disabled_extensions); + } +} + TraceWGLApi::~TraceWGLApi() { } +void TraceWGLApi::SetDisabledExtensions( + const std::string& disabled_extensions) { + if (wgl_api_) { + wgl_api_->SetDisabledExtensions(disabled_extensions); + } +} + bool GetGLWindowSystemBindingInfoWGL(GLWindowSystemBindingInfo* info) { const char* extensions = wglGetExtensionsStringEXT(); *info = GLWindowSystemBindingInfo(); @@ -134,4 +143,14 @@ bool GetGLWindowSystemBindingInfoWGL(GLWindowSystemBindingInfo* info) { return true; } +void SetDisabledExtensionsWGL(const std::string& disabled_extensions) { + DCHECK(g_current_wgl_context); + DCHECK(GLContext::TotalGLContexts() == 0); + g_current_wgl_context->SetDisabledExtensions(disabled_extensions); +} + +bool InitializeExtensionSettingsOneOffWGL() { + return GLSurfaceWGL::InitializeExtensionSettingsOneOff(); +} + } // namespace gl diff --git a/chromium/ui/gl/gl_wgl_api_implementation.h b/chromium/ui/gl/gl_wgl_api_implementation.h index cc3628c15df..8db7ab42c6d 100644 --- a/chromium/ui/gl/gl_wgl_api_implementation.h +++ b/chromium/ui/gl/gl_wgl_api_implementation.h @@ -5,15 +5,13 @@ #ifndef UI_GL_GL_WGL_API_IMPLEMENTATION_H_ #define UI_GL_GL_WGL_API_IMPLEMENTATION_H_ +#include <string> #include <vector> #include "base/compiler_specific.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_export.h" -namespace base { -class CommandLine; -} namespace gl { struct GLWindowSystemBindingInfo; @@ -22,6 +20,8 @@ GL_EXPORT void InitializeStaticGLBindingsWGL(); GL_EXPORT void InitializeDebugGLBindingsWGL(); GL_EXPORT void ClearBindingsWGL(); GL_EXPORT bool GetGLWindowSystemBindingInfoWGL(GLWindowSystemBindingInfo* info); +GL_EXPORT void SetDisabledExtensionsWGL(const std::string& disabled_extensions); +GL_EXPORT bool InitializeExtensionSettingsOneOffWGL(); class GL_EXPORT WGLApiBase : public WGLApi { public: @@ -43,8 +43,7 @@ class GL_EXPORT RealWGLApi : public WGLApiBase { RealWGLApi(); ~RealWGLApi() override; void Initialize(DriverWGL* driver); - void InitializeWithCommandLine(DriverWGL* driver, - base::CommandLine* command_line); + void SetDisabledExtensions(const std::string& disabled_extensions) override; const char* wglGetExtensionsStringARBFn(HDC hDC) override; const char* wglGetExtensionsStringEXTFn() override; @@ -60,6 +59,7 @@ class GL_EXPORT DebugWGLApi : public WGLApi { public: DebugWGLApi(WGLApi* wgl_api); ~DebugWGLApi() override; + void SetDisabledExtensions(const std::string& disabled_extensions) override; // Include the auto-generated part of this class. We split this because // it means we can easily edit the non-auto generated parts right here in @@ -75,6 +75,7 @@ class GL_EXPORT TraceWGLApi : public WGLApi { public: TraceWGLApi(WGLApi* wgl_api) : wgl_api_(wgl_api) { } ~TraceWGLApi() override; + void SetDisabledExtensions(const std::string& disabled_extensions) override; // Include the auto-generated part of this class. We split this because // it means we can easily edit the non-auto generated parts right here in @@ -88,6 +89,3 @@ class GL_EXPORT TraceWGLApi : public WGLApi { } // namespace gl #endif // UI_GL_GL_WGL_API_IMPLEMENTATION_H_ - - - diff --git a/chromium/ui/gl/gl_workarounds.h b/chromium/ui/gl/gl_workarounds.h index 2b93214209a..8660dba058b 100644 --- a/chromium/ui/gl/gl_workarounds.h +++ b/chromium/ui/gl/gl_workarounds.h @@ -11,6 +11,9 @@ struct GLWorkarounds { // glClearColor does not always work on Intel 6xxx Mac drivers. See // crbug.com/710443. bool clear_to_zero_or_one_broken = false; + // Reset texImage2D base level to workaround pixel comparison failure + // above Mac OS 10.12.4 on Intel Mac. See crbug.com/705865. + bool reset_teximage2d_base_level = false; }; } // namespace gl diff --git a/chromium/ui/gl/glx_api_unittest.cc b/chromium/ui/gl/glx_api_unittest.cc index c36c7259228..1b2dfc9fc7f 100644 --- a/chromium/ui/gl/glx_api_unittest.cc +++ b/chromium/ui/gl/glx_api_unittest.cc @@ -4,7 +4,6 @@ #include <memory> -#include "base/command_line.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_glx_api_implementation.h" #include "ui/gl/gl_implementation.h" @@ -31,13 +30,13 @@ class GLXApiTest : public testing::Test { fake_extension_string_ = ""; } - void InitializeAPI(base::CommandLine* command_line) { + void InitializeAPI(const char* disabled_extensions) { api_.reset(new RealGLXApi()); g_current_glx_context = api_.get(); - if (command_line) - api_->InitializeWithCommandLine(&g_driver_glx, command_line); - else - api_->Initialize(&g_driver_glx); + api_->Initialize(&g_driver_glx); + if (disabled_extensions) { + SetDisabledExtensionsGLX(disabled_extensions); + } g_driver_glx.InitializeExtensionBindings(); } @@ -88,10 +87,7 @@ TEST_F(GLXApiTest, DisabledExtensionBitTest) { EXPECT_TRUE(g_driver_glx.ext.b_GLX_ARB_create_context); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - InitializeAPI(&command_line); + InitializeAPI(kFakeDisabledExtensions); EXPECT_FALSE(g_driver_glx.ext.b_GLX_ARB_create_context); } @@ -108,10 +104,7 @@ TEST_F(GLXApiTest, DisabledExtensionStringTest) { EXPECT_STREQ(kFakeExtensions, GetExtensions()); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - InitializeAPI(&command_line); + InitializeAPI(kFakeDisabledExtensions); EXPECT_STREQ(kFilteredExtensions, GetExtensions()); } diff --git a/chromium/ui/gl/gpu_preference.h b/chromium/ui/gl/gpu_preference.h index 67abb755800..edfb679839b 100644 --- a/chromium/ui/gl/gpu_preference.h +++ b/chromium/ui/gl/gpu_preference.h @@ -15,6 +15,7 @@ namespace gl { // This API will likely need to be adjusted as the functionality is // implemented on more operating systems. enum GpuPreference { + GpuPreferenceNone, PreferIntegratedGpu, PreferDiscreteGpu, GpuPreferenceLast = PreferDiscreteGpu diff --git a/chromium/ui/gl/gpu_switching_manager.cc b/chromium/ui/gl/gpu_switching_manager.cc index 9718114c538..be715646580 100644 --- a/chromium/ui/gl/gpu_switching_manager.cc +++ b/chromium/ui/gl/gpu_switching_manager.cc @@ -4,120 +4,16 @@ #include "ui/gl/gpu_switching_manager.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "build/build_config.h" -#include "ui/gl/gl_switches.h" - -#if defined(OS_MACOSX) -#include <OpenGL/OpenGL.h> -#include "ui/gl/gl_context_cgl.h" -#endif // OS_MACOSX - namespace ui { -struct GpuSwitchingManager::PlatformSpecific { -#if defined(OS_MACOSX) - CGLPixelFormatObj discrete_pixel_format; -#endif // OS_MACOSX -}; - // static GpuSwitchingManager* GpuSwitchingManager::GetInstance() { return base::Singleton<GpuSwitchingManager>::get(); } -GpuSwitchingManager::GpuSwitchingManager() - : gpu_switching_option_(gl::PreferIntegratedGpu), - gpu_switching_option_set_(false), - supports_dual_gpus_(false), - supports_dual_gpus_set_(false), - platform_specific_(new PlatformSpecific) { -#if defined(OS_MACOSX) - platform_specific_->discrete_pixel_format = nullptr; -#endif // OS_MACOSX -} - -GpuSwitchingManager::~GpuSwitchingManager() { -#if defined(OS_MACOSX) - if (platform_specific_->discrete_pixel_format) - CGLReleasePixelFormat(platform_specific_->discrete_pixel_format); -#endif // OS_MACOSX -} - -void GpuSwitchingManager::ForceUseOfIntegratedGpu() { - DCHECK(SupportsDualGpus()); - if (gpu_switching_option_set_) { - DCHECK_EQ(gpu_switching_option_, gl::PreferIntegratedGpu); - } else { - gpu_switching_option_ = gl::PreferIntegratedGpu; - gpu_switching_option_set_ = true; - } -} - -void GpuSwitchingManager::ForceUseOfDiscreteGpu() { - DCHECK(SupportsDualGpus()); - if (gpu_switching_option_set_) { - DCHECK_EQ(gpu_switching_option_, gl::PreferDiscreteGpu); - } else { - gpu_switching_option_ = gl::PreferDiscreteGpu; - gpu_switching_option_set_ = true; -#if defined(OS_MACOSX) - // Create a pixel format that lasts the lifespan of Chrome, so Chrome - // stays on the discrete GPU. - SwitchToDiscreteGpuMac(); -#endif // OS_MACOSX - } -} - -bool GpuSwitchingManager::SupportsDualGpus() { - if (!supports_dual_gpus_set_) { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - bool flag = false; - if (command_line.HasSwitch(switches::kSupportsDualGpus)) { - // GPU process, flag is passed down from browser process. - std::string flag_string = command_line.GetSwitchValueASCII( - switches::kSupportsDualGpus); - if (flag_string == "true") { - flag = true; - } else if (flag_string == "false") { - flag = false; - } else { - NOTIMPLEMENTED(); - } - } else { - // Browser process. - // We only compute this flag in the browser process. -#if defined(OS_MACOSX) - flag = (vendor_ids_.size() == 2); - if (flag && command_line.HasSwitch(switches::kUseGL) && - command_line.GetSwitchValueASCII(switches::kUseGL) != - gl::kGLImplementationDesktopName) - flag = false; +GpuSwitchingManager::GpuSwitchingManager() {} - if (flag) { - // Only advertise that we have two GPUs to the rest of - // Chrome's code if we find an Intel GPU and some other - // vendor's GPU. Otherwise we don't understand the - // configuration and don't deal well with it (an example being - // the dual AMD GPUs in recent Mac Pros). - const uint32_t intel = 0x8086; - flag = ((vendor_ids_[0] == intel && vendor_ids_[1] != intel) || - (vendor_ids_[0] != intel && vendor_ids_[1] == intel)); - } -#endif // OS_MACOSX - } - supports_dual_gpus_ = flag; - supports_dual_gpus_set_ = true; - } - return supports_dual_gpus_; -} - -void GpuSwitchingManager::SetGpuVendorIds( - const std::vector<uint32_t>& vendor_ids) { - vendor_ids_ = vendor_ids; -} +GpuSwitchingManager::~GpuSwitchingManager() {} void GpuSwitchingManager::AddObserver(GpuSwitchingObserver* observer) { observer_list_.AddObserver(observer); @@ -132,23 +28,4 @@ void GpuSwitchingManager::NotifyGpuSwitched() { observer.OnGpuSwitched(); } -gl::GpuPreference GpuSwitchingManager::AdjustGpuPreference( - gl::GpuPreference gpu_preference) { - if (!gpu_switching_option_set_) - return gpu_preference; - return gpu_switching_option_; -} - -#if defined(OS_MACOSX) -void GpuSwitchingManager::SwitchToDiscreteGpuMac() { - if (platform_specific_->discrete_pixel_format) - return; - CGLPixelFormatAttribute attribs[1]; - attribs[0] = static_cast<CGLPixelFormatAttribute>(0); - GLint num_pixel_formats = 0; - CGLChoosePixelFormat(attribs, &platform_specific_->discrete_pixel_format, - &num_pixel_formats); -} -#endif // OS_MACOSX - } // namespace ui diff --git a/chromium/ui/gl/gpu_switching_manager.h b/chromium/ui/gl/gpu_switching_manager.h index 4ac76200235..4308bbed939 100644 --- a/chromium/ui/gl/gpu_switching_manager.h +++ b/chromium/ui/gl/gpu_switching_manager.h @@ -5,17 +5,10 @@ #ifndef UI_GL_GPU_SWITCHING_MANAGER_H_ #define UI_GL_GPU_SWITCHING_MANAGER_H_ -#include <stdint.h> - -#include <memory> -#include <vector> - -#include "base/macros.h" #include "base/memory/singleton.h" #include "base/observer_list.h" #include "build/build_config.h" #include "ui/gl/gl_export.h" -#include "ui/gl/gpu_preference.h" #include "ui/gl/gpu_switching_observer.h" namespace ui { @@ -25,26 +18,6 @@ class GL_EXPORT GpuSwitchingManager { // Getter for the singleton. This will return NULL on failure. static GpuSwitchingManager* GetInstance(); - // Set the switching option to PreferIntegratedGpu. - void ForceUseOfIntegratedGpu(); - // Set the switching option to PreferDiscreteGpu; switch to discrete GPU - // immediately on Mac where dual GPU switching is supported. - void ForceUseOfDiscreteGpu(); - - // If no GPU is forced, return the original GpuPreference; otherwise, return - // the forced GPU. - gl::GpuPreference AdjustGpuPreference(gl::GpuPreference gpu_preference); - - // In the browser process, the value for this flag is computed the first time - // this function is called. - // In the GPU process, the value is passed from the browser process using the - // --supports-dual-gpus commandline switch. - bool SupportsDualGpus(); - - // Sets the vendor IDs of the GPUs on the system. The length of this - // vector defines the count of GPUs. - void SetGpuVendorIds(const std::vector<uint32_t>& vendor_ids); - void AddObserver(GpuSwitchingObserver* observer); void RemoveObserver(GpuSwitchingObserver* observer); @@ -61,21 +34,6 @@ class GL_EXPORT GpuSwitchingManager { GpuSwitchingManager(); virtual ~GpuSwitchingManager(); -#if defined(OS_MACOSX) - void SwitchToDiscreteGpuMac(); -#endif // OS_MACOSX - - gl::GpuPreference gpu_switching_option_; - bool gpu_switching_option_set_; - - std::vector<uint32_t> vendor_ids_; - - bool supports_dual_gpus_; - bool supports_dual_gpus_set_; - - struct PlatformSpecific; - std::unique_ptr<PlatformSpecific> platform_specific_; - base::ObserverList<GpuSwitchingObserver> observer_list_; DISALLOW_COPY_AND_ASSIGN(GpuSwitchingManager); diff --git a/chromium/ui/gl/init/BUILD.gn b/chromium/ui/gl/init/BUILD.gn index bd43b3eee78..dea64ed0c05 100644 --- a/chromium/ui/gl/init/BUILD.gn +++ b/chromium/ui/gl/init/BUILD.gn @@ -49,11 +49,6 @@ component("init") { ] libs = [ "OpenGL.framework" ] - } else if (is_fuchsia) { - sources += [ - "gl_factory_fuchsia.cc", - "gl_initializer_fuchsia.cc", - ] } else if (use_x11) { sources += [ "gl_factory_x11.cc", diff --git a/chromium/ui/gl/init/gl_factory.cc b/chromium/ui/gl/init/gl_factory.cc index 744b430d733..643941be273 100644 --- a/chromium/ui/gl/init/gl_factory.cc +++ b/chromium/ui/gl/init/gl_factory.cc @@ -17,9 +17,8 @@ namespace gl { namespace init { -bool InitializeGLOneOff() { - TRACE_EVENT0("gpu,startup", "gl::init::InitializeOneOff"); - +namespace { +bool InitializeGLOneOffHelper(bool init_extensions) { DCHECK_EQ(kGLImplementationNone, GetGLImplementation()); std::vector<GLImplementation> allowed_impls = GetAllowedGLImplementations(); @@ -56,14 +55,28 @@ bool InitializeGLOneOff() { bool gpu_service_logging = cmd->HasSwitch(switches::kEnableGPUServiceLogging); bool disable_gl_drawing = cmd->HasSwitch(switches::kDisableGLDrawingForTests); - return InitializeGLOneOffImplementation( - impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing); + return InitializeGLOneOffImplementation(impl, fallback_to_software_gl, + gpu_service_logging, + disable_gl_drawing, init_extensions); +} + +} // namespace + +bool InitializeGLOneOff() { + TRACE_EVENT0("gpu,startup", "gl::init::InitializeOneOff"); + return InitializeGLOneOffHelper(true); +} + +bool InitializeGLNoExtensionsOneOff() { + TRACE_EVENT0("gpu,startup", "gl::init::InitializeNoExtensionsOneOff"); + return InitializeGLOneOffHelper(false); } bool InitializeGLOneOffImplementation(GLImplementation impl, bool fallback_to_software_gl, bool gpu_service_logging, - bool disable_gl_drawing) { + bool disable_gl_drawing, + bool init_extensions) { bool initialized = InitializeStaticGLBindings(impl) && InitializeGLOneOffPlatform(); if (!initialized && fallback_to_software_gl) { @@ -71,6 +84,10 @@ bool InitializeGLOneOffImplementation(GLImplementation impl, initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()) && InitializeGLOneOffPlatform(); } + if (initialized && init_extensions) { + initialized = InitializeExtensionSettingsOneOffPlatform(); + } + if (!initialized) ShutdownGL(); diff --git a/chromium/ui/gl/init/gl_factory.h b/chromium/ui/gl/init/gl_factory.h index 52dcbe1a074..44819daaca2 100644 --- a/chromium/ui/gl/init/gl_factory.h +++ b/chromium/ui/gl/init/gl_factory.h @@ -6,6 +6,7 @@ #define UI_GL_INIT_GL_FACTORY_H_ #include <memory> +#include <string> #include <vector> #include "base/memory/ref_counted.h" @@ -36,13 +37,21 @@ GL_INIT_EXPORT std::vector<GLImplementation> GetAllowedGLImplementations(); // Initializes GL bindings. GL_INIT_EXPORT bool InitializeGLOneOff(); +// Initializes GL bindings without initializing extension settings. +GL_INIT_EXPORT bool InitializeGLNoExtensionsOneOff(); + +// Initialize plaiform dependent extension settings, including bindings, +// capabilities, etc. +GL_INIT_EXPORT bool InitializeExtensionSettingsOneOffPlatform(); + // Initializes GL bindings using the provided parameters. This might be required // for use in tests, otherwise use InitializeGLOneOff() instead. GL_INIT_EXPORT bool InitializeGLOneOffImplementation( GLImplementation impl, bool fallback_to_software_gl, bool gpu_service_logging, - bool disable_gl_drawing); + bool disable_gl_drawing, + bool init_extensions); // Clears GL bindings and resets GL implementation. GL_INIT_EXPORT void ShutdownGL(); @@ -87,6 +96,11 @@ GL_INIT_EXPORT scoped_refptr<GLSurface> CreateOffscreenGLSurface( GL_INIT_EXPORT scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( const gfx::Size& size, GLSurfaceFormat format); +// Set platform dependent disabled extensions and re-initialize extension +// bindings. +GL_INIT_EXPORT void SetDisabledExtensionsPlatform( + const std::string& disabled_extensions); + } // namespace init } // namespace gl diff --git a/chromium/ui/gl/init/gl_factory_android.cc b/chromium/ui/gl/init/gl_factory_android.cc index dff62b16213..476c61730f6 100644 --- a/chromium/ui/gl/init/gl_factory_android.cc +++ b/chromium/ui/gl/init/gl_factory_android.cc @@ -39,7 +39,6 @@ class GLNonOwnedContext : public GLContextReal { bool IsCurrent(GLSurface* surface) override { return true; } void* GetHandle() override { return nullptr; } void OnSetSwapInterval(int interval) override {} - std::string GetExtensions() override; protected: ~GLNonOwnedContext() override {} @@ -66,14 +65,6 @@ bool GLNonOwnedContext::MakeCurrent(GLSurface* surface) { return true; } -std::string GLNonOwnedContext::GetExtensions() { - const char* extensions = eglQueryString(display_, EGL_EXTENSIONS); - if (!extensions) - return GLContext::GetExtensions(); - - return GLContext::GetExtensions() + " " + extensions; -} - } // namespace std::vector<GLImplementation> GetAllowedGLImplementations() { @@ -166,5 +157,37 @@ scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( } } +void SetDisabledExtensionsPlatform(const std::string& disabled_extensions) { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationEGLGLES2: + SetDisabledExtensionsEGL(disabled_extensions); + break; + case kGLImplementationMockGL: + case kGLImplementationStubGL: + case kGLImplementationOSMesaGL: + break; + default: + NOTREACHED(); + } +} + +bool InitializeExtensionSettingsOneOffPlatform() { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationEGLGLES2: + return InitializeExtensionSettingsOneOffEGL(); + case kGLImplementationMockGL: + case kGLImplementationStubGL: + case kGLImplementationOSMesaGL: + return true; + default: + NOTREACHED(); + return false; + } +} + } // namespace init } // namespace gl diff --git a/chromium/ui/gl/init/gl_factory_fuchsia.cc b/chromium/ui/gl/init/gl_factory_fuchsia.cc deleted file mode 100644 index 9ec54533292..00000000000 --- a/chromium/ui/gl/init/gl_factory_fuchsia.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gl/init/gl_factory.h" - -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_surface.h" - -namespace gl { -namespace init { - -// TODO(fuchsia): Implement these functions. - -std::vector<GLImplementation> GetAllowedGLImplementations() { - NOTIMPLEMENTED(); - return std::vector<GLImplementation>(); -} - -bool GetGLWindowSystemBindingInfo(GLWindowSystemBindingInfo* info) { - NOTIMPLEMENTED(); - return false; -} - -scoped_refptr<GLContext> CreateGLContext(GLShareGroup* share_group, - GLSurface* compatible_surface, - const GLContextAttribs& attribs) { - NOTIMPLEMENTED(); - return nullptr; -} - -scoped_refptr<GLSurface> CreateViewGLSurface(gfx::AcceleratedWidget window) { - NOTIMPLEMENTED(); - return nullptr; -} - -scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( - const gfx::Size& size, - GLSurfaceFormat format) { - NOTIMPLEMENTED(); - return nullptr; -} - -} // namespace init -} // namespace gl diff --git a/chromium/ui/gl/init/gl_factory_mac.cc b/chromium/ui/gl/init/gl_factory_mac.cc index 6716f4109df..2a34dbe549e 100644 --- a/chromium/ui/gl/init/gl_factory_mac.cc +++ b/chromium/ui/gl/init/gl_factory_mac.cc @@ -143,5 +143,18 @@ scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( } } +void SetDisabledExtensionsPlatform(const std::string& disabled_extensions) { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + // TODO(zmo): Implement this if needs arise. +} + +bool InitializeExtensionSettingsOneOffPlatform() { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + // TODO(zmo): Implement this if needs arise. + return true; +} + } // namespace init } // namespace gl diff --git a/chromium/ui/gl/init/gl_factory_ozone.cc b/chromium/ui/gl/init/gl_factory_ozone.cc index a4e0097cb20..cad4a4715ef 100644 --- a/chromium/ui/gl/init/gl_factory_ozone.cc +++ b/chromium/ui/gl/init/gl_factory_ozone.cc @@ -8,6 +8,7 @@ #include "base/trace_event/trace_event.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_context_stub.h" +#include "ui/gl/gl_egl_api_implementation.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_share_group.h" #include "ui/gl/gl_surface.h" @@ -104,5 +105,47 @@ scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( return nullptr; } +void SetDisabledExtensionsPlatform(const std::string& disabled_extensions) { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationEGLGLES2: + SetDisabledExtensionsEGL(disabled_extensions); + break; + case kGLImplementationDesktopGL: + // TODO(zmo): I don't think ozone goes down this path except for testing. + // This might change in the future though. + break; + case kGLImplementationSwiftShaderGL: + case kGLImplementationOSMesaGL: + case kGLImplementationMockGL: + case kGLImplementationStubGL: + break; + default: + NOTREACHED(); + } +} + +bool InitializeExtensionSettingsOneOffPlatform() { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationEGLGLES2: + return InitializeExtensionSettingsOneOffEGL(); + case kGLImplementationDesktopGL: + // TODO(zmo): I don't think ozone goes down this path except for testing. + // This might change in the future though. + return true; + case kGLImplementationSwiftShaderGL: + case kGLImplementationOSMesaGL: + case kGLImplementationMockGL: + case kGLImplementationStubGL: + return true; + default: + NOTREACHED(); + return false; + } +} + } // namespace init } // namespace gl diff --git a/chromium/ui/gl/init/gl_factory_win.cc b/chromium/ui/gl/init/gl_factory_win.cc index ca5b04b62f3..b9f442cd4e3 100644 --- a/chromium/ui/gl/init/gl_factory_win.cc +++ b/chromium/ui/gl/init/gl_factory_win.cc @@ -136,5 +136,44 @@ scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( } } +void SetDisabledExtensionsPlatform(const std::string& disabled_extensions) { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationDesktopGL: + SetDisabledExtensionsWGL(disabled_extensions); + break; + case kGLImplementationEGLGLES2: + SetDisabledExtensionsEGL(disabled_extensions); + break; + case kGLImplementationSwiftShaderGL: + case kGLImplementationOSMesaGL: + case kGLImplementationMockGL: + case kGLImplementationStubGL: + break; + default: + NOTREACHED(); + } +} + +bool InitializeExtensionSettingsOneOffPlatform() { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationDesktopGL: + return InitializeExtensionSettingsOneOffWGL(); + case kGLImplementationEGLGLES2: + return InitializeExtensionSettingsOneOffEGL(); + case kGLImplementationSwiftShaderGL: + case kGLImplementationOSMesaGL: + case kGLImplementationMockGL: + case kGLImplementationStubGL: + return true; + default: + NOTREACHED(); + return false; + } +} + } // namespace init } // namespace gl diff --git a/chromium/ui/gl/init/gl_factory_x11.cc b/chromium/ui/gl/init/gl_factory_x11.cc index 9b0f48174b0..b55fe76b197 100644 --- a/chromium/ui/gl/init/gl_factory_x11.cc +++ b/chromium/ui/gl/init/gl_factory_x11.cc @@ -124,5 +124,44 @@ scoped_refptr<GLSurface> CreateOffscreenGLSurfaceWithFormat( } } +void SetDisabledExtensionsPlatform(const std::string& disabled_extensions) { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationDesktopGL: + SetDisabledExtensionsGLX(disabled_extensions); + break; + case kGLImplementationEGLGLES2: + SetDisabledExtensionsEGL(disabled_extensions); + break; + case kGLImplementationSwiftShaderGL: + case kGLImplementationOSMesaGL: + case kGLImplementationMockGL: + case kGLImplementationStubGL: + break; + default: + NOTREACHED(); + } +} + +bool InitializeExtensionSettingsOneOffPlatform() { + GLImplementation implementation = GetGLImplementation(); + DCHECK_NE(kGLImplementationNone, implementation); + switch (implementation) { + case kGLImplementationDesktopGL: + return InitializeExtensionSettingsOneOffGLX(); + case kGLImplementationEGLGLES2: + return InitializeExtensionSettingsOneOffEGL(); + case kGLImplementationSwiftShaderGL: + case kGLImplementationOSMesaGL: + case kGLImplementationMockGL: + case kGLImplementationStubGL: + return true; + default: + NOTREACHED(); + return false; + } +} + } // namespace init } // namespace gl diff --git a/chromium/ui/gl/init/gl_initializer_fuchsia.cc b/chromium/ui/gl/init/gl_initializer_fuchsia.cc deleted file mode 100644 index 73d543d5ebc..00000000000 --- a/chromium/ui/gl/init/gl_initializer_fuchsia.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/gl/init/gl_initializer.h" - -namespace gl { -namespace init { - -// TODO(fuchsia): Implement these functions. - -bool InitializeGLOneOffPlatform() { - NOTIMPLEMENTED(); - return false; -} - -bool InitializeStaticGLBindings(GLImplementation implementation) { - NOTIMPLEMENTED(); - return false; -} - -void InitializeDebugGLBindings() { - NOTIMPLEMENTED(); -} - -void ShutdownGLPlatform() { - NOTIMPLEMENTED(); -} - -} // namespace init -} // namespace gl diff --git a/chromium/ui/gl/init/gl_initializer_mac.cc b/chromium/ui/gl/init/gl_initializer_mac.cc index a574fb9df48..828ce9bb013 100644 --- a/chromium/ui/gl/init/gl_initializer_mac.cc +++ b/chromium/ui/gl/init/gl_initializer_mac.cc @@ -16,6 +16,7 @@ #include "base/path_service.h" #include "base/threading/thread_restrictions.h" #include "ui/gl/gl_bindings.h" +#include "ui/gl/gl_context.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_osmesa_api_implementation.h" @@ -39,7 +40,7 @@ bool InitializeOneOffForSandbox() { // GPU-related stuff is very slow without this, probably because // the sandbox prevents loading graphics drivers or some such. std::vector<CGLPixelFormatAttribute> attribs; - if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) { + if (GLContext::SwitchableGPUsSupported()) { // Avoid switching to the discrete GPU just for this pixel // format selection. attribs.push_back(kCGLPFAAllowOfflineRenderers); diff --git a/chromium/ui/gl/sync_control_vsync_provider.cc b/chromium/ui/gl/sync_control_vsync_provider.cc index f9dc5c56e47..d45840e8a65 100644 --- a/chromium/ui/gl/sync_control_vsync_provider.cc +++ b/chromium/ui/gl/sync_control_vsync_provider.cc @@ -40,8 +40,6 @@ void SyncControlVSyncProvider::GetVSyncParameters( const UpdateVSyncCallback& callback) { TRACE_EVENT0("gpu", "SyncControlVSyncProvider::GetVSyncParameters"); #if defined(OS_LINUX) - base::TimeTicks timebase; - // The actual clock used for the system time returned by glXGetSyncValuesOML // is unspecified. In practice, the clock used is likely to be either // CLOCK_REALTIME or CLOCK_MONOTONIC, so we compare the returned time to the @@ -67,16 +65,18 @@ void SyncControlVSyncProvider::GetVSyncParameters( } struct timespec real_time; - struct timespec monotonic_time; clock_gettime(CLOCK_REALTIME, &real_time); - clock_gettime(CLOCK_MONOTONIC, &monotonic_time); + // Note: A thread context switch could happen here, between the sampling of + // the two different clocks. + const base::TimeTicks monotonic_time = base::TimeTicks::Now(); + DCHECK_EQ(base::TimeTicks::GetClock(), + base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC); int64_t real_time_in_microseconds = real_time.tv_sec * base::Time::kMicrosecondsPerSecond + real_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond; int64_t monotonic_time_in_microseconds = - monotonic_time.tv_sec * base::Time::kMicrosecondsPerSecond + - monotonic_time.tv_nsec / base::Time::kNanosecondsPerMicrosecond; + monotonic_time.since_origin().InMicroseconds(); // We need the time according to CLOCK_MONOTONIC, so if we've been given // a time from CLOCK_REALTIME, we need to convert. @@ -102,7 +102,8 @@ void SyncControlVSyncProvider::GetVSyncParameters( base::Time::kMicrosecondsPerSecond) return; - timebase = base::TimeTicks::FromInternalValue(system_time); + const base::TimeTicks timebase = + base::TimeTicks() + base::TimeDelta::FromMicroseconds(system_time); // Only need the previous calculated interval for our filtering. while (last_computed_intervals_.size() > 1) @@ -140,11 +141,10 @@ void SyncControlVSyncProvider::GetVSyncParameters( #else LOG(FATAL) #endif // USE_ASH - << "Calculated bogus refresh interval=" - << new_interval.InMicroseconds() - << " us., last_timebase_=" << last_timebase_.ToInternalValue() - << " us., timebase=" << timebase.ToInternalValue() - << " us., last_media_stream_counter_=" << last_media_stream_counter_ + << "Calculated bogus refresh interval=" << new_interval + << ", last_timebase_=" << last_timebase_ + << ", timebase=" << timebase + << ", last_media_stream_counter_=" << last_media_stream_counter_ << ", media_stream_counter=" << media_stream_counter; } else { last_good_interval_ = new_interval; diff --git a/chromium/ui/gl/vsync_provider_win.cc b/chromium/ui/gl/vsync_provider_win.cc index 8025ca9059a..b5c7c612ab7 100644 --- a/chromium/ui/gl/vsync_provider_win.cc +++ b/chromium/ui/gl/vsync_provider_win.cc @@ -105,7 +105,7 @@ void VSyncProviderWin::GetVSyncParameters(const UpdateVSyncCallback& callback) { } } - if (interval.ToInternalValue() != 0) { + if (!interval.is_zero()) { callback.Run(timebase, interval); } } diff --git a/chromium/ui/gl/wgl_api_unittest.cc b/chromium/ui/gl/wgl_api_unittest.cc index 041d3dfaf9f..a0fe216ffba 100644 --- a/chromium/ui/gl/wgl_api_unittest.cc +++ b/chromium/ui/gl/wgl_api_unittest.cc @@ -37,13 +37,13 @@ class WGLApiTest : public testing::Test { fake_arb_extension_string_ = ""; } - void InitializeAPI(base::CommandLine* command_line) { + void InitializeAPI(const char* disabled_extensions) { api_.reset(new RealWGLApi()); g_current_wgl_context = api_.get(); - if (command_line) - api_->InitializeWithCommandLine(&g_driver_wgl, command_line); - else - api_->Initialize(&g_driver_wgl); + api_->Initialize(&g_driver_wgl); + if (disabled_extensions) { + SetDisabledExtensionsWGL(disabled_extensions); + } g_driver_wgl.InitializeExtensionBindings(); } @@ -94,18 +94,14 @@ TEST_F(WGLApiTest, DisabledExtensionBitTest) { EXPECT_FALSE(g_driver_wgl.ext.b_WGL_ARB_extensions_string); - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - // NULL simulates not being able to resolve wglGetExtensionsStringARB - SetFakeARBExtensionString(NULL); + SetFakeARBExtensionString(nullptr); SetFakeEXTExtensionString(kFakeExtensions); InitializeAPI(nullptr); EXPECT_TRUE(g_driver_wgl.ext.b_WGL_ARB_extensions_string); - InitializeAPI(&command_line); + InitializeAPI(kFakeExtensions); EXPECT_FALSE(g_driver_wgl.ext.b_WGL_ARB_extensions_string); SetFakeARBExtensionString(""); @@ -123,7 +119,7 @@ TEST_F(WGLApiTest, DisabledExtensionBitTest) { InitializeAPI(nullptr); EXPECT_TRUE(g_driver_wgl.ext.b_WGL_ARB_extensions_string); - InitializeAPI(&command_line); + InitializeAPI(kFakeDisabledExtensions); EXPECT_FALSE(g_driver_wgl.ext.b_WGL_ARB_extensions_string); } @@ -134,10 +130,6 @@ TEST_F(WGLApiTest, DisabledExtensionStringTest) { "EGL_EXT_1,EGL_EXT_2,EGL_FAKE"; static const char* kFilteredExtensions = "EGL_EXT_3 EGL_EXT_4"; - base::CommandLine command_line(base::CommandLine::NO_PROGRAM); - command_line.AppendSwitchASCII(switches::kDisableGLExtensions, - kFakeDisabledExtensions); - SetFakeARBExtensionString(kFakeExtensions); SetFakeEXTExtensionString(kFakeExtensions); @@ -145,7 +137,7 @@ TEST_F(WGLApiTest, DisabledExtensionStringTest) { EXPECT_EQ(stringpair(kFakeExtensions, kFakeExtensions), GetExtensions()); - InitializeAPI(&command_line); + InitializeAPI(kFakeDisabledExtensions); EXPECT_EQ(stringpair(kFilteredExtensions, kFilteredExtensions), GetExtensions()); } diff --git a/chromium/ui/gl/yuv_to_rgb_converter.cc b/chromium/ui/gl/yuv_to_rgb_converter.cc index daef3990fbc..73410cb1c04 100644 --- a/chromium/ui/gl/yuv_to_rgb_converter.cc +++ b/chromium/ui/gl/yuv_to_rgb_converter.cc @@ -6,6 +6,7 @@ #include "base/strings/stringize_macros.h" #include "base/strings/stringprintf.h" +#include "ui/gfx/color_transform.h" #include "ui/gl/gl_helper.h" #include "ui/gl/gl_version_info.h" #include "ui/gl/scoped_binders.h" @@ -55,21 +56,25 @@ STRINGIZE( uniform sampler2DRect a_uv_texture; VARYING vec2 v_texCoord; void main() { - vec3 yuv_adj = vec3(-0.0625, -0.5, -0.5); - mat3 yuv_matrix = mat3(vec3(1.164, 1.164, 1.164), - vec3(0.0, -.391, 2.018), - vec3(1.596, -.813, 0.0)); vec3 yuv = vec3( TEX(a_y_texture, v_texCoord).r, TEX(a_uv_texture, v_texCoord * 0.5).rg); - FRAGCOLOR = vec4(yuv_matrix * (yuv + yuv_adj), 1.0); + FRAGCOLOR = vec4(DoColorConversion(yuv), 1.0); } ); // clang-format on } // namespace -YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info) { +YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info, + const gfx::ColorSpace color_space) { + std::unique_ptr<gfx::ColorTransform> color_transform = + gfx::ColorTransform::NewColorTransform( + color_space, color_space.GetAsFullRangeRGB(), + gfx::ColorTransform::Intent::INTENT_PERCEPTUAL); + DCHECK(color_transform->CanGetShaderSource()); + std::string do_color_conversion = color_transform->GetShaderSource(); + bool use_core_profile = gl_version_info.is_desktop_core_profile; glGenFramebuffersEXT(1, &framebuffer_); vertex_buffer_ = GLHelper::SetupQuadVertexBuffer(); @@ -82,10 +87,10 @@ YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info) { .c_str()); fragment_shader_ = GLHelper::LoadShader( GL_FRAGMENT_SHADER, - base::StringPrintf("%s\n%s", + base::StringPrintf("%s\n%s\n%s", use_core_profile ? kFragmentHeaderCoreProfile : kFragmentHeaderCompatiblityProfile, - kFragmentShader) + do_color_conversion.c_str(), kFragmentShader) .c_str()); program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_); diff --git a/chromium/ui/gl/yuv_to_rgb_converter.h b/chromium/ui/gl/yuv_to_rgb_converter.h index 7d9f8222589..94fbf6a2edb 100644 --- a/chromium/ui/gl/yuv_to_rgb_converter.h +++ b/chromium/ui/gl/yuv_to_rgb_converter.h @@ -7,13 +7,18 @@ #include "ui/gfx/geometry/size.h" +namespace gfx { +class ColorSpace; +} // namespace gfx + namespace gl { struct GLVersionInfo; class YUVToRGBConverter { public: - explicit YUVToRGBConverter(const GLVersionInfo& gl_version_info); + explicit YUVToRGBConverter(const GLVersionInfo& gl_version_info, + const gfx::ColorSpace color_space); ~YUVToRGBConverter(); // The input Y and UV textures should be bound to these texture objects diff --git a/chromium/ui/keyboard/content/keyboard_ui_content.cc b/chromium/ui/keyboard/content/keyboard_ui_content.cc index 4a40c654709..7a01622e498 100644 --- a/chromium/ui/keyboard/content/keyboard_ui_content.cc +++ b/chromium/ui/keyboard/content/keyboard_ui_content.cc @@ -230,7 +230,6 @@ void KeyboardUIContent::ReloadKeyboardIfNeeded() { TRACE_EVENT0("vk", "ReloadKeyboardIfNeeded"); GetContentsWindow()->SetBounds(gfx::Rect()); keyboard_contents_->ClosePage(); - keyboard_controller()->SetKeyboardMode(FULL_WIDTH); } LoadContents(GetVirtualKeyboardUrl()); } diff --git a/chromium/ui/keyboard/keyboard_controller.cc b/chromium/ui/keyboard/keyboard_controller.cc index 343ee92cdd4..8eb698f5c7d 100644 --- a/chromium/ui/keyboard/keyboard_controller.cc +++ b/chromium/ui/keyboard/keyboard_controller.cc @@ -192,7 +192,7 @@ namespace keyboard { class CallbackAnimationObserver : public ui::LayerAnimationObserver { public: CallbackAnimationObserver(const scoped_refptr<ui::LayerAnimator>& animator, - base::Callback<void(void)> callback); + base::OnceCallback<void(void)> callback); ~CallbackAnimationObserver() override; private: @@ -202,16 +202,15 @@ class CallbackAnimationObserver : public ui::LayerAnimationObserver { void OnLayerAnimationScheduled(ui::LayerAnimationSequence* seq) override {} scoped_refptr<ui::LayerAnimator> animator_; - base::Callback<void(void)> callback_; + base::OnceCallback<void(void)> callback_; DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver); }; CallbackAnimationObserver::CallbackAnimationObserver( const scoped_refptr<ui::LayerAnimator>& animator, - base::Callback<void(void)> callback) - : animator_(animator), callback_(callback) { -} + base::OnceCallback<void(void)> callback) + : animator_(animator), callback_(std::move(callback)) {} CallbackAnimationObserver::~CallbackAnimationObserver() { animator_->RemoveObserver(this); @@ -222,7 +221,8 @@ void CallbackAnimationObserver::OnLayerAnimationEnded( if (animator_->is_animating()) return; animator_->RemoveObserver(this); - callback_.Run(); + DCHECK(!callback_.is_null()); + std::move(callback_).Run(); } void CallbackAnimationObserver::OnLayerAnimationAborted( @@ -237,9 +237,8 @@ KeyboardController::KeyboardController(std::unique_ptr<KeyboardUI> ui, KeyboardLayoutDelegate* delegate) : ui_(std::move(ui)), layout_delegate_(delegate), - show_on_resize_(false), + show_on_content_update_(false), keyboard_locked_(false), - keyboard_mode_(FULL_WIDTH), state_(KeyboardControllerState::UNKNOWN), weak_factory_report_lingering_state_(this), weak_factory_will_hide_(this) { @@ -319,7 +318,7 @@ void KeyboardController::SetContainerBounds(const gfx::Rect& new_bounds, container_->SetBounds(new_bounds); if (contents_loaded) { - bool should_show = show_on_resize(); + const bool should_show = show_on_content_update_; if (state_ == KeyboardControllerState::LOADING_EXTENSION) ChangeState(KeyboardControllerState::HIDDEN); if (should_show) { @@ -341,14 +340,10 @@ void KeyboardController::SetContainerBounds(const gfx::Rect& new_bounds, } } - if (keyboard_mode() == FULL_WIDTH) { - // We need to send out this notification only if keyboard is visible since - // the contents window is resized even if keyboard is hidden. - if (keyboard_visible()) - NotifyContentsBoundsChanging(new_bounds); - } else if (keyboard_mode() == FLOATING) { - NotifyContentsBoundsChanging(gfx::Rect()); - } + // We need to send out this notification only if keyboard is visible since + // the contents window is resized even if keyboard is hidden. + if (keyboard_visible()) + NotifyContentsBoundsChanging(new_bounds); } void KeyboardController::AddObserver(KeyboardControllerObserver* observer) { @@ -364,23 +359,6 @@ void KeyboardController::RemoveObserver(KeyboardControllerObserver* observer) { observer_list_.RemoveObserver(observer); } -void KeyboardController::SetKeyboardMode(KeyboardMode mode) { - if (keyboard_mode_ == mode) - return; - - keyboard_mode_ = mode; - // When keyboard is floating, no overscroll or resize is necessary. Sets - // keyboard bounds to zero so overscroll or resize is disabled. - if (keyboard_mode_ == FLOATING) { - NotifyContentsBoundsChanging(gfx::Rect()); - } else if (keyboard_mode_ == FULL_WIDTH) { - AdjustKeyboardBounds(); - // No animation is needed. Just change the bounds and ensure caret in - // workarea. - NotifyKeyboardBoundsChangingAndEnsrueCaretInWorkArea(); - } -} - void KeyboardController::HideKeyboard(HideReason reason) { TRACE_EVENT0("vk", "HideKeyboard"); @@ -389,7 +367,7 @@ void KeyboardController::HideKeyboard(HideReason reason) { case KeyboardControllerState::HIDDEN: return; case KeyboardControllerState::LOADING_EXTENSION: - show_on_resize_ = false; + show_on_content_update_ = false; return; case KeyboardControllerState::WILL_HIDE: @@ -476,28 +454,17 @@ void KeyboardController::OnWindowBoundsChanged(aura::Window* window, return; int container_height = container_->bounds().height(); - if (keyboard_mode_ == FULL_WIDTH) { - container_->SetBounds(gfx::Rect(new_bounds.x(), - new_bounds.bottom() - container_height, - new_bounds.width(), - container_height)); - } else if (keyboard_mode_ == FLOATING) { - // When screen rotate, horizontally center floating virtual keyboard - // window and vertically align it to the bottom. - int container_width = container_->bounds().width(); - container_->SetBounds(gfx::Rect( - new_bounds.x() + (new_bounds.width() - container_width) / 2, - new_bounds.bottom() - container_height, - container_width, - container_height)); - } + + container_->SetBounds(gfx::Rect(new_bounds.x(), + new_bounds.bottom() - container_height, + new_bounds.width(), container_height)); } void KeyboardController::Reload() { if (ui_->HasContentsWindow()) { // A reload should never try to show virtual keyboard. If keyboard is not // visible before reload, it should keep invisible after reload. - show_on_resize_ = false; + show_on_content_update_ = false; ui_->ReloadKeyboardIfNeeded(); } } @@ -519,16 +486,16 @@ void KeyboardController::OnTextInputStateChanged( if (should_hide) { switch (state_) { case KeyboardControllerState::LOADING_EXTENSION: - show_on_resize_ = false; + show_on_content_update_ = false; return; case KeyboardControllerState::SHOWN: ChangeState(KeyboardControllerState::WILL_HIDE); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, - base::Bind(&KeyboardController::HideKeyboard, - weak_factory_will_hide_.GetWeakPtr(), - HIDE_REASON_AUTOMATIC), + base::BindOnce(&KeyboardController::HideKeyboard, + weak_factory_will_hide_.GetWeakPtr(), + HIDE_REASON_AUTOMATIC), base::TimeDelta::FromMilliseconds(kHideKeyboardDelayMs)); return; default: @@ -597,7 +564,7 @@ void KeyboardController::PopulateKeyboardContent(int64_t display_id, case KeyboardControllerState::SHOWN: return; case KeyboardControllerState::LOADING_EXTENSION: - show_on_resize_ |= show_keyboard; + show_on_content_update_ |= show_keyboard; return; default: break; @@ -628,7 +595,7 @@ void KeyboardController::PopulateKeyboardContent(int64_t display_id, switch (state_) { case KeyboardControllerState::INITIAL: DCHECK(ui_->GetContentsWindow()->bounds().height() == 0); - show_on_resize_ = show_keyboard; + show_on_content_update_ = show_keyboard; ChangeState(KeyboardControllerState::LOADING_EXTENSION); return; case KeyboardControllerState::WILL_HIDE: @@ -654,15 +621,11 @@ void KeyboardController::PopulateKeyboardContent(int64_t display_id, container_animator->set_preemption_strategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); - if (keyboard_mode_ == FLOATING) { - animation_observer_.reset(); - } else { - animation_observer_.reset(new CallbackAnimationObserver( - container_animator, - base::Bind(&KeyboardController::ShowAnimationFinished, - base::Unretained(this)))); - container_animator->AddObserver(animation_observer_.get()); - } + animation_observer_.reset(new CallbackAnimationObserver( + container_animator, + base::BindOnce(&KeyboardController::ShowAnimationFinished, + base::Unretained(this)))); + container_animator->AddObserver(animation_observer_.get()); ui_->ShowKeyboardContainer(container_.get()); @@ -675,7 +638,7 @@ void KeyboardController::PopulateKeyboardContent(int64_t display_id, container_->layer()->SetOpacity(1.0); ChangeState(KeyboardControllerState::SHOWN); - NotifyKeyboardBoundsChangingAndEnsrueCaretInWorkArea(); + NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea(); } bool KeyboardController::WillHideKeyboard() const { @@ -689,7 +652,7 @@ void KeyboardController::ShowAnimationFinished() { } void KeyboardController:: - NotifyKeyboardBoundsChangingAndEnsrueCaretInWorkArea() { + NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea() { // Notify observers after animation finished to prevent reveal desktop // background during animation. NotifyContentsBoundsChanging(container_->bounds()); @@ -697,20 +660,12 @@ void KeyboardController:: } void KeyboardController::AdjustKeyboardBounds() { - // When keyboard is floating, no resize is necessary. - if (keyboard_mode_ == FLOATING) - return; - - if (keyboard_mode_ == FULL_WIDTH) { - // TODO(bshe): revisit this logic after we decide to support resize virtual - // keyboard. - int keyboard_height = GetContainerWindow()->bounds().height(); - const gfx::Rect& root_bounds = container_->GetRootWindow()->bounds(); - gfx::Rect new_bounds = root_bounds; - new_bounds.set_y(root_bounds.height() - keyboard_height); - new_bounds.set_height(keyboard_height); - GetContainerWindow()->SetBounds(new_bounds); - } + int keyboard_height = GetContainerWindow()->bounds().height(); + const gfx::Rect& root_bounds = container_->GetRootWindow()->bounds(); + gfx::Rect new_bounds = root_bounds; + new_bounds.set_y(root_bounds.height() - keyboard_height); + new_bounds.set_height(keyboard_height); + GetContainerWindow()->SetBounds(new_bounds); } void KeyboardController::CheckStateTransition(KeyboardControllerState prev, @@ -745,7 +700,7 @@ void KeyboardController::ChangeState(KeyboardControllerState state) { if (state != KeyboardControllerState::WILL_HIDE) weak_factory_will_hide_.InvalidateWeakPtrs(); if (state != KeyboardControllerState::LOADING_EXTENSION) - show_on_resize_ = false; + show_on_content_update_ = false; for (KeyboardControllerObserver& observer : observer_list_) observer.OnStateChanged(state); @@ -755,8 +710,8 @@ void KeyboardController::ChangeState(KeyboardControllerState state) { case KeyboardControllerState::WILL_HIDE: base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, - base::Bind(&KeyboardController::ReportLingeringState, - weak_factory_report_lingering_state_.GetWeakPtr()), + base::BindOnce(&KeyboardController::ReportLingeringState, + weak_factory_report_lingering_state_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kReportLingeringStateDelayMs)); break; default: diff --git a/chromium/ui/keyboard/keyboard_controller.h b/chromium/ui/keyboard/keyboard_controller.h index a8da8554f2e..47331edca04 100644 --- a/chromium/ui/keyboard/keyboard_controller.h +++ b/chromium/ui/keyboard/keyboard_controller.h @@ -35,17 +35,6 @@ class KeyboardUI; // or hide animation finishes. constexpr int kAnimationDistance = 30; -enum KeyboardMode { - // Invalid mode. - NONE, - // Full width virtual keyboard. The virtual keyboard window has the same width - // as the display. - FULL_WIDTH, - // Floating virtual keyboard. The virtual keyboard window has customizable - // width and is draggable. - FLOATING, -}; - // Represents the current state of the keyboard managed by the controller. // Don't change the numeric value of the members because they are used in UMA // - VirtualKeyboard.ControllerStateTransition. @@ -113,10 +102,6 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, bool keyboard_locked() const { return keyboard_locked_; } - KeyboardMode keyboard_mode() const { return keyboard_mode_; } - - void SetKeyboardMode(KeyboardMode mode); - // Immediately starts hiding animation of virtual keyboard and notifies // observers bounds change. This method forcibly sets keyboard_locked_ // false while closing the keyboard. @@ -163,8 +148,6 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // For access to SetContainerBounds. friend class KeyboardLayoutManager; - bool show_on_resize() const { return show_on_resize_; } - // aura::WindowObserver overrides void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override; void OnWindowAddedToRootWindow(aura::Window* window) override; @@ -183,8 +166,8 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, void OnShowImeIfNeeded() override; // Sets the bounds of the container window. Shows the keyboard if contents - // is first loaded and show_on_resize() is true. Called by - // KayboardLayoutManager. + // is first loaded and show_on_content_update_ is true. Called by + // KeyboardLayoutManager. void SetContainerBounds(const gfx::Rect& new_bounds, const bool contents_loaded); @@ -199,7 +182,7 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // is aborted, it won't be called. void ShowAnimationFinished(); - void NotifyKeyboardBoundsChangingAndEnsrueCaretInWorkArea(); + void NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea(); // Called when the keyboard mode is set or the keyboard is moved to another // display. @@ -222,10 +205,11 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // uses container_'s animator. std::unique_ptr<CallbackAnimationObserver> animation_observer_; - bool show_on_resize_; + // If true, show the keyboard window when keyboard UI content updates. + bool show_on_content_update_; + // If true, the keyboard is always visible even if no window has input focus. bool keyboard_locked_; - KeyboardMode keyboard_mode_; KeyboardEventFilter event_filter_; base::ObserverList<KeyboardControllerObserver> observer_list_; diff --git a/chromium/ui/keyboard/keyboard_controller_unittest.cc b/chromium/ui/keyboard/keyboard_controller_unittest.cc index 170ef7668cf..b1e23d904d8 100644 --- a/chromium/ui/keyboard/keyboard_controller_unittest.cc +++ b/chromium/ui/keyboard/keyboard_controller_unittest.cc @@ -11,6 +11,7 @@ #include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "base/test/scoped_task_environment.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/test/aura_test_helper.h" @@ -293,8 +294,8 @@ class KeyboardControllerTest : public testing::TestWithParam<bool>, if (controller_->ui()->GetContentsWindow()->bounds().height() == 0) { // Set initial bounds for test keyboard window. controller_->ui()->GetContentsWindow()->SetBounds( - FullWidthKeyboardBoundsFromRootBounds( - root_window()->bounds(), kDefaultVirtualKeyboardHeight)); + KeyboardBoundsFromRootBounds(root_window()->bounds(), + kDefaultVirtualKeyboardHeight)); } } } @@ -345,16 +346,16 @@ TEST_P(KeyboardControllerTest, KeyboardSize) { ASSERT_EQ(screen_bounds.height(), initial_bounds.y()); VerifyKeyboardWindowSize(container, keyboard); - // In FULL_WIDTH mode, attempt to change window width or move window up from - // the bottom are ignored. Changing window height is supported. + // Attempt to change window width or move window up from the bottom are + // ignored. Changing window height is supported. gfx::Rect expected_bounds(0, screen_bounds.height() - 50, screen_bounds.width(), 50); // The x position of new bounds may not be 0 if shelf is on the left side of - // screen. In FULL_WIDTH mode, the virtual keyboard should always align with - // the left edge of screen. See http://crbug.com/510595. + // screen. The virtual keyboard should always align with the left edge of + // screen. See http://crbug.com/510595. gfx::Rect new_bounds(10, 0, 50, 50); keyboard->SetBounds(new_bounds); ASSERT_EQ(expected_bounds, container->bounds()); @@ -371,7 +372,18 @@ TEST_P(KeyboardControllerTest, KeyboardSize) { VerifyKeyboardWindowSize(container, keyboard); } -TEST_P(KeyboardControllerTest, KeyboardSizeMultiRootWindow) { +// Flaky on Windows. See http://crbug.com/757044 +#if defined(OS_WIN) +#define MAYBE_KeyboardSizeMultiRootWindow DISABLED_KeyboardSizeMultiRootWindow +#else +#define MAYBE_KeyboardSizeMultiRootWindow KeyboardSizeMultiRootWindow +#endif + +// Since TEST_P does concatenation, macro prescan only occurs if it's invoked +// indirectly. +#define TEST_P_INDIRECT(a, b) TEST_P(a, b) + +TEST_P_INDIRECT(KeyboardControllerTest, MAYBE_KeyboardSizeMultiRootWindow) { aura::Window* container(controller()->GetContainerWindow()); aura::Window* keyboard(ui()->GetContentsWindow()); gfx::Rect screen_bounds = root_window()->bounds(); @@ -403,18 +415,6 @@ TEST_P(KeyboardControllerTest, KeyboardSizeMultiRootWindow) { VerifyKeyboardWindowSize(container, keyboard); } -TEST_P(KeyboardControllerTest, FloatingKeyboardSize) { - aura::Window* container(controller()->GetContainerWindow()); - aura::Window* keyboard(ui()->GetContentsWindow()); - root_window()->AddChild(container); - controller()->SetKeyboardMode(FLOATING); - container->AddChild(keyboard); - gfx::Rect new_bounds(0, 50, 50, 50); - keyboard->SetBounds(new_bounds); - ASSERT_EQ(new_bounds, container->bounds()); - VerifyKeyboardWindowSize(container, keyboard); -} - // Tests that tapping/clicking inside the keyboard does not give it focus. TEST_P(KeyboardControllerTest, ClickDoesNotFocusKeyboard) { ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; @@ -529,66 +529,6 @@ TEST_P(KeyboardControllerTest, CheckOverscrollInsetDuringVisibilityChange) { EXPECT_TRUE(ShouldEnableInsets(ui()->GetContentsWindow())); } -// Verify switch to FLOATING mode will reset the overscroll or resize and when -// in FLOATING mode, overscroll or resize wont be triggered. -TEST_P(KeyboardControllerTest, FloatingKeyboardDontOverscrollOrResize) { - ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; - ui::DummyTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT); - ui::DummyTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE); - - base::RunLoop run_loop; - aura::Window* container(controller()->GetContainerWindow()); - root_window()->AddChild(container); - std::unique_ptr<KeyboardContainerObserver> keyboard_container_observer( - new KeyboardContainerObserver(container, &run_loop)); - gfx::Rect screen_bounds = root_window()->bounds(); - ScopedTouchKeyboardEnabler scoped_touch_keyboard_enabler; - - SetFocus(&input_client); - gfx::Rect expected_bounds( - 0, screen_bounds.height() - kDefaultVirtualKeyboardHeight, - screen_bounds.width(), kDefaultVirtualKeyboardHeight); - // Verify overscroll or resize is in effect. - EXPECT_EQ(expected_bounds, notified_bounds()); - EXPECT_EQ(1, number_of_calls()); - - controller()->SetKeyboardMode(FLOATING); - // Switch to FLOATING should clear overscroll or resize. - EXPECT_EQ(gfx::Rect(), notified_bounds()); - EXPECT_EQ(2, number_of_calls()); - SetFocus(&no_input_client); - run_loop.Run(); - EXPECT_EQ(gfx::Rect(), notified_bounds()); - EXPECT_EQ(3, number_of_calls()); - SetFocus(&input_client); - // In FLOATING mode, no overscroll or resize should be triggered. - EXPECT_EQ(3, number_of_calls()); - EXPECT_EQ(gfx::Rect(), controller()->current_keyboard_bounds()); -} - -// Verify switch to FULL_WIDTH mode will move virtual keyboard to the right -// place and sets the correct overscroll. -TEST_P(KeyboardControllerTest, SwitchToFullWidthVirtualKeyboard) { - ScopedTouchKeyboardEnabler scoped_keyboard_enabler; - ui::DummyTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT); - - aura::Window* container(controller()->GetContainerWindow()); - root_window()->AddChild(container); - gfx::Rect screen_bounds = root_window()->bounds(); - SetFocus(&input_client); - - controller()->SetKeyboardMode(FLOATING); - EXPECT_EQ(gfx::Rect(), notified_bounds()); - EXPECT_EQ(gfx::Rect(), controller()->current_keyboard_bounds()); - - controller()->SetKeyboardMode(FULL_WIDTH); - gfx::Rect expected_bounds( - 0, screen_bounds.height() - kDefaultVirtualKeyboardHeight, - screen_bounds.width(), kDefaultVirtualKeyboardHeight); - EXPECT_EQ(expected_bounds, notified_bounds()); - EXPECT_EQ(expected_bounds, controller()->current_keyboard_bounds()); -} - TEST_P(KeyboardControllerTest, AlwaysVisibleWhenLocked) { ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; ui::DummyTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT); @@ -750,24 +690,25 @@ TEST_P(KeyboardControllerAnimationTest, ContainerShowWhileHide) { EXPECT_EQ(1.0, layer->opacity()); } -// Test for crbug.com/568274. -TEST_P(KeyboardControllerTest, FloatingKeyboardShowOnFirstTap) { - ScopedTouchKeyboardEnabler scoped_keyboard_enabler; - aura::Window* container(controller()->GetContainerWindow()); - aura::Window* keyboard(ui()->GetContentsWindow()); - root_window()->AddChild(container); +TEST_P(KeyboardControllerAnimationTest, SetBoundsOnOldKeyboardUiReference) { + ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; - controller()->SetKeyboardMode(FLOATING); - container->AddChild(keyboard); - // Mock focus on an input field. - ui()->GetInputMethod()->ShowImeIfNeeded(); - // Mock set keyboard size from javascript side. In floating mode, virtual - // keyboard's size is decided by client. - gfx::Rect new_bounds(0, 50, 50, 50); - keyboard->SetBounds(new_bounds); - ASSERT_EQ(new_bounds, container->bounds()); - EXPECT_TRUE(keyboard->IsVisible()); - EXPECT_TRUE(container->IsVisible()); + // Ensure keyboard ui is populated + ui::Layer* layer = keyboard_container()->layer(); + ShowKeyboard(); + RunAnimationForLayer(layer); + + ASSERT_TRUE(controller()->ui()); + + aura::Window* container_window = controller()->GetContainerWindow(); + + // Simulate removal of keyboard controller from root window as done by + // RootWindowController::DeactivateKeyboard() + container_window->parent()->RemoveChild(container_window); + + // lingering handle to the contents window is adjusted. + // container_window's LayoutManager should abort silently and not crash. + controller()->ui()->GetContentsWindow()->SetBounds(gfx::Rect()); } TEST_P(KeyboardControllerTest, DisplayChangeShouldNotifyBoundsChange) { @@ -777,7 +718,6 @@ TEST_P(KeyboardControllerTest, DisplayChangeShouldNotifyBoundsChange) { aura::Window* container(controller()->GetContainerWindow()); root_window()->AddChild(container); - controller()->SetKeyboardMode(FULL_WIDTH); SetFocus(&input_client); gfx::Rect new_bounds(0, 0, 1280, 800); ASSERT_NE(new_bounds, root_window()->bounds()); diff --git a/chromium/ui/keyboard/keyboard_layout_manager.cc b/chromium/ui/keyboard/keyboard_layout_manager.cc index 8b718c15da0..68d34977049 100644 --- a/chromium/ui/keyboard/keyboard_layout_manager.cc +++ b/chromium/ui/keyboard/keyboard_layout_manager.cc @@ -16,30 +16,19 @@ void KeyboardLayoutManager::OnWindowResized() { if (contents_window_) { gfx::Rect container_bounds = controller_->GetContainerWindow()->bounds(); // Always align container window and keyboard window. - if (controller_->keyboard_mode() == FULL_WIDTH) { - SetChildBounds(contents_window_, gfx::Rect(container_bounds.size())); - } else { - SetChildBoundsDirect(contents_window_, - gfx::Rect(container_bounds.size())); - } + SetChildBounds(contents_window_, gfx::Rect(container_bounds.size())); } } void KeyboardLayoutManager::OnWindowAddedToLayout(aura::Window* child) { DCHECK(!contents_window_); contents_window_ = child; - if (controller_->keyboard_mode() == FULL_WIDTH) { - controller_->GetContainerWindow()->SetBounds(gfx::Rect()); - } else if (controller_->keyboard_mode() == FLOATING) { - controller_->GetContainerWindow()->SetBounds(child->bounds()); - SetChildBoundsDirect(contents_window_, gfx::Rect(child->bounds().size())); - } + controller_->GetContainerWindow()->SetBounds(gfx::Rect()); } void KeyboardLayoutManager::SetChildBounds(aura::Window* child, const gfx::Rect& requested_bounds) { DCHECK(child == contents_window_); - TRACE_EVENT0("vk", "KeyboardLayoutSetChildBounds"); // Request to change the bounds of the contents window @@ -49,17 +38,22 @@ void KeyboardLayoutManager::SetChildBounds(aura::Window* child, const aura::Window* root_window = controller_->GetContainerWindow()->GetRootWindow(); + + // If the keyboard has been deactivated, this reference will be null. + if (!root_window) + return; + gfx::Rect new_bounds = requested_bounds; - if (controller_->keyboard_mode() == FULL_WIDTH) { - // Honors only the height of the request bounds - const gfx::Rect& window_bounds = root_window->bounds(); - new_bounds.set_y(window_bounds.height() - requested_bounds.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); - new_bounds.set_width(window_bounds.width()); - } + + // Honors only the height of the request bounds + const gfx::Rect& window_bounds = root_window->bounds(); + new_bounds.set_y(window_bounds.height() - requested_bounds.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); + new_bounds.set_width(window_bounds.width()); + // 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. diff --git a/chromium/ui/keyboard/keyboard_test_util.cc b/chromium/ui/keyboard/keyboard_test_util.cc index f62b41a538a..ebd8b715aba 100644 --- a/chromium/ui/keyboard/keyboard_test_util.cc +++ b/chromium/ui/keyboard/keyboard_test_util.cc @@ -92,8 +92,8 @@ void WaitControllerStateChangesTo(KeyboardControllerState state) { waiter.Wait(); } -gfx::Rect FullWidthKeyboardBoundsFromRootBounds(const gfx::Rect& root_bounds, - int keyboard_height) { +gfx::Rect KeyboardBoundsFromRootBounds(const gfx::Rect& root_bounds, + int keyboard_height) { return gfx::Rect(root_bounds.x(), root_bounds.bottom() - keyboard_height, root_bounds.width(), keyboard_height); } diff --git a/chromium/ui/keyboard/keyboard_test_util.h b/chromium/ui/keyboard/keyboard_test_util.h index edca936431d..3d7dc4f6620 100644 --- a/chromium/ui/keyboard/keyboard_test_util.h +++ b/chromium/ui/keyboard/keyboard_test_util.h @@ -27,10 +27,9 @@ bool WaitUntilHidden(); void WaitControllerStateChangesTo(const KeyboardControllerState state); // Gets the calculated keyboard bounds from |root_bounds|. The keyboard height -// is specified by |keyboard_height|. This should be only called when keyboard -// is in FULL_WDITH mode. -gfx::Rect FullWidthKeyboardBoundsFromRootBounds(const gfx::Rect& root_bounds, - int keyboard_height); +// is specified by |keyboard_height|. +gfx::Rect KeyboardBoundsFromRootBounds(const gfx::Rect& root_bounds, + int keyboard_height); class FakeKeyboardUI : public KeyboardUI { public: diff --git a/chromium/ui/latency/latency_info.cc b/chromium/ui/latency/latency_info.cc index ff1fffe2980..dbc6da7f05f 100644 --- a/chromium/ui/latency/latency_info.cc +++ b/chromium/ui/latency/latency_info.cc @@ -338,8 +338,8 @@ LatencyInfo::AsTraceableData() { new base::DictionaryValue()); component_info->SetDouble("comp_id", static_cast<double>(lc.first.second)); component_info->SetDouble( - "time", - static_cast<double>(lc.second.event_time.ToInternalValue())); + "time", static_cast<double>( + lc.second.event_time.since_origin().InMicroseconds())); component_info->SetDouble("count", lc.second.event_count); component_info->SetDouble("sequence_number", lc.second.sequence_number); diff --git a/chromium/ui/latency/latency_info_unittest.cc b/chromium/ui/latency/latency_info_unittest.cc index 16e4abd3681..3e872d4ac6c 100644 --- a/chromium/ui/latency/latency_info_unittest.cc +++ b/chromium/ui/latency/latency_info_unittest.cc @@ -10,21 +10,24 @@ namespace ui { +namespace { + +// Returns a fake TimeTicks based on the given microsecond offset. +base::TimeTicks ToTestTimeTicks(int64_t micros) { + return base::TimeTicks() + base::TimeDelta::FromMicroseconds(micros); +} + +} // namespace + TEST(LatencyInfoTest, AddTwoSeparateEvent) { LatencyInfo info; info.set_trace_id(1); EXPECT_FALSE(info.began()); - info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - 0, - 1, - base::TimeTicks::FromInternalValue(100), - 1); + info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, + 1, ToTestTimeTicks(100), 1); EXPECT_TRUE(info.began()); - info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, - 1, - 5, - base::TimeTicks::FromInternalValue(1000), - 2); + info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 1, + 5, ToTestTimeTicks(1000), 2); EXPECT_EQ(info.latency_components().size(), 2u); LatencyInfo::LatencyComponent component; @@ -36,27 +39,21 @@ TEST(LatencyInfoTest, AddTwoSeparateEvent) { info.FindLatency(INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, 0, &component)); EXPECT_EQ(component.sequence_number, 1); EXPECT_EQ(component.event_count, 1u); - EXPECT_EQ(component.event_time.ToInternalValue(), 100); + EXPECT_EQ(component.event_time, ToTestTimeTicks(100)); EXPECT_TRUE( info.FindLatency(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 1, &component)); EXPECT_EQ(component.sequence_number, 5); EXPECT_EQ(component.event_count, 2u); - EXPECT_EQ(component.event_time.ToInternalValue(), 1000); + EXPECT_EQ(component.event_time, ToTestTimeTicks(1000)); } TEST(LatencyInfoTest, AddTwoSameEvent) { LatencyInfo info; info.set_trace_id(1); - info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, - 0, - 30, - base::TimeTicks::FromInternalValue(100), - 2); - info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, - 0, - 13, - base::TimeTicks::FromInternalValue(200), - 3); + info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, + 30, ToTestTimeTicks(100), 2); + info.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, + 13, ToTestTimeTicks(200), 3); EXPECT_EQ(info.latency_components().size(), 1u); LatencyInfo::LatencyComponent component; @@ -68,7 +65,7 @@ TEST(LatencyInfoTest, AddTwoSameEvent) { info.FindLatency(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, &component)); EXPECT_EQ(component.sequence_number, 30); EXPECT_EQ(component.event_count, 5u); - EXPECT_EQ(component.event_time.ToInternalValue(), (100 * 2 + 200 * 3) / 5); + EXPECT_EQ(component.event_time, ToTestTimeTicks((100 * 2 + 200 * 3) / 5)); } TEST(LatencyInfoTest, RemoveLatency) { 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 fbd0d800d60..81741683f54 100644 --- a/chromium/ui/login/account_picker/md_screen_account_picker.js +++ b/chromium/ui/login/account_picker/md_screen_account_picker.js @@ -32,7 +32,7 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { 'removeUserPodFingerprintIcon', 'setPinEnabledForUser', 'setAuthType', - 'setTouchViewState', + 'setTabletModeState', 'setPublicSessionDisplayName', 'setPublicSessionLocales', 'setPublicSessionKeyboardLayouts', @@ -146,7 +146,7 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { // again already. If that happens, ignore the onShow() call. return; } - chrome.send('getTouchViewState'); + chrome.send('getTabletModeState'); if (!this.firstShown_) return; this.firstShown_ = false; @@ -348,11 +348,11 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { }, /** - * Sets the state of touch view mode. - * @param {boolean} isTouchViewEnabled true if the mode is on. + * Sets the state of tablet mode. + * @param {boolean} isTabletModeEnabled true if the mode is on. */ - setTouchViewState: function(isTouchViewEnabled) { - $('pod-row').setTouchViewState(isTouchViewEnabled); + setTabletModeState: function(isTabletModeEnabled) { + $('pod-row').setTabletModeState(isTabletModeEnabled); }, /** 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 c51218a828a..9f30bcfa6d7 100644 --- a/chromium/ui/login/account_picker/md_user_pod_row.js +++ b/chromium/ui/login/account_picker/md_user_pod_row.js @@ -2909,8 +2909,8 @@ cr.define('login', function() { // Array of users that are shown (public/supervised/regular). users_: [], - // If we're in Touch View mode. - touchViewEnabled_: false, + // If we're in tablet mode. + tabletModeEnabled_: false, // If testing mode is enabled. testingModeEnabled_: false, @@ -2957,14 +2957,14 @@ cr.define('login', function() { /** * Return true if user pod row has only single user pod in it, which should - * always be focused except desktop and touch view modes. + * always be focused except desktop and tablet modes. * @type {boolean} */ get alwaysFocusSinglePod() { var isDesktopUserManager = Oobe.getInstance().displayType == DISPLAY_TYPE.DESKTOP_USER_MANAGER; - return (isDesktopUserManager || this.touchViewEnabled_) ? + return (isDesktopUserManager || this.tabletModeEnabled_) ? false : this.pods.length == 1; }, @@ -3519,16 +3519,18 @@ cr.define('login', function() { }, /** - * Sets the state of touch view mode. - * @param {boolean} isTouchViewEnabled true if the mode is on. + * Sets the state of tablet mode. + * @param {boolean} isTabletModeEnabled true if the mode is on. */ - setTouchViewState: function(isTouchViewEnabled) { - this.touchViewEnabled_ = isTouchViewEnabled; + setTabletModeState: function(isTabletModeEnabled) { + this.tabletModeEnabled_ = isTabletModeEnabled; this.pods.forEach(function(pod, index) { - pod.actionBoxAreaElement.classList.toggle('forced', isTouchViewEnabled); - if (pod.isPublicSessionPod) + pod.actionBoxAreaElement.classList.toggle( + 'forced', isTabletModeEnabled); + if (pod.isPublicSessionPod) { pod.querySelector('.button-container') - .classList.toggle('forced', isTouchViewEnabled); + .classList.toggle('forced', isTabletModeEnabled); + } }); }, @@ -3677,6 +3679,8 @@ cr.define('login', function() { // apply to account picker. // This is a hacky solution: we can make #scroll-container hide the // overflow area and manully position #inner-container. + // NOTE: The global states set here might need to be cleared in + // handleHide. Please update the code there when adding new stuff here. var isScreenShrinked = this.isScreenShrinked_(); $('scroll-container') .classList.toggle('disable-scroll', isScreenShrinked); @@ -4784,6 +4788,11 @@ cr.define('login', function() { event, this.listeners_[event][0], this.listeners_[event][1]); } $('login-header-bar').buttonsTabIndex = 0; + + // Clear global states that should only applies to account picker. + $('scroll-container').classList.remove('disable-scroll'); + $('inner-container').classList.remove('disable-scroll'); + $('inner-container').style.top = 'unset'; }, /** diff --git a/chromium/ui/login/account_picker/screen_account_picker.js b/chromium/ui/login/account_picker/screen_account_picker.js index 9af9bf08945..dd272b971c4 100644 --- a/chromium/ui/login/account_picker/screen_account_picker.js +++ b/chromium/ui/login/account_picker/screen_account_picker.js @@ -39,7 +39,7 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { 'removeUserPodFingerprintIcon', 'setPinEnabledForUser', 'setAuthType', - 'setTouchViewState', + 'setTabletModeState', 'setPublicSessionDisplayName', 'setPublicSessionLocales', 'setPublicSessionKeyboardLayouts', @@ -148,7 +148,7 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { // again already. If that happens, ignore the onShow() call. return; } - chrome.send('getTouchViewState'); + chrome.send('getTabletModeState'); if (!this.firstShown_) return; this.firstShown_ = false; @@ -425,11 +425,11 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() { }, /** - * Sets the state of touch view mode. - * @param {boolean} isTouchViewEnabled true if the mode is on. + * Sets the state of tablet mode. + * @param {boolean} isTabletModeEnabled true if the mode is on. */ - setTouchViewState: function(isTouchViewEnabled) { - $('pod-row').setTouchViewState(isTouchViewEnabled); + setTabletModeState: function(isTabletModeEnabled) { + $('pod-row').setTabletModeState(isTabletModeEnabled); }, /** diff --git a/chromium/ui/login/account_picker/user_pod_row.css b/chromium/ui/login/account_picker/user_pod_row.css index b8a0ef64dd6..29bc6f66bf4 100644 --- a/chromium/ui/login/account_picker/user_pod_row.css +++ b/chromium/ui/login/account_picker/user_pod_row.css @@ -163,7 +163,7 @@ html[dir=rtl] .main-pane { } .name-container, -.pod.focused:not(.multiprofiles-policy-applied):not(.public-account) .auth-container { +.pod.focused:not(.multiprofiles-policy-applied) .auth-container { background-color: white; display: flex; position: absolute; diff --git a/chromium/ui/login/account_picker/user_pod_row.js b/chromium/ui/login/account_picker/user_pod_row.js index 2bbe7060b88..eeaf35190a4 100644 --- a/chromium/ui/login/account_picker/user_pod_row.js +++ b/chromium/ui/login/account_picker/user_pod_row.js @@ -2684,8 +2684,8 @@ cr.define('login', function() { // Array of users that are shown (public/supervised/regular). users_: [], - // If we're in Touch View mode. - touchViewEnabled_: false, + // If we're in tablet mode. + tabletModeEnabled_: false, /** @override */ decorate: function() { @@ -2721,15 +2721,16 @@ cr.define('login', function() { /** * Return true if user pod row has only single user pod in it, which should - * always be focused except desktop and touch view modes. + * always be focused except desktop and tablet modes. * @type {boolean} */ get alwaysFocusSinglePod() { var isDesktopUserManager = Oobe.getInstance().displayType == DISPLAY_TYPE.DESKTOP_USER_MANAGER; - return (isDesktopUserManager || this.touchViewEnabled_) ? - false : this.children.length == 1; + return (isDesktopUserManager || this.tabletModeEnabled_) ? + false : + this.children.length == 1; }, /** @@ -3246,13 +3247,14 @@ cr.define('login', function() { }, /** - * Sets the state of touch view mode. - * @param {boolean} isTouchViewEnabled true if the mode is on. + * Sets the state of tablet mode. + * @param {boolean} isTabletModeEnabled true if the mode is on. */ - setTouchViewState: function(isTouchViewEnabled) { - this.touchViewEnabled_ = isTouchViewEnabled; + setTabletModeState: function(isTabletModeEnabled) { + this.tabletModeEnabled_ = isTabletModeEnabled; this.pods.forEach(function(pod, index) { - pod.actionBoxAreaElement.classList.toggle('forced', isTouchViewEnabled); + pod.actionBoxAreaElement.classList.toggle( + 'forced', isTabletModeEnabled); }); }, diff --git a/chromium/ui/message_center/BUILD.gn b/chromium/ui/message_center/BUILD.gn index 15f7dd5d819..95c62220a34 100644 --- a/chromium/ui/message_center/BUILD.gn +++ b/chromium/ui/message_center/BUILD.gn @@ -4,8 +4,9 @@ import("//build/config/features.gni") import("//build/config/ui.gni") -import("//testing/test.gni") import("//components/vector_icons/vector_icons.gni") +import("//testing/test.gni") +import("//ui/base/ui_features.gni") aggregate_vector_icons("message_center_vector_icons") { icon_directory = "vector_icons" @@ -27,12 +28,13 @@ component("message_center") { deps = [ "//base", "//ui/base", + "//ui/strings", "//url", ] defines = [ "MESSAGE_CENTER_IMPLEMENTATION" ] - if (!is_ios && !is_android) { + if (enable_message_center) { deps += [ ":message_center_vector_icons", "//base:i18n", @@ -46,7 +48,6 @@ component("message_center") { "//ui/gfx/geometry", "//ui/native_theme", "//ui/resources", - "//ui/strings", ] configs += [ @@ -122,10 +123,6 @@ component("message_center") { "views/constants.h", "views/desktop_popup_alignment_delegate.cc", "views/desktop_popup_alignment_delegate.h", - "views/message_bubble_base.cc", - "views/message_bubble_base.h", - "views/message_center_bubble.cc", - "views/message_center_bubble.h", "views/message_center_button_bar.cc", "views/message_center_button_bar.h", "views/message_center_controller.h", @@ -192,16 +189,17 @@ component("message_center") { } } -static_library("test_support") { - testonly = true +if (enable_message_center) { + static_library("test_support") { + testonly = true - if (!is_ios && !is_android) { sources = [ "fake_message_center.cc", "fake_message_center.h", "fake_message_center_tray_delegate.cc", "fake_message_center_tray_delegate.h", ] + deps = [ "//base", "//base/test:test_support", @@ -209,53 +207,52 @@ static_library("test_support") { "//ui/gfx", "//ui/gfx/geometry", ] - } - - public_deps = [ - ":message_center", - ] -} - -test("message_center_unittests") { - sources = [ - "test/run_all_unittests.cc", - ] - deps = [ - ":message_center", - ":test_support", - "//base", - "//base/test:test_support", - "//skia", - "//testing/gmock", - "//testing/gtest", - "//ui/base", - "//ui/base:test_support", - "//ui/events", - "//ui/events:test_support", - "//ui/gfx", - "//ui/gfx/geometry", - "//ui/gl", - "//ui/gl:test_support", - "//ui/resources", - "//ui/resources:ui_test_pak", - "//url", - ] - - data_deps = [ - "//third_party/mesa:osmesa", - "//ui/resources:ui_test_pak_data", - ] + public_deps = [ + ":message_center", + ] + } - if (!is_ios && !is_android) { - sources += [ + test("message_center_unittests") { + sources = [ "cocoa/notification_controller_unittest.mm", "cocoa/popup_collection_unittest.mm", "cocoa/popup_controller_unittest.mm", "message_center_impl_unittest.cc", "message_center_tray_unittest.cc", + "mojo/struct_traits_unittest.cc", "notification_delegate_unittest.cc", "notification_list_unittest.cc", + "test/run_all_unittests.cc", + ] + + deps = [ + ":message_center", + ":test_support", + "//base", + "//base/test:test_support", + "//mojo/edk/system", + "//skia", + "//testing/gmock", + "//testing/gtest", + "//ui/base", + "//ui/base:test_support", + "//ui/events", + "//ui/events:test_support", + "//ui/gfx", + "//ui/gfx:test_support", + "//ui/gfx/geometry", + "//ui/gl", + "//ui/gl:test_support", + "//ui/message_center/mojo:test_interfaces", + "//ui/resources", + "//ui/resources:ui_test_pak", + "//url", + ] + + data_deps = [ + "//third_party/mesa:osmesa", + "//ui/resources:ui_test_pak_data", ] if (is_mac) { @@ -281,14 +278,5 @@ test("message_center_unittests") { "//ui/views:test_support", ] } - - if (!is_ios) { - sources += [ "mojo/struct_traits_unittest.cc" ] - deps += [ - "//mojo/edk/system", - "//ui/gfx:test_support", - "//ui/message_center/mojo:test_interfaces", - ] - } - } # !is_ios && !is_android + } } diff --git a/chromium/ui/message_center/OWNERS b/chromium/ui/message_center/OWNERS index cc04892d667..134480b4cbc 100644 --- a/chromium/ui/message_center/OWNERS +++ b/chromium/ui/message_center/OWNERS @@ -1,5 +1,6 @@ dewittj@chromium.org mukai@chromium.org +peter@chromium.org stevenjb@chromium.org yoshiki@chromium.org diff --git a/chromium/ui/message_center/dummy_message_center.cc b/chromium/ui/message_center/dummy_message_center.cc index 26225db6e72..56410a6ec3d 100644 --- a/chromium/ui/message_center/dummy_message_center.cc +++ b/chromium/ui/message_center/dummy_message_center.cc @@ -6,11 +6,11 @@ #include "ui/message_center/message_center.h" // This file contains dummy implementation of MessageCenter and used to compile -// and link with Android and iOS implementations of Chrome which do not have +// and link with Android implementations of Chrome which do not have // notification systems yet. This is to avoid spreading compile-time flags // everywhere in the code. -#if !defined(OS_ANDROID) && !defined(OS_IOS) -#error This file should only be used in Android or iOS builds. +#if !defined(OS_ANDROID) +#error This file should only be used in Android. #endif namespace message_center { diff --git a/chromium/ui/message_center/fake_message_center.cc b/chromium/ui/message_center/fake_message_center.cc index 1900b0e65f6..c61176eda10 100644 --- a/chromium/ui/message_center/fake_message_center.cc +++ b/chromium/ui/message_center/fake_message_center.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ui/message_center/fake_message_center.h" +#include "base/strings/string_util.h" #include "ui/message_center/notification_list.h" namespace message_center { @@ -52,7 +53,11 @@ bool FakeMessageCenter::HasClickedListener(const std::string& id) { message_center::Notification* FakeMessageCenter::FindVisibleNotificationById( const std::string& id) { - return NULL; + for (auto* notification : GetVisibleNotifications()) { + if (id == notification->id()) + return notification; + } + return nullptr; } const NotificationList::Notifications& @@ -142,6 +147,13 @@ void FakeMessageCenter::RestartPopupTimers() {} void FakeMessageCenter::PausePopupTimers() {} +const base::string16& FakeMessageCenter::GetProductOSName() const { + return base::EmptyString16(); +} + +void FakeMessageCenter::SetProductOSName( + const base::string16& product_os_name) {} + void FakeMessageCenter::DisableTimersForTest() {} void FakeMessageCenter::EnableChangeQueueForTest(bool enabled) {} diff --git a/chromium/ui/message_center/fake_message_center.h b/chromium/ui/message_center/fake_message_center.h index 7a8766ab2be..c489f6721b1 100644 --- a/chromium/ui/message_center/fake_message_center.h +++ b/chromium/ui/message_center/fake_message_center.h @@ -68,6 +68,8 @@ class FakeMessageCenter : public MessageCenter { bool IsMessageCenterVisible() const override; void RestartPopupTimers() override; void PausePopupTimers() override; + const base::string16& GetProductOSName() const override; + void SetProductOSName(const base::string16& product_os_name) override; protected: void DisableTimersForTest() override; diff --git a/chromium/ui/message_center/message_center.cc b/chromium/ui/message_center/message_center.cc index fde6d8d280b..aa305a37a2e 100644 --- a/chromium/ui/message_center/message_center.cc +++ b/chromium/ui/message_center/message_center.cc @@ -4,8 +4,10 @@ #include "ui/message_center/message_center.h" +#include "base/command_line.h" #include "base/observer_list.h" #include "ui/message_center/message_center_impl.h" +#include "ui/message_center/message_center_switches.h" namespace message_center { @@ -34,6 +36,21 @@ void MessageCenter::Shutdown() { g_message_center = NULL; } +// static +bool MessageCenter::IsNewStyleNotificationEnabled() { +// For Chrome OS, the default is Enabled. +// For other platforms, the default is Disabled. +#if defined(OS_CHROMEOS) + // Returns true if not explicitly disabled. + return !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableMessageCenterNewStyleNotification); +#else + // Returns true if explicitly enabled. + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableMessageCenterNewStyleNotification); +#endif +} + MessageCenter::MessageCenter() { } diff --git a/chromium/ui/message_center/message_center.h b/chromium/ui/message_center/message_center.h index 05c00662d43..2eebf0ff4b7 100644 --- a/chromium/ui/message_center/message_center.h +++ b/chromium/ui/message_center/message_center.h @@ -63,6 +63,10 @@ class MESSAGE_CENTER_EXPORT MessageCenter { // Destroys the global message_center object. static void Shutdown(); + // Returns if new style notification is enabled, i.e. NotificationViewMD is + // used instead of NotificationView. + static bool IsNewStyleNotificationEnabled(); + // Management of the observer list. virtual void AddObserver(MessageCenterObserver* observer) = 0; virtual void RemoveObserver(MessageCenterObserver* observer) = 0; @@ -189,6 +193,11 @@ class MESSAGE_CENTER_EXPORT MessageCenter { // example, after the mouse leaves the popup.) virtual void RestartPopupTimers() = 0; + // "Chromium OS" or "Chrome OS" in the current locale. + // Return empty string if not on these platforms. + virtual const base::string16& GetProductOSName() const = 0; + virtual void SetProductOSName(const base::string16& product_os_name) = 0; + protected: friend class ::DownloadNotification; friend class ::DownloadNotificationTestBase; diff --git a/chromium/ui/message_center/message_center_impl.cc b/chromium/ui/message_center/message_center_impl.cc index c4396746704..e2369ca3cb2 100644 --- a/chromium/ui/message_center/message_center_impl.cc +++ b/chromium/ui/message_center/message_center_impl.cc @@ -15,6 +15,7 @@ #include "base/memory/ptr_util.h" #include "base/observer_list.h" #include "base/stl_util.h" +#include "base/strings/string_util.h" #include "build/build_config.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/message_center_switches.h" @@ -943,6 +944,15 @@ void MessageCenterImpl::PausePopupTimers() { popup_timers_controller_->PauseAll(); } +const base::string16& MessageCenterImpl::GetProductOSName() const { + return product_os_name_; +} + +void MessageCenterImpl::SetProductOSName( + const base::string16& product_os_name) { + product_os_name_ = product_os_name; +} + void MessageCenterImpl::DisableTimersForTest() { popup_timers_controller_.reset(); } diff --git a/chromium/ui/message_center/message_center_impl.h b/chromium/ui/message_center/message_center_impl.h index 3dd9c7f349a..ea4348a36ef 100644 --- a/chromium/ui/message_center/message_center_impl.h +++ b/chromium/ui/message_center/message_center_impl.h @@ -84,6 +84,8 @@ class MessageCenterImpl : public MessageCenter, void EnterQuietModeWithExpire(const base::TimeDelta& expires_in) override; void RestartPopupTimers() override; void PausePopupTimers() override; + const base::string16& GetProductOSName() const override; + void SetProductOSName(const base::string16& product_os_name) override; void ForceNotificationFlush(const std::string& id) override; // NotificationBlocker::Observer overrides: @@ -137,6 +139,8 @@ class MessageCenterImpl : public MessageCenter, // center is visible. std::unique_ptr<internal::ChangeQueue> notification_queue_; + base::string16 product_os_name_; + DISALLOW_COPY_AND_ASSIGN(MessageCenterImpl); }; diff --git a/chromium/ui/message_center/message_center_style.h b/chromium/ui/message_center/message_center_style.h index be5acf24780..0caf6711cb7 100644 --- a/chromium/ui/message_center/message_center_style.h +++ b/chromium/ui/message_center/message_center_style.h @@ -9,6 +9,7 @@ #include "build/build_config.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/size.h" #include "ui/message_center/message_center_export.h" @@ -28,6 +29,7 @@ const int kNotificationImageBorderSize = 10; const int kNotificationPreferredImageWidth = 360; const int kNotificationPreferredImageHeight = 240; const int kSmallImageSize = 16; +const int kSmallImageSizeMD = 18; const int kSmallImagePadding = 4; // Limits. @@ -100,6 +102,15 @@ const SkColor kSmallImageMaskBackgroundColor = SkColorSetRGB(0xa3, 0xa3, 0xa3); const SkColor kControlButtonBackgroundColor = SkColorSetA(SK_ColorWHITE, 0.9 * 0xff); +// Accent colors of system notifications. +const SkColor kSystemNotificationColorNormal = SkColorSetRGB(0x33, 0x67, 0xd6); +const SkColor kSystemNotificationColorWarning = SkColorSetRGB(0xea, 0x61, 0x0); +const SkColor kSystemNotificationColorCriticalWarning = + SkColorSetRGB(0xc5, 0x39, 0x29); + +// Default accent color of notifications that are not generated by system. +const SkColor kNotificationDefaultAccentColor = gfx::kChromeIconGrey; + // Limits. // Given the size of an image, returns the size of the properly scaled-up image diff --git a/chromium/ui/message_center/message_center_tray.h b/chromium/ui/message_center/message_center_tray.h index 339654d5d90..50a5acc0060 100644 --- a/chromium/ui/message_center/message_center_tray.h +++ b/chromium/ui/message_center/message_center_tray.h @@ -21,10 +21,6 @@ namespace message_center { class MessageCenter; -// Implementation found with each supported platform's implementation of -// MessageCenterTrayDelegate. -MessageCenterTrayDelegate* CreateMessageCenterTray(); - // Class that observes a MessageCenter. Manages the popup and message center // bubbles. Tells the MessageCenterTrayHost when the tray is changed, as well // as when bubbles are shown and hidden. diff --git a/chromium/ui/message_center/notification.cc b/chromium/ui/message_center/notification.cc index b3387f0b81f..6e1bc1711cb 100644 --- a/chromium/ui/message_center/notification.cc +++ b/chromium/ui/message_center/notification.cc @@ -5,8 +5,17 @@ #include "ui/message_center/notification.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/vector_icon_types.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/message_center_style.h" #include "ui/message_center/notification_delegate.h" #include "ui/message_center/notification_types.h" +#include "ui/strings/grit/ui_strings.h" namespace message_center { @@ -14,6 +23,15 @@ namespace { unsigned g_next_serial_number_ = 0; +const gfx::ImageSkia CreateSolidColorImage(int width, + int height, + SkColor color) { + SkBitmap bitmap; + bitmap.allocN32Pixels(width, height); + bitmap.eraseColor(color); + return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); +} + } // namespace NotificationItem::NotificationItem(const base::string16& title, @@ -43,6 +61,7 @@ RichNotificationData::RichNotificationData(const RichNotificationData& other) small_image(other.small_image), items(other.items), progress(other.progress), + progress_status(other.progress_status), buttons(other.buttons), should_make_spoken_feedback_for_popup_updates( other.should_make_spoken_feedback_for_popup_updates), @@ -53,7 +72,10 @@ RichNotificationData::RichNotificationData(const RichNotificationData& other) vibration_pattern(other.vibration_pattern), renotify(other.renotify), silent(other.silent), - accessible_name(other.accessible_name) {} + accessible_name(other.accessible_name), + accent_color(other.accent_color), + use_image_as_icon(other.use_image_as_icon) { +} RichNotificationData::~RichNotificationData() = default; @@ -161,6 +183,18 @@ bool Notification::UseOriginAsContextMessage() const { origin_url_.SchemeIsHTTPOrHTTPS(); } +gfx::Image Notification::GenerateMaskedSmallIcon(SkColor color) const { + if (!vector_small_image().is_empty()) + return gfx::Image(gfx::CreateVectorIcon(vector_small_image(), color)); + + if (small_image().IsEmpty()) + return small_image(); + + gfx::ImageSkia image = small_image().AsImageSkia(); + return gfx::Image(gfx::ImageSkiaOperations::CreateMaskedImage( + CreateSolidColorImage(image.width(), image.height(), color), image)); +} + // static std::unique_ptr<Notification> Notification::CreateSystemNotification( const std::string& notification_id, @@ -169,14 +203,60 @@ std::unique_ptr<Notification> Notification::CreateSystemNotification( const gfx::Image& icon, const std::string& system_component_id, const base::Closure& click_callback) { - std::unique_ptr<Notification> notification(new Notification( + std::unique_ptr<Notification> notification = CreateSystemNotification( NOTIFICATION_TYPE_SIMPLE, notification_id, title, message, icon, base::string16() /* display_source */, GURL(), NotifierId(NotifierId::SYSTEM_COMPONENT, system_component_id), RichNotificationData(), - new HandleNotificationClickedDelegate(click_callback))); + new HandleNotificationClickedDelegate(click_callback), gfx::kNoneIcon, + SystemNotificationWarningLevel::CRITICAL_WARNING); notification->SetSystemPriority(); return notification; } +// static +std::unique_ptr<Notification> Notification::CreateSystemNotification( + NotificationType type, + const std::string& id, + const base::string16& title, + const base::string16& message, + const gfx::Image& icon, + const base::string16& display_source, + const GURL& origin_url, + const NotifierId& notifier_id, + const RichNotificationData& optional_fields, + scoped_refptr<NotificationDelegate> delegate, + const gfx::VectorIcon& small_image, + SystemNotificationWarningLevel color_type) { + SkColor color = message_center::kSystemNotificationColorNormal; + switch (color_type) { + case SystemNotificationWarningLevel::NORMAL: + color = message_center::kSystemNotificationColorNormal; + break; + case SystemNotificationWarningLevel::WARNING: + color = message_center::kSystemNotificationColorWarning; + break; + case SystemNotificationWarningLevel::CRITICAL_WARNING: + color = message_center::kSystemNotificationColorCriticalWarning; + break; + } + base::string16 display_source_or_default = display_source; + if (display_source_or_default.empty()) { + display_source_or_default = l10n_util::GetStringFUTF16( + IDS_MESSAGE_CENTER_NOTIFICATION_CHROMEOS_SYSTEM, + MessageCenter::Get()->GetProductOSName()); + } + std::unique_ptr<Notification> notification = base::MakeUnique<Notification>( + type, id, title, message, icon, display_source_or_default, origin_url, + notifier_id, optional_fields, delegate); + notification->set_accent_color(color); + notification->set_small_image( + small_image.is_empty() + ? gfx::Image() + : gfx::Image(gfx::CreateVectorIcon(small_image, color))); + if (!small_image.is_empty()) + notification->set_vector_small_image(small_image); + return notification; +} + } // namespace message_center diff --git a/chromium/ui/message_center/notification.h b/chromium/ui/message_center/notification.h index b8277519199..a3cd1dfe04b 100644 --- a/chromium/ui/message_center/notification.h +++ b/chromium/ui/message_center/notification.h @@ -14,24 +14,28 @@ #include "base/strings/string16.h" #include "base/time/time.h" #include "base/values.h" +#include "mojo/public/cpp/bindings/struct_traits.h" // nogncheck +#include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/skia_util.h" +#include "ui/gfx/vector_icon_types.h" #include "ui/message_center/message_center_export.h" #include "ui/message_center/notification_delegate.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/notifier_settings.h" #include "url/gurl.h" -#if !defined(OS_IOS) -#include "mojo/public/cpp/bindings/struct_traits.h" // nogncheck -#endif +namespace gfx { +struct VectorIcon; +} // namespace gfx namespace message_center { -#if !defined(OS_IOS) namespace mojom { class NotificationDataView; } -#endif // Represents an individual item in NOTIFICATION_TYPE_MULTIPLE notifications. struct MESSAGE_CENTER_EXPORT NotificationItem { @@ -50,6 +54,8 @@ enum class ButtonType { TEXT }; +enum class SystemNotificationWarningLevel { NORMAL, WARNING, CRITICAL_WARNING }; + // Represents a button to be shown as part of a notification. struct MESSAGE_CENTER_EXPORT ButtonInfo { explicit ButtonInfo(const base::string16& title); @@ -102,13 +108,33 @@ class MESSAGE_CENTER_EXPORT RichNotificationData { // notification. Optional. gfx::Image small_image; + // Vector version of |small_image|. + // Used by Notification::GenerateMaskedSmallIcon. + // If not available, |small_image| will be used by the method. Optional. + // + // Due to the restriction of CreateVectorIcon, this should be a pointer to + // globally defined VectorIcon instance e.g. kNotificationCapsLockIcon. + // gfx::Image created by gfx::CreateVectorIcon internally stores reference to + // VectorIcon, so the VectorIcon should live longer than gfx::Image instance. + // As a temporary solution to this problem, we make this variable a pointer + // and only pass globally defined constants. + // TODO(tetsui): Remove the pointer, after fixing VectorIconSource not to + // retain VectorIcon reference. https://crbug.com/760866 + const gfx::VectorIcon* vector_small_image = &gfx::kNoneIcon; + // Items to display on the notification. Only applicable for notifications // that have type NOTIFICATION_TYPE_MULTIPLE. std::vector<NotificationItem> items; // Progress, in range of [0-100], of NOTIFICATION_TYPE_PROGRESS notifications. + // Values outside of the range (e.g. -1) will show an infinite loading + // progress bar. int progress = 0; + // Status text string shown in NOTIFICATION_TYPE_PROGRESS notifications. + // If MD style notification is not enabled, this attribute is ignored. + base::string16 progress_status; + // Buttons that should show up on the notification. A maximum of 16 buttons // is supported by the current implementation, but this may differ between // platforms. @@ -142,6 +168,17 @@ class MESSAGE_CENTER_EXPORT RichNotificationData { // An accessible description of the notification's contents. base::string16 accessible_name; + + // Unified theme color used in new style notification. + // Usually, it should not be set directly. + // For system notification, CreateSystemNotification with + // SystemNotificationWarningLevel should be used. + SkColor accent_color = SK_ColorTRANSPARENT; + + // Shows |image| as the right icon when notification is collapsed, + // and hides the icon when the notification is expanded. + // This is only effective when new style notification is enabled. + bool use_image_as_icon = false; }; class MESSAGE_CENTER_EXPORT Notification { @@ -275,6 +312,14 @@ class MESSAGE_CENTER_EXPORT Notification { int progress() const { return optional_fields_.progress; } void set_progress(int progress) { optional_fields_.progress = progress; } + + base::string16 progress_status() const { + return optional_fields_.progress_status; + } + void set_progress_status(const base::string16& progress_status) { + optional_fields_.progress_status = progress_status; + } + // End unpacked values. // Images fetched asynchronously. @@ -289,6 +334,23 @@ class MESSAGE_CENTER_EXPORT Notification { optional_fields_.small_image = image; } + const gfx::VectorIcon& vector_small_image() const { + return *optional_fields_.vector_small_image; + } + // Due to the restriction of CreateVectorIcon, this should be a pointer to + // globally defined VectorIcon instance e.g. kNotificationCapsLockIcon. + // See detailed comment in RichNotificationData::vector_small_image. + void set_vector_small_image(const gfx::VectorIcon& image) { + optional_fields_.vector_small_image = ℑ + } + + // Mask the color of |small_image| to the given |color|. + // If |vector_small_image| is available, it returns the vector image + // filled by the |color|. + // Otherwise, it uses alpha channel of the rasterized |small_image| for + // masking. + gfx::Image GenerateMaskedSmallIcon(SkColor color) const; + // Buttons, with icons fetched asynchronously. const std::vector<ButtonInfo>& buttons() const { return optional_fields_.buttons; @@ -338,6 +400,16 @@ class MESSAGE_CENTER_EXPORT Notification { return optional_fields_.accessible_name; } + SkColor accent_color() const { return optional_fields_.accent_color; } + void set_accent_color(SkColor accent_color) { + optional_fields_.accent_color = accent_color; + } + + bool use_image_as_icon() const { return optional_fields_.use_image_as_icon; } + void set_use_image_as_icon(bool use_image_as_icon) { + optional_fields_.use_image_as_icon = use_image_as_icon; + } + NotificationDelegate* delegate() const { return delegate_.get(); } const RichNotificationData& rich_notification_data() const { @@ -357,6 +429,13 @@ class MESSAGE_CENTER_EXPORT Notification { // Helper method to create a simple system notification. |click_callback| // will be invoked when the notification is clicked. + // + // It should only be used for critical notification, as SetSystemPriority and + // CRITICAL_WARNING color are set inside, which means the notification would + // not go away without user interaction. + // + // TODO(tetsui): Add a function parameter |small_image| of gfx::VectorIcon, so + // display source of critical system notification is illustrated by icon. static std::unique_ptr<Notification> CreateSystemNotification( const std::string& notification_id, const base::string16& title, @@ -365,6 +444,25 @@ class MESSAGE_CENTER_EXPORT Notification { const std::string& system_component_id, const base::Closure& click_callback); + // Factory method to create all kinds of notifications generated by system, + // from normal priority ones to critical priority ones. + // |small_image| is a small icon show on the upper left header to illustrate + // |display_source| of the notification. + // One specified in the |optional_fields| is overridden. + static std::unique_ptr<Notification> CreateSystemNotification( + NotificationType type, + const std::string& id, + const base::string16& title, + const base::string16& message, + const gfx::Image& icon, + const base::string16& display_source, + const GURL& origin_url, + const NotifierId& notifier_id, + const RichNotificationData& optional_fields, + scoped_refptr<NotificationDelegate> delegate, + const gfx::VectorIcon& small_image, + SystemNotificationWarningLevel color_type); + protected: // The type of notification we'd like displayed. NotificationType type_; @@ -377,10 +475,13 @@ class MESSAGE_CENTER_EXPORT Notification { gfx::Image icon_; // The display string for the source of the notification. Could be - // the same as origin_url_, or the name of an extension. + // the same as |origin_url_|, or the name of an extension. + // Expected to be a localized user facing string. base::string16 display_source_; private: + friend struct mojo::StructTraits<mojom::NotificationDataView, Notification>; + // The origin URL of the script which requested the notification. // Can be empty if requested through a chrome app or extension or if // it's a system notification. @@ -394,10 +495,6 @@ class MESSAGE_CENTER_EXPORT Notification { // A proxy object that allows access back to the JavaScript object that // represents the notification, for firing events. scoped_refptr<NotificationDelegate> delegate_; - -#if !defined(OS_IOS) - friend struct mojo::StructTraits<mojom::NotificationDataView, Notification>; -#endif }; } // namespace message_center 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 b68afa7189f..b30ee4211b5 100644 --- a/chromium/ui/message_center/vector_icons/notification_expand_less.icon +++ b/chromium/ui/message_center/vector_icons/notification_expand_less.icon @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -CANVAS_DIMENSIONS, 24, -MOVE_TO, 12, 8, -R_LINE_TO, -6, 6, -R_LINE_TO, 1.41f, 1.41f, -LINE_TO, 12, 10.83f, -R_LINE_TO, 4.59f, 4.58f, -LINE_TO, 18, 14, +CANVAS_DIMENSIONS, 16, +MOVE_TO, 2.62f, 13, +LINE_TO, 8, 7.75f, +LINE_TO, 13.33f, 13, +LINE_TO, 15, 11.31f, +R_LINE_TO, -7, -6.89f, +R_LINE_TO, -7, 6.89f, CLOSE, END 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 d54e1e058b3..b779f5f8e5c 100644 --- a/chromium/ui/message_center/vector_icons/notification_expand_more.icon +++ b/chromium/ui/message_center/vector_icons/notification_expand_more.icon @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -CANVAS_DIMENSIONS, 24, -MOVE_TO, 16.59f, 8.59f, -LINE_TO, 12, 13.17f, -LINE_TO, 7.41f, 8.59f, -LINE_TO, 6, 10, -R_LINE_TO, 6, 6, -R_LINE_TO, 6, -6, +CANVAS_DIMENSIONS, 16, +MOVE_TO, 2.62f, 3, +LINE_TO, 8, 8.25f, +LINE_TO, 13.33f, 3, +LINE_TO, 15, 4.69f, +R_LINE_TO, -7, 6.89f, +R_LINE_TO, -7, -6.89f, CLOSE, END diff --git a/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc b/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc index f57d23ad449..90c14c1b443 100644 --- a/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc +++ b/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc @@ -8,7 +8,6 @@ #include "ui/display/screen.h" #include "ui/gfx/geometry/rect.h" #include "ui/message_center/message_center_style.h" -#include "ui/message_center/views/message_popup_collection.h" namespace message_center { diff --git a/chromium/ui/message_center/views/message_bubble_base.cc b/chromium/ui/message_center/views/message_bubble_base.cc deleted file mode 100644 index 25f0120c3b3..00000000000 --- a/chromium/ui/message_center/views/message_bubble_base.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/message_center/views/message_bubble_base.h" - -#include "base/bind.h" -#include "base/location.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "ui/views/widget/widget.h" -#include "ui/views/widget/widget_observer.h" - -namespace { -// Delay laying out the MessageBubbleBase until all notifications have been -// added and icons have had a chance to load. -const int kUpdateDelayMs = 50; -const int kMessageBubbleBaseDefaultMaxHeight = 400; -} - -namespace message_center { - -MessageBubbleBase::MessageBubbleBase(MessageCenter* message_center, - MessageCenterTray* tray) - : message_center_(message_center), - tray_(tray), - bubble_view_(NULL), - max_height_(kMessageBubbleBaseDefaultMaxHeight), - weak_ptr_factory_(this) { -} - -MessageBubbleBase::~MessageBubbleBase() { - if (bubble_view_) - bubble_view_->ResetDelegate(); -} - -void MessageBubbleBase::BubbleViewDestroyed() { - bubble_view_ = NULL; - OnBubbleViewDestroyed(); -} - -void MessageBubbleBase::ScheduleUpdate() { - weak_ptr_factory_.InvalidateWeakPtrs(); // Cancel any pending update. - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&MessageBubbleBase::UpdateBubbleView, - weak_ptr_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kUpdateDelayMs)); -} - -bool MessageBubbleBase::IsVisible() const { - return bubble_view() && bubble_view()->GetWidget()->IsVisible(); -} - -void MessageBubbleBase::SetMaxHeight(int height) { - // Maximum height makes sense only for the new design. - if (height == 0) - height = kMessageBubbleBaseDefaultMaxHeight; - if (height == max_height_) - return; - - max_height_ = height; - if (bubble_view_) - bubble_view_->SetMaxHeight(max_height_); -} - -} // namespace message_center diff --git a/chromium/ui/message_center/views/message_bubble_base.h b/chromium/ui/message_center/views/message_bubble_base.h deleted file mode 100644 index 779b9b3fa03..00000000000 --- a/chromium/ui/message_center/views/message_bubble_base.h +++ /dev/null @@ -1,76 +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_MESSAGE_CENTER_VIEWS_MESSAGE_BUBBLE_BASE_H_ -#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_BUBBLE_BASE_H_ - -#include <memory> - -#include "base/macros.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/message_center/message_center.h" -#include "ui/message_center/message_center_export.h" -#include "ui/views/bubble/tray_bubble_view.h" - -namespace message_center { -class MessageCenterTray; - -class MESSAGE_CENTER_EXPORT MessageBubbleBase { - public: - MessageBubbleBase(MessageCenter* message_center, MessageCenterTray* tray); - - virtual ~MessageBubbleBase(); - - // Gets called when the bubble view associated with this bubble is - // destroyed. Clears |bubble_view_| and calls OnBubbleViewDestroyed. - void BubbleViewDestroyed(); - - // Sets/Gets the maximum height of the bubble view. Setting 0 changes the - // bubble to the default size. max_height() will return the default size - // if SetMaxHeight() has not been called yet. - void SetMaxHeight(int height); - int max_height() const { return max_height_; } - - // Called after the bubble view has been constructed. Creates and initializes - // the bubble contents. - virtual void InitializeContents(views::TrayBubbleView* bubble_view) = 0; - - // Called from BubbleViewDestroyed for implementation specific details. - virtual void OnBubbleViewDestroyed() = 0; - - // Updates the bubble; implementation dependent. - virtual void UpdateBubbleView() = 0; - - // Called when the mouse enters/exists the view. - virtual void OnMouseEnteredView() = 0; - virtual void OnMouseExitedView() = 0; - - // Schedules bubble for layout after all notifications have been - // added and icons have had a chance to load. - void ScheduleUpdate(); - - bool IsVisible() const; - - views::TrayBubbleView* bubble_view() const { return bubble_view_; } - - protected: - MessageCenter* message_center() { return message_center_; } - MessageCenterTray* tray() { return tray_; } - void set_bubble_view(views::TrayBubbleView* bubble_view) { - bubble_view_ = bubble_view; - } - - private: - MessageCenter* message_center_; - MessageCenterTray* tray_; - views::TrayBubbleView* bubble_view_; - int max_height_; - base::WeakPtrFactory<MessageBubbleBase> weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(MessageBubbleBase); -}; - -} // namespace message_center - -#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_BUBBLE_BASE_H_ diff --git a/chromium/ui/message_center/views/message_center_bubble.cc b/chromium/ui/message_center/views/message_center_bubble.cc deleted file mode 100644 index ad76cef5ace..00000000000 --- a/chromium/ui/message_center/views/message_center_bubble.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/message_center/views/message_center_bubble.h" - -#include "base/macros.h" -#include "ui/message_center/message_center_style.h" -#include "ui/message_center/views/message_center_view.h" -#include "ui/views/layout/fill_layout.h" -#include "ui/views/widget/widget.h" - -namespace message_center { - -// ContentsView //////////////////////////////////////////////////////////////// - -// Handles changes in MessageCenterView sizes. -class ContentsView : public views::View { - public: - explicit ContentsView(MessageCenterBubble* bubble, views::View* contents); - ~ContentsView() override; - - // Overridden from views::View: - int GetHeightForWidth(int width) const override; - - protected: - // Overridden from views::View: - void ChildPreferredSizeChanged(View* child) override; - - private: - base::WeakPtr<MessageCenterBubble> bubble_; - - DISALLOW_COPY_AND_ASSIGN(ContentsView); -}; - -ContentsView::ContentsView(MessageCenterBubble* bubble, views::View* contents) - : bubble_(bubble->AsWeakPtr()) { - SetLayoutManager(new views::FillLayout()); - AddChildView(contents); -} - -ContentsView::~ContentsView() { -} - -int ContentsView::GetHeightForWidth(int width) const { - DCHECK_EQ(1, child_count()); - int contents_width = std::max(width - GetInsets().width(), 0); - int contents_height = child_at(0)->GetHeightForWidth(contents_width); - return contents_height + GetInsets().height(); -} - -void ContentsView::ChildPreferredSizeChanged(View* child) { - // TODO(dharcourt): Reduce the amount of updating this requires. - if (bubble_.get()) - bubble_->bubble_view()->UpdateBubble(); -} - -// MessageCenterBubble ///////////////////////////////////////////////////////// - -MessageCenterBubble::MessageCenterBubble(MessageCenter* message_center, - MessageCenterTray* tray) - : MessageBubbleBase(message_center, tray), - message_center_view_(NULL), - initially_settings_visible_(false) {} - -MessageCenterBubble::~MessageCenterBubble() { - // Removs this from the widget observers just in case. MessageCenterBubble - // might be destoryed without calling its Widget's Close/CloseNow. - if (bubble_view() && bubble_view()->GetWidget()) - bubble_view()->GetWidget()->RemoveObserver(this); -} - -void MessageCenterBubble::SetSettingsVisible() { - if (message_center_view_) - message_center_view_->SetSettingsVisible(true); - else - initially_settings_visible_ = true; -} - -void MessageCenterBubble::InitializeContents( - views::TrayBubbleView* new_bubble_view) { - set_bubble_view(new_bubble_view); - bubble_view()->GetWidget()->AddObserver(this); - message_center_view_ = new MessageCenterView( - message_center(), tray(), max_height(), initially_settings_visible_); - bubble_view()->AddChildView(new ContentsView(this, message_center_view_)); - message_center_view_->Init(); - // Resize the content of the bubble view to the given bubble size. This is - // necessary in case of the bubble border forcing a bigger size then the - // |new_bubble_view| actually wants. See crbug.com/169390. - bubble_view()->Layout(); - UpdateBubbleView(); -} - -void MessageCenterBubble::OnBubbleViewDestroyed() { - message_center_view_ = NULL; -} - -void MessageCenterBubble::UpdateBubbleView() { - if (!bubble_view()) - return; // Could get called after view is closed - const NotificationList::Notifications& notifications = - message_center()->GetVisibleNotifications(); - message_center_view_->SetNotifications(notifications); - bubble_view()->GetWidget()->Show(); - bubble_view()->UpdateBubble(); -} - -void MessageCenterBubble::OnWidgetClosing(views::Widget* widget) { - if (bubble_view() && bubble_view()->GetWidget()) - bubble_view()->GetWidget()->RemoveObserver(this); - if (message_center_view_) - message_center_view_->SetIsClosing(true); -} - -void MessageCenterBubble::OnMouseEnteredView() { -} - -void MessageCenterBubble::OnMouseExitedView() { -} - -size_t MessageCenterBubble::NumMessageViewsForTest() const { - return message_center_view_->NumMessageViewsForTest(); -} - -} // namespace message_center diff --git a/chromium/ui/message_center/views/message_center_bubble.h b/chromium/ui/message_center/views/message_center_bubble.h deleted file mode 100644 index 138c562ea03..00000000000 --- a/chromium/ui/message_center/views/message_center_bubble.h +++ /dev/null @@ -1,55 +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_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_BUBBLE_H_ -#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_BUBBLE_H_ - -#include <stddef.h> - -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "ui/message_center/message_center_export.h" -#include "ui/message_center/views/message_bubble_base.h" -#include "ui/views/widget/widget_observer.h" - -namespace message_center { - -class MessageCenterView; - -// Bubble for message center. -class MESSAGE_CENTER_EXPORT MessageCenterBubble - : public MessageBubbleBase, - public views::WidgetObserver, - public base::SupportsWeakPtr<MessageCenterBubble> { - public: - MessageCenterBubble(MessageCenter* message_center, MessageCenterTray* tray); - - ~MessageCenterBubble() override; - - void SetSettingsVisible(); - - // Overridden from views::WidgetObserver: - void OnWidgetClosing(views::Widget* widget) override; - - // Overridden from MessageBubbleBase: - void InitializeContents(views::TrayBubbleView* bubble_view) override; - void OnBubbleViewDestroyed() override; - void UpdateBubbleView() override; - void OnMouseEnteredView() override; - void OnMouseExitedView() override; - - size_t NumMessageViewsForTest() const; - - private: - MessageCenterView* message_center_view_; - - // Use settings view as the initially visible content if true. - bool initially_settings_visible_; - - DISALLOW_COPY_AND_ASSIGN(MessageCenterBubble); -}; - -} // namespace message_center - -#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_BUBBLE_H_ diff --git a/chromium/ui/message_center/views/message_center_button_bar.cc b/chromium/ui/message_center/views/message_center_button_bar.cc index f08fc1655ca..95500a59060 100644 --- a/chromium/ui/message_center/views/message_center_button_bar.cc +++ b/chromium/ui/message_center/views/message_center_button_bar.cc @@ -44,11 +44,11 @@ views::ToggleImageButton* CreateNotificationCenterButton( int text_id) { auto* button = new views::ToggleImageButton(listener); ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance(); - button->SetImage(views::CustomButton::STATE_NORMAL, + button->SetImage(views::Button::STATE_NORMAL, *resource_bundle.GetImageSkiaNamed(normal_id)); - button->SetImage(views::CustomButton::STATE_HOVERED, + button->SetImage(views::Button::STATE_HOVERED, *resource_bundle.GetImageSkiaNamed(hover_id)); - button->SetImage(views::CustomButton::STATE_PRESSED, + button->SetImage(views::Button::STATE_PRESSED, *resource_bundle.GetImageSkiaNamed(pressed_id)); button->SetImageAlignment(views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE); diff --git a/chromium/ui/message_center/views/message_center_view.cc b/chromium/ui/message_center/views/message_center_view.cc index ae14fb94d9c..256689a02ce 100644 --- a/chromium/ui/message_center/views/message_center_view.cc +++ b/chromium/ui/message_center/views/message_center_view.cc @@ -179,7 +179,6 @@ void MessageCenterView::ClearAllClosableNotifications() { } void MessageCenterView::OnAllNotificationsCleared() { - is_clearing_all_notifications_ = false; SetViewHierarchyEnabled(scroller_, true); button_bar_->SetCloseAllButtonEnabled(false); @@ -189,6 +188,7 @@ void MessageCenterView::OnAllNotificationsCleared() { message_center_->RemoveAllNotifications( true /* by_user */, message_center::MessageCenter::RemoveType::NON_PINNED); + is_clearing_all_notifications_ = false; } size_t MessageCenterView::NumMessageViewsForTest() const { @@ -358,7 +358,10 @@ void MessageCenterView::OnNotificationRemoved(const std::string& id, MessageView* view = view_iter->second; int index = message_list_view_->GetIndexOf(view); DCHECK_LE(0, index); - if (by_user) { + + // We skip repositioning during clear-all anomation, since we don't need keep + // positions. + if (by_user && !is_clearing_all_notifications_) { message_list_view_->SetRepositionTarget(view->bounds()); // Moves the keyboard focus to the next notification if the removed // notification is focused so that the user can dismiss notifications @@ -645,7 +648,7 @@ void MessageCenterView::EnableCloseAllIfAppropriate() { if (mode_ == Mode::NOTIFICATIONS) { bool no_closable_views = true; for (const auto& view : notification_views_) { - if (!view.second->pinned()) { + if (!view.second->GetPinned()) { no_closable_views = false; break; } @@ -668,29 +671,28 @@ void MessageCenterView::UpdateNotification(const std::string& id) { if (view_iter == notification_views_.end()) return; - // TODO(dimich): add MessageCenter::GetVisibleNotificationById(id) MessageView* view = view_iter->second; - const NotificationList::Notifications& notifications = - message_center_->GetVisibleNotifications(); - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); - iter != notifications.end(); ++iter) { - if ((*iter)->id() == id) { - int old_width = view->width(); - int old_height = view->height(); - bool old_pinned = view->pinned(); - message_list_view_->UpdateNotification(view, **iter); - if (view->GetHeightForWidth(old_width) != old_height) { - Update(true /* animate */); - } else if (view->pinned() != old_pinned) { - // Animate flag is false, since the pinned flag transition doesn't need - // animation. - Update(false /* animate */); - } - break; + Notification* notification = message_center_->FindVisibleNotificationById(id); + if (notification) { + int old_width = view->width(); + int old_height = view->height(); + bool old_pinned = view->GetPinned(); + message_list_view_->UpdateNotification(view, *notification); + if (view->GetHeightForWidth(old_width) != old_height) { + Update(true /* animate */); + } else if (view->GetPinned() != old_pinned) { + // Animate flag is false, since the pinned flag transition doesn't need + // animation. + Update(false /* animate */); } } + // Checks for investigation of the crash crbug.com/737858. It looks the view + // is stale, but we're not sure. These checks are to confirm that assumption. + // TODO(yoshiki): remove these after fixing the crash. + CHECK(notification_views_.find(id) != notification_views_.end()); + CHECK(message_list_view_->Contains(view)); + // Notify accessibility that the contents have changed. view->NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, false); } diff --git a/chromium/ui/message_center/views/message_center_view_unittest.cc b/chromium/ui/message_center/views/message_center_view_unittest.cc index 7a5a4f9b10b..b48b0003ce1 100644 --- a/chromium/ui/message_center/views/message_center_view_unittest.cc +++ b/chromium/ui/message_center/views/message_center_view_unittest.cc @@ -458,7 +458,10 @@ TEST_F(MessageCenterViewTest, Size) { GetMessageListView()->GetInsets().height()); } -TEST_F(MessageCenterViewTest, SizeAfterUpdate) { +// TODO(tetsui): The test is broken because there's no guarantee anymore that +// height would change after setting longer message, as NotificationViewMD +// implements collapse / expand functionality of long message. +TEST_F(MessageCenterViewTest, DISABLED_SizeAfterUpdate) { EXPECT_EQ(2, GetMessageListView()->child_count()); int width = GetMessageListView()->width() - GetMessageListView()->GetInsets().width(); diff --git a/chromium/ui/message_center/views/message_list_view.cc b/chromium/ui/message_center/views/message_list_view.cc index 6a1b78426e6..9e261afe14d 100644 --- a/chromium/ui/message_center/views/message_list_view.cc +++ b/chromium/ui/message_center/views/message_list_view.cc @@ -6,7 +6,7 @@ #include "base/command_line.h" #include "base/location.h" -#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" @@ -177,12 +177,12 @@ int MessageListView::GetHeightForWidth(int width) const { return height + GetInsets().height(); } -void MessageListView::PaintChildren(const ui::PaintContext& context) { +void MessageListView::PaintChildren(const views::PaintInfo& paint_info) { // Paint in the inversed order. Otherwise upper notification may be // hidden by the lower one. for (int i = child_count() - 1; i >= 0; --i) { if (!child_at(i)->layer()) - child_at(i)->Paint(context); + child_at(i)->Paint(paint_info); } } @@ -260,7 +260,7 @@ void MessageListView::ClearAllClosableNotifications( continue; if (gfx::IntersectRects(child->bounds(), visible_scroll_rect).IsEmpty()) continue; - if (child->pinned()) + if (child->GetPinned()) continue; if (deleting_views_.find(child) != deleting_views_.end() || deleted_when_done_.find(child) != deleted_when_done_.end()) { @@ -300,18 +300,28 @@ void MessageListView::OnBoundsAnimatorProgressed( } void MessageListView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { + // It's possible for the delayed task that queues the next animation for + // clearing all notifications to be delayed more than we want. In this case, + // the BoundsAnimator can finish while a clear all is still happening. So, + // explicitly check if |clearing_all_views_| is empty. + if (clear_all_started_ && !clearing_all_views_.empty()) { + return; + } + bool need_update = false; if (clear_all_started_) { clear_all_started_ = false; - // TODO(yoshiki): we shouldn't touch views in OnAllNotificationsCleared(). - // Or rename it to like OnAllNotificationsClearing(). for (auto& observer : observers_) observer.OnAllNotificationsCleared(); - // Need to update layout after deleting the views. - if (!deleted_when_done_.empty()) - need_update = true; + // Just return here if new animation is initiated in the above observers, + // since the code below assumes no animation is running. In the current + // impelementation, the observer tries removing the notification and their + // views and starts animation if the message center keeps opening. + // The code below will be executed when the new animation is finished. + if (animator_.IsAnimating()) + return; } // None of these views should be deleted. @@ -334,7 +344,7 @@ void MessageListView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { GetWidget()->SynthesizeMouseMoveEvent(); if (quit_message_loop_after_animation_for_test_) - base::MessageLoop::current()->QuitWhenIdle(); + base::RunLoop::QuitCurrentWhenIdleDeprecated(); } bool MessageListView::IsValidChild(const views::View* child) const { @@ -356,9 +366,17 @@ void MessageListView::DoUpdateIfPossible() { return; } - if (!clearing_all_views_.empty()) { - if (!clear_all_started_) - AnimateClearingOneNotification(); + // Start the clearing all animation if necessary. + if (!clearing_all_views_.empty() && !clear_all_started_) { + AnimateClearingOneNotification(); + return; + } + + // Skip during the clering all animation. + // This checks |clear_all_started_! rather than |clearing_all_views_|, since + // the latter is empty during the animation of the last element. + if (clear_all_started_) { + DCHECK(!clearing_all_views_.empty()); return; } @@ -539,6 +557,10 @@ bool MessageListView::AnimateChild(views::View* child, int top, int height, bool animate_on_move) { + // Do not call this during clearing all animation. + DCHECK(clearing_all_views_.empty()); + DCHECK(!clear_all_started_); + gfx::Rect child_area = GetContentsBounds(); if (adding_views_.find(child) != adding_views_.end()) { child->SetBounds(child_area.right(), top, child_area.width(), height); @@ -573,9 +595,6 @@ void MessageListView::AnimateClearingOneNotification() { new_bounds.set_x(new_bounds.right() + kMarginBetweenItems); animator_.AnimateViewTo(child, new_bounds); - // Deleting the child after animation. - deleted_when_done_.insert(child); - // Schedule to start sliding out next notification after a short delay. if (!clearing_all_views_.empty()) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( diff --git a/chromium/ui/message_center/views/message_list_view.h b/chromium/ui/message_center/views/message_list_view.h index b12f80f692a..a2232fa08ac 100644 --- a/chromium/ui/message_center/views/message_list_view.h +++ b/chromium/ui/message_center/views/message_list_view.h @@ -63,7 +63,7 @@ class MESSAGE_CENTER_EXPORT MessageListView void Layout() override; gfx::Size CalculatePreferredSize() const override; int GetHeightForWidth(int width) const override; - void PaintChildren(const ui::PaintContext& context) override; + void PaintChildren(const views::PaintInfo& paint_info) override; void ReorderChildLayers(ui::Layer* parent_layer) override; // Overridden from views::BoundsAnimatorObserver. diff --git a/chromium/ui/message_center/views/message_list_view_unittest.cc b/chromium/ui/message_center/views/message_list_view_unittest.cc index 2615a975785..ba561c1d529 100644 --- a/chromium/ui/message_center/views/message_list_view_unittest.cc +++ b/chromium/ui/message_center/views/message_list_view_unittest.cc @@ -156,12 +156,18 @@ class MessageListViewTest : public views::ViewsTestBase, RunPendingMessages(); } + bool is_on_all_notifications_cleared_called() const { + return is_on_all_notifications_cleared_called_; + } + private: // MockNotificationView::Test override void RegisterCall(CallType type) override {} // MessageListView::Observer override - void OnAllNotificationsCleared() override {} + void OnAllNotificationsCleared() override { + is_on_all_notifications_cleared_called_ = true; + } // MessageCenterController override: void ClickOnNotification(const std::string& notification_id) override {} @@ -186,6 +192,8 @@ class MessageListViewTest : public views::ViewsTestBase, // MessageListView to be tested. std::unique_ptr<MessageListView> message_list_view_; + bool is_on_all_notifications_cleared_called_ = false; + DISALLOW_COPY_AND_ASSIGN(MessageListViewTest); }; @@ -445,7 +453,7 @@ TEST_F(MessageListViewTest, ClearAllClosableNotifications) { RunPendingAnimations(); - EXPECT_EQ(0, message_list_view()->child_count()); + EXPECT_TRUE(is_on_all_notifications_cleared_called()); } // Regression test for crbug.com/713983 @@ -467,13 +475,9 @@ TEST_F(MessageListViewTest, RemoveWhileClearAll) { message_center::RichNotificationData(), nullptr)); message_list_view()->AddNotificationAt(notification_view1, 0); - EXPECT_EQ(1, message_list_view()->child_count()); - RunPendingAnimations(); message_list_view()->AddNotificationAt(notification_view2, 1); - EXPECT_EQ(2, message_list_view()->child_count()); - RunPendingAnimations(); // Call RemoveNotification() @@ -486,7 +490,7 @@ TEST_F(MessageListViewTest, RemoveWhileClearAll) { message_list_view()->bounds()); RunPendingAnimations(); - EXPECT_EQ(0, message_list_view()->child_count()); + EXPECT_TRUE(is_on_all_notifications_cleared_called()); } } // namespace diff --git a/chromium/ui/message_center/views/message_popup_collection.cc b/chromium/ui/message_center/views/message_popup_collection.cc index be17705b3d7..5695d1b76cd 100644 --- a/chromium/ui/message_center/views/message_popup_collection.cc +++ b/chromium/ui/message_center/views/message_popup_collection.cc @@ -195,20 +195,13 @@ void MessagePopupCollection::UpdateWidgets() { continue; #endif - MessageView* view; // Create top-level notification. + MessageView* view = MessageViewFactory::Create(NULL, *(*iter), true); #if defined(OS_CHROMEOS) - if ((*iter)->pinned()) { - Notification notification = *(*iter); - // Override pinned status, since toasts should be closable even when it's - // pinned. - notification.set_pinned(false); - view = MessageViewFactory::Create(NULL, notification, true); - } else + // Disable pinned feature since this is a popup. + view->set_force_disable_pinned(); #endif // defined(OS_CHROMEOS) - { - view = MessageViewFactory::Create(NULL, *(*iter), true); - } + view->SetExpanded(true); // TODO(yoshiki): Temporary disable context menu on custom notifications. // See crbug.com/750307 for detail. diff --git a/chromium/ui/message_center/views/message_view.cc b/chromium/ui/message_center/views/message_view.cc index 340d9bcce8d..be708d9ed29 100644 --- a/chromium/ui/message_center/views/message_view.cc +++ b/chromium/ui/message_center/views/message_view.cc @@ -91,7 +91,7 @@ void MessageView::UpdateWithNotification(const Notification& notification) { display_source_ = notification.display_source(); pinned_ = notification.pinned(); accessible_name_ = CreateAccessibleName(notification); - slide_out_controller_.set_enabled(!notification.pinned()); + slide_out_controller_.set_enabled(!GetPinned()); } // static @@ -113,6 +113,15 @@ void MessageView::SetIsNested() { -gfx::ShadowValue::GetMargin(shadow.values))); } +void MessageView::SetExpanded(bool expanded) { + // Not implemented by default. +} + +bool MessageView::IsExpanded() const { + // Not implemented by default. + return false; +} + void MessageView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ui::AX_ROLE_BUTTON; node_data->AddStringAttribute( @@ -148,7 +157,7 @@ bool MessageView::OnKeyPressed(const ui::KeyEvent& event) { bool MessageView::OnKeyReleased(const ui::KeyEvent& event) { // Space key handling is triggerred at key-release timing. See - // ui/views/controls/buttons/custom_button.cc for why. + // ui/views/controls/buttons/button.cc for why. if (event.flags() != ui::EF_NONE || event.key_code() != ui::VKEY_SPACE) return false; @@ -231,6 +240,10 @@ void MessageView::OnSlideOut() { controller_->RemoveNotification(notification_id_, true); // By user. } +bool MessageView::GetPinned() const { + return pinned_ && !force_disable_pinned_; +} + void MessageView::OnCloseButtonPressed() { controller_->RemoveNotification(notification_id_, true); // By user. } diff --git a/chromium/ui/message_center/views/message_view.h b/chromium/ui/message_center/views/message_view.h index d07d76a6760..e6aeacdc991 100644 --- a/chromium/ui/message_center/views/message_view.h +++ b/chromium/ui/message_center/views/message_view.h @@ -55,6 +55,9 @@ class MESSAGE_CENTER_EXPORT MessageView virtual void RequestFocusOnCloseButton() = 0; virtual void UpdateControlButtonsVisibility() = 0; + virtual void SetExpanded(bool expanded); + virtual bool IsExpanded() const; + void OnCloseButtonPressed(); void OnSettingsButtonPressed(); @@ -74,16 +77,21 @@ class MESSAGE_CENTER_EXPORT MessageView void OnSlideChanged() override; void OnSlideOut() override; + bool GetPinned() const; + void set_scroller(views::ScrollView* scroller) { scroller_ = scroller; } std::string notification_id() { return notification_id_; } NotifierId notifier_id() { return notifier_id_; } const base::string16& display_source() const { return display_source_; } - bool pinned() const { return pinned_; } - void set_controller(MessageCenterController* controller) { controller_ = controller; } +#if defined(OS_CHROMEOS) + // By calling this, all notifications are treated as non-pinned forcibly. + void set_force_disable_pinned() { force_disable_pinned_ = true; } +#endif + protected: // Creates and add close button to view hierarchy when necessary. Derived // classes should call this after its view hierarchy is populated to ensure @@ -108,7 +116,12 @@ class MESSAGE_CENTER_EXPORT MessageView base::string16 accessible_name_; base::string16 display_source_; + + // Flag if the notification is set to pinned or not. bool pinned_ = false; + // Flag if pin is forcibly disabled on this view. If true, the view is never + // pinned regardless of the value of |pinned_|. + bool force_disable_pinned_ = false; std::unique_ptr<views::Painter> focus_painter_; diff --git a/chromium/ui/message_center/views/message_view_factory.cc b/chromium/ui/message_center/views/message_view_factory.cc index 60567af4533..bbd2b49fbb7 100644 --- a/chromium/ui/message_center/views/message_view_factory.cc +++ b/chromium/ui/message_center/views/message_view_factory.cc @@ -5,7 +5,7 @@ #include "ui/message_center/views/message_view_factory.h" #include "base/command_line.h" -#include "ui/message_center/message_center_switches.h" +#include "ui/message_center/message_center.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/views/notification_view.h" #include "ui/message_center/views/notification_view_md.h" @@ -26,23 +26,13 @@ MessageView* MessageViewFactory::Create(MessageCenterController* controller, case NOTIFICATION_TYPE_IMAGE: case NOTIFICATION_TYPE_MULTIPLE: case NOTIFICATION_TYPE_SIMPLE: - case NOTIFICATION_TYPE_PROGRESS: { - bool new_style_notification_enabled = false; // default value - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableMessageCenterNewStyleNotification)) { - new_style_notification_enabled = true; - } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableMessageCenterNewStyleNotification)) { - new_style_notification_enabled = false; - } - + case NOTIFICATION_TYPE_PROGRESS: // All above roads lead to the generic NotificationView. - if (new_style_notification_enabled) + if (MessageCenter::IsNewStyleNotificationEnabled()) notification_view = new NotificationViewMD(controller, notification); else notification_view = new NotificationView(controller, notification); break; - } #if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX) case NOTIFICATION_TYPE_CUSTOM: notification_view = diff --git a/chromium/ui/message_center/views/notification_button.cc b/chromium/ui/message_center/views/notification_button.cc index fe0877a8041..8b7b9f7880f 100644 --- a/chromium/ui/message_center/views/notification_button.cc +++ b/chromium/ui/message_center/views/notification_button.cc @@ -18,7 +18,7 @@ namespace message_center { NotificationButton::NotificationButton(views::ButtonListener* listener) - : views::CustomButton(listener), icon_(NULL), title_(NULL) { + : views::Button(listener), icon_(NULL), title_(NULL) { SetFocusForPlatform(); // Create a background so that it does not change when the MessageView // background changes to show touch feedback @@ -81,7 +81,7 @@ int NotificationButton::GetHeightForWidth(int width) const { } void NotificationButton::OnFocus() { - views::CustomButton::OnFocus(); + views::Button::OnFocus(); ScrollRectToVisible(GetLocalBounds()); } diff --git a/chromium/ui/message_center/views/notification_button.h b/chromium/ui/message_center/views/notification_button.h index 3d3d2186b5b..21d54c43aca 100644 --- a/chromium/ui/message_center/views/notification_button.h +++ b/chromium/ui/message_center/views/notification_button.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "ui/gfx/image/image_skia.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "ui/views/painter.h" #include "ui/views/view.h" @@ -19,7 +19,7 @@ class Label; namespace message_center { // NotificationButtons render the action buttons of notifications. -class NotificationButton : public views::CustomButton { +class NotificationButton : public views::Button { public: NotificationButton(views::ButtonListener* listener); ~NotificationButton() override; @@ -34,7 +34,7 @@ class NotificationButton : public views::CustomButton { void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; - // Overridden from views::CustomButton: + // Overridden from views::Button: void StateChanged(ButtonState old_state) override; private: diff --git a/chromium/ui/message_center/views/notification_control_buttons_view.cc b/chromium/ui/message_center/views/notification_control_buttons_view.cc index bb10972f525..e3dbd3dc5e8 100644 --- a/chromium/ui/message_center/views/notification_control_buttons_view.cc +++ b/chromium/ui/message_center/views/notification_control_buttons_view.cc @@ -55,7 +55,7 @@ void NotificationControlButtonsView::ShowCloseButton(bool show) { if (show && !close_button_) { close_button_ = base::MakeUnique<message_center::PaddedButton>(this); close_button_->set_owned_by_client(); - close_button_->SetImage(views::CustomButton::STATE_NORMAL, + close_button_->SetImage(views::Button::STATE_NORMAL, message_center::GetCloseIcon()); close_button_->SetAccessibleName(l10n_util::GetStringUTF16( IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); @@ -77,7 +77,7 @@ void NotificationControlButtonsView::ShowSettingsButton(bool show) { if (show && !settings_button_) { settings_button_ = base::MakeUnique<message_center::PaddedButton>(this); settings_button_->set_owned_by_client(); - settings_button_->SetImage(views::CustomButton::STATE_NORMAL, + settings_button_->SetImage(views::Button::STATE_NORMAL, message_center::GetSettingsIcon()); settings_button_->SetAccessibleName(l10n_util::GetStringUTF16( IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME)); @@ -131,13 +131,11 @@ bool NotificationControlButtonsView::IsSettingsButtonFocused() const { return settings_button_ && settings_button_->HasFocus(); } -message_center::PaddedButton* NotificationControlButtonsView::close_button() - const { +views::Button* NotificationControlButtonsView::close_button() const { return close_button_.get(); } -message_center::PaddedButton* NotificationControlButtonsView::settings_button() - const { +views::Button* NotificationControlButtonsView::settings_button() const { return settings_button_.get(); } diff --git a/chromium/ui/message_center/views/notification_control_buttons_view.h b/chromium/ui/message_center/views/notification_control_buttons_view.h index 627f0bc9c66..31441a979dc 100644 --- a/chromium/ui/message_center/views/notification_control_buttons_view.h +++ b/chromium/ui/message_center/views/notification_control_buttons_view.h @@ -20,6 +20,10 @@ namespace gfx { class LinearAnimation; } +namespace views { +class Button; +} + namespace message_center { class MessageView; @@ -55,8 +59,8 @@ class MESSAGE_CENTER_EXPORT NotificationControlButtonsView bool IsSettingsButtonFocused() const; // Methods for retrieving the control buttons directly. - message_center::PaddedButton* close_button() const; - message_center::PaddedButton* settings_button() const; + views::Button* close_button() const; + views::Button* settings_button() const; // views::View const char* GetClassName() const override; diff --git a/chromium/ui/message_center/views/notification_header_view.cc b/chromium/ui/message_center/views/notification_header_view.cc index a43e6eaaf37..b5c7e9f23d9 100644 --- a/chromium/ui/message_center/views/notification_header_view.cc +++ b/chromium/ui/message_center/views/notification_header_view.cc @@ -8,17 +8,17 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/font_list.h" #include "ui/gfx/paint_vector_icon.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/vector_icons.h" -#include "ui/message_center/views/padded_button.h" +#include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/strings/grit/ui_strings.h" -#include "ui/views/animation/flood_fill_ink_drop_ripple.h" -#include "ui/views/animation/ink_drop_highlight.h" -#include "ui/views/animation/ink_drop_impl.h" +#include "ui/views/animation/ink_drop_stub.h" +#include "ui/views/border.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" @@ -29,22 +29,38 @@ namespace message_center { namespace { -constexpr int kHeaderHeight = 28; -constexpr int kAppIconSize = 12; -constexpr int kExpandIconSize = 12; -constexpr gfx::Insets kHeaderPadding(0, 12, 0, 2); +constexpr int kHeaderHeight = 32; constexpr int kHeaderHorizontalSpacing = 2; -constexpr int kAppInfoConatainerTopPadding = 12; + +// The padding outer the header and the control buttons. +constexpr gfx::Insets kHeaderOuterPadding(2, 2, 0, 2); + +// Paddings of the views of texts. +// Top: 9px = 11px (from the mock) - 2px (outer padding) +// Buttom: 6px from the mock +constexpr gfx::Insets kTextViewPadding(9, 0, 6, 0); + +// Paddings of the entire header. +// Left: 14px = 16px (from the mock) - 2px (outer padding) +// Right: 2px = minimum padding between the control buttons and the header +constexpr gfx::Insets kHeaderPadding(0, 14, 0, 2); + +// Paddings of the app icon (small image). +// Top: 8px = 10px (from the mock) - 2px (outer padding) +// Bottom: 4px from the mock +// Right: 4px = 6px (from the mock) - kHeaderHorizontalSpacing +constexpr gfx::Insets kAppIconPadding(8, 0, 4, 4); + +// Size of the expand icon. 8px = 32px - 15px - 9px (values from the mock). +constexpr int kExpandIconSize = 8; +// Paddings of the expand buttons. +// Top: 13px = 15px (from the mock) - 2px (outer padding) +// Bottom: 9px from the mock +constexpr gfx::Insets kExpandIconViewPadding(13, 0, 9, 0); + // Bullet character. The divider symbol between different parts of the header. constexpr wchar_t kNotificationHeaderDivider[] = L" \u2022 "; -// Base ink drop color of action buttons. -const SkColor kInkDropBaseColor = SkColorSetRGB(0x0, 0x0, 0x0); -// Ripple ink drop opacity of action buttons. -constexpr float kInkDropRippleVisibleOpacity = 0.08f; -// Highlight (hover) ink drop opacity of action buttons. -constexpr float kInkDropHighlightVisibleOpacity = 0.08f; - // base::TimeBase has similar constants, but some of them are missing. constexpr int64_t kMinuteInMillis = 60LL * 1000LL; constexpr int64_t kHourInMillis = 60LL * kMinuteInMillis; @@ -52,6 +68,9 @@ constexpr int64_t kDayInMillis = 24LL * kHourInMillis; // In Android, DateUtils.YEAR_IN_MILLIS is 364 days. constexpr int64_t kYearInMillis = 364LL * kDayInMillis; +// "Roboto-Regular, 12sp" is specified in the mock. +constexpr int kHeaderTextFontSize = 12; + // ExpandButtton forwards all mouse and key events to NotificationHeaderView, // but takes tab focus for accessibility purpose. class ExpandButton : public views::ImageView { @@ -59,17 +78,17 @@ class ExpandButton : public views::ImageView { ExpandButton(); ~ExpandButton() override; + // Overridden from views::ImageView: void OnPaint(gfx::Canvas* canvas) override; void OnFocus() override; void OnBlur() override; + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; private: std::unique_ptr<views::Painter> focus_painter_; }; ExpandButton::ExpandButton() { - SetImage(gfx::CreateVectorIcon(kNotificationExpandMoreIcon, kExpandIconSize, - gfx::kChromeIconGrey)); focus_painter_ = views::Painter::CreateSolidFocusPainter( kFocusBorderColor, gfx::Insets(1, 2, 2, 2)); SetFocusBehavior(FocusBehavior::ALWAYS); @@ -92,6 +111,11 @@ void ExpandButton::OnBlur() { SchedulePaint(); } +void ExpandButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { + node_data->role = ui::AX_ROLE_BUTTON; + node_data->SetName(views::ImageView::GetTooltipText()); +} + // Do relative time string formatting that is similar to // com.java.android.widget.DateTimeView.updateRelativeTime. // Chromium has its own base::TimeFormat::Simple(), but none of the formats @@ -121,44 +145,59 @@ base::string16 FormatToRelativeTime(base::Time past) { } } +gfx::FontList GetHeaderTextFontList() { + gfx::Font default_font; + int font_size_delta = kHeaderTextFontSize - default_font.GetFontSize(); + gfx::Font font = default_font.Derive(font_size_delta, gfx::Font::NORMAL, + gfx::Font::Weight::NORMAL); + DCHECK_EQ(kHeaderTextFontSize, font.GetFontSize()); + return gfx::FontList(font); +} + } // namespace -NotificationHeaderView::NotificationHeaderView(views::ButtonListener* listener) - : views::CustomButton(listener) { - SetInkDropMode(InkDropMode::ON); - set_has_ink_drop_action_on_click(true); - set_animate_on_state_change(true); - set_notify_enter_exit_on_child(true); - set_ink_drop_base_color(kInkDropBaseColor); - set_ink_drop_visible_opacity(kInkDropRippleVisibleOpacity); +NotificationHeaderView::NotificationHeaderView( + NotificationControlButtonsView* control_buttons_view, + views::ButtonListener* listener) + : views::Button(listener) { + const int kInnerHeaderHeight = kHeaderHeight - kHeaderOuterPadding.height(); - views::BoxLayout* layout = new views::BoxLayout( - views::BoxLayout::kHorizontal, kHeaderPadding, kHeaderHorizontalSpacing); + views::BoxLayout* layout = + new views::BoxLayout(views::BoxLayout::kHorizontal, kHeaderOuterPadding, + kHeaderHorizontalSpacing); layout->set_cross_axis_alignment( - views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); + views::BoxLayout::CROSS_AXIS_ALIGNMENT_START); SetLayoutManager(layout); views::View* app_info_container = new views::View(); - views::BoxLayout* app_info_layout = - new views::BoxLayout(views::BoxLayout::kHorizontal, - gfx::Insets(kAppInfoConatainerTopPadding, 0, 0, 0), - kHeaderHorizontalSpacing); + views::BoxLayout* app_info_layout = new views::BoxLayout( + views::BoxLayout::kHorizontal, kHeaderPadding, kHeaderHorizontalSpacing); app_info_layout->set_cross_axis_alignment( - views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); + views::BoxLayout::CROSS_AXIS_ALIGNMENT_START); app_info_container->SetLayoutManager(app_info_layout); AddChildView(app_info_container); // App icon view app_icon_view_ = new views::ImageView(); - app_icon_view_->SetImageSize(gfx::Size(kAppIconSize, kAppIconSize)); + app_icon_view_->SetImageSize(gfx::Size(kSmallImageSizeMD, kSmallImageSizeMD)); + app_icon_view_->SetBorder(views::CreateEmptyBorder(kAppIconPadding)); + app_icon_view_->SetVerticalAlignment(views::ImageView::LEADING); + app_icon_view_->SetHorizontalAlignment(views::ImageView::LEADING); + DCHECK_EQ(kInnerHeaderHeight, app_icon_view_->GetPreferredSize().height()); app_info_container->AddChildView(app_icon_view_); + // Font list for text views. + const gfx::FontList& font_list = GetHeaderTextFontList(); + // The height must be 15px to match with the mock. + DCHECK_EQ(15, font_list.GetHeight()); + // App name view - const gfx::FontList& font_list = views::Label().font_list().Derive( - -2, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); app_name_view_ = new views::Label(base::string16()); app_name_view_->SetFontList(font_list); app_name_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + app_name_view_->SetEnabledColor(accent_color_); + app_name_view_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); + DCHECK_EQ(kInnerHeaderHeight, app_name_view_->GetPreferredSize().height()); app_info_container->AddChildView(app_name_view_); // Summary text divider @@ -166,14 +205,20 @@ NotificationHeaderView::NotificationHeaderView(views::ButtonListener* listener) new views::Label(base::WideToUTF16(kNotificationHeaderDivider)); summary_text_divider_->SetFontList(font_list); summary_text_divider_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + summary_text_divider_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); summary_text_divider_->SetVisible(false); + DCHECK_EQ(kInnerHeaderHeight, + summary_text_divider_->GetPreferredSize().height()); app_info_container->AddChildView(summary_text_divider_); // Summary text view summary_text_view_ = new views::Label(base::string16()); summary_text_view_->SetFontList(font_list); summary_text_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + summary_text_view_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); summary_text_view_->SetVisible(false); + DCHECK_EQ(kInnerHeaderHeight, + summary_text_view_->GetPreferredSize().height()); app_info_container->AddChildView(summary_text_view_); // Timestamp divider @@ -181,49 +226,51 @@ NotificationHeaderView::NotificationHeaderView(views::ButtonListener* listener) new views::Label(base::WideToUTF16(kNotificationHeaderDivider)); timestamp_divider_->SetFontList(font_list); timestamp_divider_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + timestamp_divider_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); timestamp_divider_->SetVisible(false); + DCHECK_EQ(kInnerHeaderHeight, + timestamp_divider_->GetPreferredSize().height()); app_info_container->AddChildView(timestamp_divider_); // Timestamp view timestamp_view_ = new views::Label(base::string16()); timestamp_view_->SetFontList(font_list); timestamp_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + timestamp_view_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); timestamp_view_->SetVisible(false); + DCHECK_EQ(kInnerHeaderHeight, timestamp_view_->GetPreferredSize().height()); app_info_container->AddChildView(timestamp_view_); // Expand button view expand_button_ = new ExpandButton(); + SetExpanded(is_expanded_); + expand_button_->SetBorder(views::CreateEmptyBorder(kExpandIconViewPadding)); + expand_button_->SetVerticalAlignment(views::ImageView::LEADING); + expand_button_->SetHorizontalAlignment(views::ImageView::LEADING); + expand_button_->SetImageSize(gfx::Size(kExpandIconSize, kExpandIconSize)); + DCHECK_EQ(kInnerHeaderHeight, expand_button_->GetPreferredSize().height()); app_info_container->AddChildView(expand_button_); // Spacer between left-aligned views and right-aligned views views::View* spacer = new views::View; - spacer->SetPreferredSize(gfx::Size(1, kHeaderHeight)); + spacer->SetPreferredSize(gfx::Size(1, kInnerHeaderHeight)); AddChildView(spacer); layout->SetFlexForView(spacer, 1); - // Settings button view - settings_button_ = new PaddedButton(listener); - settings_button_->SetImage(views::Button::STATE_NORMAL, GetSettingsIcon()); - settings_button_->SetAccessibleName(l10n_util::GetStringUTF16( - IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME)); - settings_button_->SetTooltipText(l10n_util::GetStringUTF16( - IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME)); - AddChildView(settings_button_); - - // Close button view - close_button_ = new PaddedButton(listener); - close_button_->SetImage(views::Button::STATE_NORMAL, GetCloseIcon()); - close_button_->SetAccessibleName(l10n_util::GetStringUTF16( - IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); - close_button_->SetTooltipText(l10n_util::GetStringUTF16( - IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP)); - AddChildView(close_button_); + // Settings and close buttons view + AddChildView(control_buttons_view); + + SetPreferredSize(gfx::Size(kNotificationWidth, kHeaderHeight)); } void NotificationHeaderView::SetAppIcon(const gfx::ImageSkia& img) { app_icon_view_->SetImage(img); } +void NotificationHeaderView::ClearAppIcon() { + app_icon_view_->SetImage(gfx::CreateVectorIcon(kProductIcon, accent_color_)); +} + void NotificationHeaderView::SetAppName(const base::string16& name) { app_name_view_->SetText(name); } @@ -277,75 +324,27 @@ void NotificationHeaderView::SetExpandButtonEnabled(bool enabled) { } void NotificationHeaderView::SetExpanded(bool expanded) { - expand_button_->SetImage( - gfx::CreateVectorIcon( - expanded ? kNotificationExpandLessIcon : kNotificationExpandMoreIcon, - kExpandIconSize, gfx::kChromeIconGrey)); -} - -void NotificationHeaderView::SetSettingsButtonEnabled(bool enabled) { - if (settings_button_enabled_ != enabled) { - settings_button_enabled_ = enabled; - UpdateControlButtonsVisibility(); - } -} - -void NotificationHeaderView::SetCloseButtonEnabled(bool enabled) { - if (close_button_enabled_ != enabled) { - close_button_enabled_ = enabled; - UpdateControlButtonsVisibility(); - } + is_expanded_ = expanded; + expand_button_->SetImage(gfx::CreateVectorIcon( + expanded ? kNotificationExpandLessIcon : kNotificationExpandMoreIcon, + kExpandIconSize, accent_color_)); + expand_button_->SetTooltipText(l10n_util::GetStringUTF16( + expanded ? IDS_MESSAGE_CENTER_COLLAPSE_NOTIFICATION + : IDS_MESSAGE_CENTER_EXPAND_NOTIFICATION)); } -void NotificationHeaderView::SetControlButtonsVisible(bool visible) { - if (is_control_buttons_visible_ != visible) { - is_control_buttons_visible_ = visible; - UpdateControlButtonsVisibility(); - } +void NotificationHeaderView::SetAccentColor(SkColor color) { + accent_color_ = color; + app_name_view_->SetEnabledColor(accent_color_); + SetExpanded(is_expanded_); } bool NotificationHeaderView::IsExpandButtonEnabled() { return expand_button_->visible(); } -bool NotificationHeaderView::IsSettingsButtonEnabled() { - return settings_button_enabled_; -} - -bool NotificationHeaderView::IsCloseButtonEnabled() { - return close_button_enabled_; -} - std::unique_ptr<views::InkDrop> NotificationHeaderView::CreateInkDrop() { - auto ink_drop = base::MakeUnique<views::InkDropImpl>(this, size()); - ink_drop->SetAutoHighlightMode( - views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE); - ink_drop->SetShowHighlightOnHover(false); - return ink_drop; -} - -std::unique_ptr<views::InkDropRipple> -NotificationHeaderView::CreateInkDropRipple() const { - return base::MakeUnique<views::FloodFillInkDropRipple>( - size(), GetInkDropCenterBasedOnLastEvent(), GetInkDropBaseColor(), - ink_drop_visible_opacity()); -} - -std::unique_ptr<views::InkDropHighlight> -NotificationHeaderView::CreateInkDropHighlight() const { - auto highlight = base::MakeUnique<views::InkDropHighlight>( - size(), kInkDropSmallCornerRadius, - gfx::RectF(GetLocalBounds()).CenterPoint(), GetInkDropBaseColor()); - highlight->set_visible_opacity(kInkDropHighlightVisibleOpacity); - return highlight; -} - -void NotificationHeaderView::UpdateControlButtonsVisibility() { - settings_button_->SetVisible(settings_button_enabled_ && - is_control_buttons_visible_); - close_button_->SetVisible(close_button_enabled_ && - is_control_buttons_visible_); - Layout(); + return base::MakeUnique<views::InkDropStub>(); } void NotificationHeaderView::UpdateSummaryTextVisibility() { diff --git a/chromium/ui/message_center/views/notification_header_view.h b/chromium/ui/message_center/views/notification_header_view.h index 2471c67a405..403475ec6a3 100644 --- a/chromium/ui/message_center/views/notification_header_view.h +++ b/chromium/ui/message_center/views/notification_header_view.h @@ -6,20 +6,22 @@ #define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_HEADER_VIEW_H_ #include "base/macros.h" -#include "ui/message_center/views/padded_button.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/message_center/message_center_style.h" +#include "ui/views/controls/button/button.h" namespace views { -class ImageButton; class ImageView; class Label; } namespace message_center { -class NotificationHeaderView : public views::CustomButton { +class NotificationControlButtonsView; + +class NotificationHeaderView : public views::Button { public: - NotificationHeaderView(views::ButtonListener* listener); + NotificationHeaderView(NotificationControlButtonsView* control_buttons_view, + views::ButtonListener* listener); void SetAppIcon(const gfx::ImageSkia& img); void SetAppName(const base::string16& name); void SetProgress(int progress); @@ -30,29 +32,28 @@ class NotificationHeaderView : public views::CustomButton { void SetSettingsButtonEnabled(bool enabled); void SetCloseButtonEnabled(bool enabled); void SetControlButtonsVisible(bool visible); + // Set the unified theme color used among the app icon, app name, and expand + // button. + void SetAccentColor(SkColor color); + void ClearAppIcon(); void ClearProgress(); void ClearOverflowIndicator(); void ClearTimestamp(); bool IsExpandButtonEnabled(); - bool IsSettingsButtonEnabled(); - bool IsCloseButtonEnabled(); - bool IsCloseButtonFocused(); - // CustomButton override: + // Button override: std::unique_ptr<views::InkDrop> CreateInkDrop() override; - std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override; - std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() - const override; views::ImageView* expand_button() { return expand_button_; } - views::ImageButton* settings_button() { return settings_button_; } - views::ImageButton* close_button() { return close_button_; } + + SkColor accent_color_for_testing() { return accent_color_; } private: - void UpdateControlButtonsVisibility(); // Update visibility for both |summary_text_view_| and |timestamp_view_|. void UpdateSummaryTextVisibility(); + SkColor accent_color_ = message_center::kNotificationDefaultAccentColor; + views::Label* app_name_view_ = nullptr; views::Label* summary_text_divider_ = nullptr; views::Label* summary_text_view_ = nullptr; @@ -60,15 +61,12 @@ class NotificationHeaderView : public views::CustomButton { views::Label* timestamp_view_ = nullptr; views::ImageView* app_icon_view_ = nullptr; views::ImageView* expand_button_ = nullptr; - PaddedButton* settings_button_ = nullptr; - PaddedButton* close_button_ = nullptr; bool settings_button_enabled_ = false; - bool close_button_enabled_ = false; - bool is_control_buttons_visible_ = false; bool has_progress_ = false; bool has_overflow_indicator_ = false; bool has_timestamp_ = false; + bool is_expanded_ = false; DISALLOW_COPY_AND_ASSIGN(NotificationHeaderView); }; diff --git a/chromium/ui/message_center/views/notification_view.cc b/chromium/ui/message_center/views/notification_view.cc index 0a1ad48595f..d536729d02f 100644 --- a/chromium/ui/message_center/views/notification_view.cc +++ b/chromium/ui/message_center/views/notification_view.cc @@ -289,6 +289,7 @@ void NotificationView::Layout() { // Top views. int top_height = top_view_->GetHeightForWidth(content_width); top_view_->SetBounds(insets.left(), insets.top(), content_width, top_height); + ShrinkTopmostLabel(); // Icon. icon_view_->SetBounds(insets.left(), insets.top(), kNotificationIconSize, @@ -340,10 +341,6 @@ gfx::NativeCursor NotificationView::GetCursor(const ui::MouseEvent& event) { return views::GetNativeHandCursor(); } -void NotificationView::OnMouseMoved(const ui::MouseEvent& event) { - MessageView::OnMouseMoved(event); - UpdateControlButtonsVisibility(); -} void NotificationView::OnMouseEntered(const ui::MouseEvent& event) { MessageView::OnMouseEntered(event); @@ -655,7 +652,7 @@ void NotificationView::UpdateControlButtonsVisibilityWithNotification( control_buttons_view_->ShowSettingsButton( notification.delegate() && notification.delegate()->ShouldDisplaySettingsButton()); - control_buttons_view_->ShowCloseButton(!notification.pinned()); + control_buttons_view_->ShowCloseButton(!GetPinned()); UpdateControlButtonsVisibility(); } @@ -722,4 +719,18 @@ int NotificationView::GetMessageHeight(int width, int limit) const { message_view_->GetSizeForWidthAndLines(width, limit).height() : 0; } +void NotificationView::ShrinkTopmostLabel() { +// Reduce width of the topmost label not to be covered by the control buttons +// only on non Chrome OS platform. +#if !defined(OS_CHROMEOS) + const int content_width = width() - GetInsets().width(); + const int buttons_width = control_buttons_view_->GetPreferredSize().width(); + if (top_view_->child_count() > 0) { + gfx::Rect bounds = top_view_->child_at(0)->bounds(); + bounds.set_width(content_width - buttons_width); + top_view_->child_at(0)->SetBoundsRect(bounds); + } +#endif +} + } // namespace message_center diff --git a/chromium/ui/message_center/views/notification_view.h b/chromium/ui/message_center/views/notification_view.h index 23e18509b0e..972a21ff9f4 100644 --- a/chromium/ui/message_center/views/notification_view.h +++ b/chromium/ui/message_center/views/notification_view.h @@ -47,7 +47,6 @@ class MESSAGE_CENTER_EXPORT NotificationView void OnFocus() override; void ScrollRectToVisible(const gfx::Rect& rect) override; gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; - void OnMouseMoved(const ui::MouseEvent& event) override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; @@ -87,6 +86,7 @@ class MESSAGE_CENTER_EXPORT NotificationView void CreateOrUpdateSmallIconView(const Notification& notification); void CreateOrUpdateImageView(const Notification& notification); void CreateOrUpdateActionButtonViews(const Notification& notification); + // TODO(yoshiki): Move this to message_center::MessageView void UpdateControlButtonsVisibilityWithNotification( const Notification& notification); @@ -99,6 +99,9 @@ class MESSAGE_CENTER_EXPORT NotificationView // notification. base::string16 FormatContextMessage(const Notification& notification) const; + // 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_; diff --git a/chromium/ui/message_center/views/notification_view_md.cc b/chromium/ui/message_center/views/notification_view_md.cc index 39555c6e1a5..3c6a123c721 100644 --- a/chromium/ui/message_center/views/notification_view_md.cc +++ b/chromium/ui/message_center/views/notification_view_md.cc @@ -24,6 +24,7 @@ #include "ui/message_center/views/bounded_label.h" #include "ui/message_center/views/constants.h" #include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/message_center/views/notification_header_view.h" #include "ui/message_center/views/padded_button.h" #include "ui/message_center/views/proportional_image_view.h" @@ -31,7 +32,6 @@ #include "ui/views/animation/ink_drop_highlight.h" #include "ui/views/background.h" #include "ui/views/border.h" -#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/controls/progress_bar.h" @@ -47,20 +47,21 @@ namespace message_center { namespace { // Dimensions. -constexpr gfx::Insets kContentRowPadding(4, 12, 12, 12); +constexpr gfx::Insets kContentRowPadding(2, 12, 12, 12); constexpr gfx::Insets kActionsRowPadding(8, 8, 8, 8); constexpr int kActionsRowHorizontalSpacing = 8; constexpr gfx::Insets kActionButtonPadding(0, 12, 0, 12); -constexpr gfx::Size kActionButtonMinSize(88, 32); -constexpr gfx::Size kIconViewSize(30, 30); +constexpr gfx::Insets kStatusTextPadding(4, 0, 0, 0); +constexpr gfx::Size kActionButtonMinSize(0, 32); +// TODO(tetsui): Move |kIconViewSize| to message_center_style.h and merge with +// contradicting |kNotificationIconSize|. +constexpr gfx::Size kIconViewSize(36, 36); constexpr gfx::Insets kLargeImageContainerPadding(0, 12, 12, 12); constexpr gfx::Size kLargeImageMinSize(328, 0); constexpr gfx::Size kLargeImageMaxSize(328, 218); +constexpr gfx::Insets kLeftContentPadding(0, 4, 0, 4); +constexpr gfx::Insets kLeftContentPaddingWithIcon(0, 4, 0, 12); -// Foreground of small icon image. -constexpr SkColor kSmallImageBackgroundColor = SK_ColorWHITE; -// Background of small icon image. -const SkColor kSmallImageColor = SkColorSetRGB(0x43, 0x43, 0x43); // Background of inline actions area. const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee); // Base ink drop color of action buttons. @@ -74,6 +75,9 @@ const SkColor kActionButtonTextColor = SkColorSetRGB(0x33, 0x67, 0xD6); // Background color of the large image. const SkColor kLargeImageBackgroundColor = SkColorSetRGB(0xf5, 0xf5, 0xf5); +const SkColor kRegularTextColorMD = SkColorSetRGB(0x21, 0x21, 0x21); +const SkColor kDimTextColorMD = SkColorSetRGB(0x75, 0x75, 0x75); + // Max number of lines for message_view_. constexpr int kMaxLinesForMessageView = 1; constexpr int kMaxLinesForExpandedMessageView = 4; @@ -82,40 +86,66 @@ constexpr int kCompactTitleMessageViewSpacing = 12; constexpr int kProgressBarHeight = 4; -constexpr int kMessageViewWidth = +constexpr int kMessageViewWidthWithIcon = message_center::kNotificationWidth - kIconViewSize.width() - + kLeftContentPaddingWithIcon.left() - kLeftContentPaddingWithIcon.right() - kContentRowPadding.left() - kContentRowPadding.right(); -const gfx::ImageSkia CreateSolidColorImage(int width, - int height, - SkColor color) { - SkBitmap bitmap; - bitmap.allocN32Pixels(width, height); - bitmap.eraseColor(color); - return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); -} - -// Take the alpha channel of icon, mask it with the foreground, -// then add the masked foreground on top of the background -const gfx::ImageSkia GetMaskedIcon(const gfx::ImageSkia& icon) { - int width = icon.width(); - int height = icon.height(); - - // Background color grey - const gfx::ImageSkia background = CreateSolidColorImage( - width, height, message_center::kSmallImageBackgroundColor); - // Foreground color white - const gfx::ImageSkia foreground = - CreateSolidColorImage(width, height, message_center::kSmallImageColor); - const gfx::ImageSkia masked_small_image = - gfx::ImageSkiaOperations::CreateMaskedImage(foreground, icon); - return gfx::ImageSkiaOperations::CreateSuperimposedImage(background, - masked_small_image); -} - -const gfx::ImageSkia GetProductIcon() { - return gfx::CreateVectorIcon(kProductIcon, kSmallImageColor); -} +constexpr int kMessageViewWidth = + message_center::kNotificationWidth - kLeftContentPadding.left() - + kLeftContentPadding.right() - kContentRowPadding.left() - + kContentRowPadding.right(); + +// "Roboto-Regular, 13sp" is specified in the mock. +constexpr int kTextFontSize = 13; + +// FontList for the texts except for the header. +gfx::FontList GetTextFontList() { + gfx::Font default_font; + int font_size_delta = kTextFontSize - default_font.GetFontSize(); + gfx::Font font = default_font.Derive(font_size_delta, gfx::Font::NORMAL, + gfx::Font::Weight::NORMAL); + DCHECK_EQ(kTextFontSize, font.GetFontSize()); + return gfx::FontList(font); +} + +#if defined(OS_CHROMEOS) +// Return true if the default |display_source| should be used for the +// notifications from the notifier. +// +// Ideally, we shuold fix the callers to set appropriate |display_source|, but +// string resource is frozen in M62. For now, we should use this to prevent +// empty |display_source| in system notifications. +// TODO(tetsui): Remove this hack after M62 is released. +bool ShowDefaultDisplaySource(const NotifierId& notifier) { + // The string constants are written in plain to prevent circular dependencies. + // This should not be accepted usually but I think it's OK here, as this + // function has a clear plan to be removed soon. + return notifier.type == NotifierId::SYSTEM_COMPONENT && + ( + // ARC notification. + // chrome/browser/chromeos/arc/arc_auth_notification.cc + notifier.id == "arc_auth" || + // chrome/browser/chromeos/arc/notification/ + // arc_boot_error_notification.cc + notifier.id == "arc_boot_error" || + // chrome/browser/chromeos/arc/notification/ + // arc_provision_notification_service.cc + notifier.id == "arc_managed_provision" || + // Happiness survey notification. + // chrome/browser/chromeos/hats/hats_notification_controller.cc + notifier.id == "ash.hats" || + // Sign-in error notification. + // chrome/browser/signin/signin_error_notifier_ash.cc + // chrome/browser/chromeos/authpolicy/ + // auth_policy_credentials_manager.cc + notifier.id == "chrome://settings/signin/" || + // CUPS printing notification. + // chrome/browser/chromeos/printing/cups_print_job_notification.cc + notifier.id == + "chrome://settings/printing/cups-print-job-notification"); +} +#endif // ItemView //////////////////////////////////////////////////////////////////// @@ -136,18 +166,22 @@ ItemView::ItemView(const message_center::NotificationItem& item) { SetLayoutManager( new views::BoxLayout(views::BoxLayout::kHorizontal, gfx::Insets(), 0)); + const gfx::FontList font_list = GetTextFontList(); + views::Label* title = new views::Label(item.title); + title->SetFontList(font_list); title->set_collapse_when_hidden(true); title->SetHorizontalAlignment(gfx::ALIGN_LEFT); - title->SetEnabledColor(message_center::kRegularTextColor); + title->SetEnabledColor(message_center::kRegularTextColorMD); title->SetBackgroundColor(message_center::kDimTextBackgroundColor); AddChildView(title); views::Label* message = new views::Label(l10n_util::GetStringFUTF16( IDS_MESSAGE_CENTER_LIST_NOTIFICATION_MESSAGE_WITH_DIVIDER, item.message)); + message->SetFontList(font_list); message->set_collapse_when_hidden(true); message->SetHorizontalAlignment(gfx::ALIGN_LEFT); - message->SetEnabledColor(message_center::kDimTextColor); + message->SetEnabledColor(kDimTextColorMD); message->SetBackgroundColor(message_center::kDimTextBackgroundColor); AddChildView(message); } @@ -193,19 +227,18 @@ const char* CompactTitleMessageView::GetClassName() const { CompactTitleMessageView::CompactTitleMessageView() { SetLayoutManager(new views::FillLayout()); - const gfx::FontList& font_list = views::Label().font_list().Derive( - 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); + const gfx::FontList& font_list = GetTextFontList(); title_view_ = new views::Label(); title_view_->SetFontList(font_list); title_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - title_view_->SetEnabledColor(message_center::kRegularTextColor); + title_view_->SetEnabledColor(kRegularTextColorMD); AddChildView(title_view_); message_view_ = new views::Label(); message_view_->SetFontList(font_list); message_view_->SetHorizontalAlignment(gfx::ALIGN_RIGHT); - message_view_->SetEnabledColor(message_center::kDimTextColor); + message_view_->SetEnabledColor(kDimTextColorMD); AddChildView(message_view_); } @@ -213,8 +246,7 @@ void CompactTitleMessageView::OnPaint(gfx::Canvas* canvas) { base::string16 title = title_; base::string16 message = message_; - const gfx::FontList& font_list = views::Label().font_list().Derive( - 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); + const gfx::FontList& font_list = GetTextFontList(); // Elides title and message. The behavior is based on Android's one. // * If the title is too long, only the title is shown. @@ -239,61 +271,6 @@ void CompactTitleMessageView::OnPaint(gfx::Canvas* canvas) { views::View::OnPaint(canvas); } -// NotificationButtonMD //////////////////////////////////////////////////////// - -// This class is needed in addition to LabelButton mainly becuase we want to set -// visible_opacity of InkDropHighlight. -// This button capitalizes the given label string. -class NotificationButtonMD : public views::LabelButton { - public: - NotificationButtonMD(views::ButtonListener* listener, - const base::string16& text); - ~NotificationButtonMD() override; - - void SetText(const base::string16& text) override; - const char* GetClassName() const override; - - std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() - const override; - - private: - DISALLOW_COPY_AND_ASSIGN(NotificationButtonMD); -}; - -NotificationButtonMD::NotificationButtonMD(views::ButtonListener* listener, - const base::string16& text) - : views::LabelButton(listener, - base::i18n::ToUpper(text), - views::style::CONTEXT_BUTTON_MD) { - SetHorizontalAlignment(gfx::ALIGN_CENTER); - SetInkDropMode(views::LabelButton::InkDropMode::ON); - set_has_ink_drop_action_on_click(true); - set_ink_drop_base_color(kActionButtonInkDropBaseColor); - set_ink_drop_visible_opacity(kActionButtonInkDropRippleVisibleOpacity); - SetEnabledTextColors(kActionButtonTextColor); - SetBorder(views::CreateEmptyBorder(kActionButtonPadding)); - SetMinSize(kActionButtonMinSize); - SetFocusForPlatform(); -} - -NotificationButtonMD::~NotificationButtonMD() = default; - -void NotificationButtonMD::SetText(const base::string16& text) { - views::LabelButton::SetText(base::i18n::ToUpper(text)); -} - -const char* NotificationButtonMD::GetClassName() const { - return "NotificationButtonMD"; -} - -std::unique_ptr<views::InkDropHighlight> -NotificationButtonMD::CreateInkDropHighlight() const { - std::unique_ptr<views::InkDropHighlight> highlight = - views::LabelButton::CreateInkDropHighlight(); - highlight->set_visible_opacity(kActionButtonInkDropHighlightVisibleOpacity); - return highlight; -} - // LargeImageView ////////////////////////////////////////////////////////////// class LargeImageView : public views::View { @@ -409,6 +386,42 @@ const char* LargeImageContainerView::GetClassName() const { } // anonymous namespace +// NotificationButtonMD //////////////////////////////////////////////////////// + +NotificationButtonMD::NotificationButtonMD(views::ButtonListener* listener, + const base::string16& text) + : views::LabelButton(listener, + base::i18n::ToUpper(text), + views::style::CONTEXT_BUTTON_MD) { + SetHorizontalAlignment(gfx::ALIGN_CENTER); + SetInkDropMode(views::LabelButton::InkDropMode::ON); + set_has_ink_drop_action_on_click(true); + set_ink_drop_base_color(kActionButtonInkDropBaseColor); + set_ink_drop_visible_opacity(kActionButtonInkDropRippleVisibleOpacity); + SetEnabledTextColors(kActionButtonTextColor); + SetBorder(views::CreateEmptyBorder(kActionButtonPadding)); + SetMinSize(kActionButtonMinSize); + SetFocusForPlatform(); +} + +NotificationButtonMD::~NotificationButtonMD() = default; + +void NotificationButtonMD::SetText(const base::string16& text) { + views::LabelButton::SetText(base::i18n::ToUpper(text)); +} + +const char* NotificationButtonMD::GetClassName() const { + return "NotificationButtonMD"; +} + +std::unique_ptr<views::InkDropHighlight> +NotificationButtonMD::CreateInkDropHighlight() const { + std::unique_ptr<views::InkDropHighlight> highlight = + views::LabelButton::CreateInkDropHighlight(); + highlight->set_visible_opacity(kActionButtonInkDropHighlightVisibleOpacity); + return highlight; +} + // //////////////////////////////////////////////////////////// // NotificationViewMD // //////////////////////////////////////////////////////////// @@ -426,10 +439,6 @@ views::View* NotificationViewMD::TargetForRect(views::View* root, // called. But buttons are exceptions, they'll have their own event handlings. std::vector<views::View*> buttons(action_buttons_.begin(), action_buttons_.end()); - if (header_row_->settings_button()) - buttons.push_back(header_row_->settings_button()); - if (header_row_->close_button()) - buttons.push_back(header_row_->close_button()); if (header_row_->expand_button()) buttons.push_back(header_row_->expand_button()); buttons.push_back(header_row_); @@ -450,12 +459,11 @@ void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) { CreateOrUpdateMessageView(notification); CreateOrUpdateCompactTitleMessageView(notification); CreateOrUpdateProgressBarView(notification); + CreateOrUpdateProgressStatusView(notification); CreateOrUpdateListItemViews(notification); CreateOrUpdateIconView(notification); CreateOrUpdateSmallIconView(notification); CreateOrUpdateImageView(notification); - CreateOrUpdateCloseButtonView(notification); - CreateOrUpdateSettingsButtonView(notification); UpdateViewForExpandedState(expanded_); // Should be called at the last because SynthesizeMouseMoveEvent() requires // everything is in the right location when called. @@ -469,8 +477,13 @@ NotificationViewMD::NotificationViewMD(MessageCenterController* controller, SetLayoutManager( new views::BoxLayout(views::BoxLayout::kVertical, gfx::Insets(), 0)); + control_buttons_view_ = + base::MakeUnique<NotificationControlButtonsView>(this); + control_buttons_view_->set_owned_by_client(); + control_buttons_view_->SetBackgroundColor(SK_ColorTRANSPARENT); + // |header_row_| contains app_icon, app_name, control buttons, etc... - header_row_ = new NotificationHeaderView(this); + header_row_ = new NotificationHeaderView(control_buttons_view_.get(), this); AddChildView(header_row_); // |content_row_| contains title, message, image, progressbar, etc... @@ -486,6 +499,7 @@ NotificationViewMD::NotificationViewMD(MessageCenterController* controller, left_content_ = new views::View(); left_content_->SetLayoutManager( new views::BoxLayout(views::BoxLayout::kVertical, gfx::Insets(), 0)); + left_content_->SetBorder(views::CreateEmptyBorder(kLeftContentPadding)); content_row_->AddChildView(left_content_); content_row_layout->SetFlexForView(left_content_, 1); @@ -505,9 +519,11 @@ NotificationViewMD::NotificationViewMD(MessageCenterController* controller, AddChildView(actions_row_); CreateOrUpdateViews(notification); + UpdateControlButtonsVisibilityWithNotification(notification); SetEventTargeter( std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); + set_notify_enter_exit_on_child(true); } NotificationViewMD::~NotificationViewMD() {} @@ -539,11 +555,6 @@ gfx::NativeCursor NotificationViewMD::GetCursor(const ui::MouseEvent& event) { return views::GetNativeHandCursor(); } -void NotificationViewMD::OnMouseMoved(const ui::MouseEvent& event) { - MessageView::OnMouseMoved(event); - UpdateControlButtonsVisibility(); -} - void NotificationViewMD::OnMouseEntered(const ui::MouseEvent& event) { MessageView::OnMouseEntered(event); UpdateControlButtonsVisibility(); @@ -557,12 +568,23 @@ void NotificationViewMD::OnMouseExited(const ui::MouseEvent& event) { void NotificationViewMD::UpdateWithNotification( const Notification& notification) { MessageView::UpdateWithNotification(notification); + UpdateControlButtonsVisibilityWithNotification(notification); CreateOrUpdateViews(notification); Layout(); SchedulePaint(); } +// TODO(yoshiki): Move this to the parent class (MessageView). +void NotificationViewMD::UpdateControlButtonsVisibilityWithNotification( + const Notification& notification) { + control_buttons_view_->ShowSettingsButton( + notification.delegate() && + notification.delegate()->ShouldDisplaySettingsButton()); + control_buttons_view_->ShowCloseButton(!GetPinned()); + UpdateControlButtonsVisibility(); +} + void NotificationViewMD::ButtonPressed(views::Button* sender, const ui::Event& event) { // Certain operations can cause |this| to be destructed, so copy the members @@ -570,20 +592,6 @@ void NotificationViewMD::ButtonPressed(views::Button* sender, // TODO(dewittj): Remove this hack. std::string id(notification_id()); - if (header_row_->IsCloseButtonEnabled() && - sender == header_row_->close_button()) { - // Warning: This causes the NotificationViewMD itself to be deleted, so - // don't do anything afterwards. - OnCloseButtonPressed(); - return; - } - - if (header_row_->IsSettingsButtonEnabled() && - sender == header_row_->settings_button()) { - controller()->ClickOnSettingsButton(id); - return; - } - // Tapping anywhere on |header_row_| can expand the notification, though only // |expand_button| can be focused by TAB. if (IsExpandable() && sender == header_row_) { @@ -603,22 +611,39 @@ void NotificationViewMD::ButtonPressed(views::Button* sender, } bool NotificationViewMD::IsCloseButtonFocused() const { - if (!header_row_->IsCloseButtonEnabled()) - return false; - - const views::FocusManager* focus_manager = GetFocusManager(); - return focus_manager && - focus_manager->GetFocusedView() == header_row_->close_button(); + return control_buttons_view_->IsCloseButtonFocused(); } void NotificationViewMD::RequestFocusOnCloseButton() { - if (header_row_->IsCloseButtonEnabled()) - header_row_->close_button()->RequestFocus(); + control_buttons_view_->RequestFocusOnCloseButton(); } void NotificationViewMD::CreateOrUpdateContextTitleView( const Notification& notification) { +#if defined(OS_CHROMEOS) + // If |origin_url| and |display_source| are both empty, assume it is + // system notification, and use default |display_source| and + // |accent_color| for system notification. + // TODO(tetsui): Remove this after all system notification transition is + // completed. + // All system notification should use Notification::CreateSystemNotification() + if ((notification.display_source().empty() && + notification.origin_url().is_empty()) || + ShowDefaultDisplaySource(notification.notifier_id())) { + header_row_->SetAppName(l10n_util::GetStringFUTF16( + IDS_MESSAGE_CENTER_NOTIFICATION_CHROMEOS_SYSTEM, + MessageCenter::Get()->GetProductOSName())); + header_row_->SetAccentColor(message_center::kSystemNotificationColorNormal); + header_row_->SetTimestamp(notification.timestamp()); + return; + } +#endif + header_row_->SetAppName(notification.display_source()); + header_row_->SetAccentColor( + notification.accent_color() == SK_ColorTRANSPARENT + ? message_center::kNotificationDefaultAccentColor + : notification.accent_color()); header_row_->SetTimestamp(notification.timestamp()); } @@ -631,8 +656,6 @@ void NotificationViewMD::CreateOrUpdateTitleView( title_view_ = nullptr; return; } - const gfx::FontList& font_list = views::Label().font_list().Derive( - 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); int title_character_limit = kNotificationWidth * kMaxTitleLines / kMinPixelsPerTitleCharacter; @@ -640,10 +663,12 @@ void NotificationViewMD::CreateOrUpdateTitleView( base::string16 title = gfx::TruncateString( notification.title(), title_character_limit, gfx::WORD_BREAK); if (!title_view_) { + const gfx::FontList& font_list = GetTextFontList(); + title_view_ = new views::Label(title); title_view_->SetFontList(font_list); title_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - title_view_->SetEnabledColor(message_center::kRegularTextColor); + title_view_->SetEnabledColor(kRegularTextColorMD); left_content_->AddChildView(title_view_); } else { title_view_->SetText(title); @@ -663,22 +688,12 @@ void NotificationViewMD::CreateOrUpdateMessageView( base::string16 text = gfx::TruncateString( notification.message(), kMessageCharacterLimit, gfx::WORD_BREAK); - const gfx::FontList& font_list = views::Label().font_list().Derive( - 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); + const gfx::FontList& font_list = GetTextFontList(); if (!message_view_) { message_view_ = new BoundedLabel(text, font_list); message_view_->SetLineLimit(kMaxLinesForMessageView); - message_view_->SetColors(message_center::kDimTextColor, - kContextTextBackgroundColor); - - // TODO(tetsui): Workaround https://crbug.com/682266 by explicitly setting - // the width. - // Ideally, we should fix the original bug, but it seems there's no obvious - // solution for the bug according to https://crbug.com/678337#c7, we should - // ensure that the change won't break any of the users of BoxLayout class. - DCHECK(right_content_); - message_view_->SizeToFit(kMessageViewWidth); + message_view_->SetColors(kDimTextColorMD, kContextTextBackgroundColor); left_content_->AddChildView(message_view_); } else { @@ -730,7 +745,35 @@ void NotificationViewMD::CreateOrUpdateProgressBarView( progress_bar_view_->SetValue(notification.progress() / 100.0); progress_bar_view_->SetVisible(notification.items().empty()); - header_row_->SetProgress(notification.progress()); + if (0 <= notification.progress() && notification.progress() <= 100) + header_row_->SetProgress(notification.progress()); + else + header_row_->ClearProgress(); +} + +void NotificationViewMD::CreateOrUpdateProgressStatusView( + const Notification& notification) { + if (notification.type() != NOTIFICATION_TYPE_PROGRESS || + notification.progress_status().empty()) { + if (!status_view_) + return; + DCHECK(left_content_->Contains(status_view_)); + delete status_view_; + status_view_ = nullptr; + return; + } + + if (!status_view_) { + const gfx::FontList& font_list = GetTextFontList(); + status_view_ = new views::Label(); + status_view_->SetFontList(font_list); + status_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); + status_view_->SetEnabledColor(kDimTextColorMD); + status_view_->SetBorder(views::CreateEmptyBorder(kStatusTextPadding)); + left_content_->AddChildView(status_view_); + } + + status_view_->SetText(notification.progress_status()); } void NotificationViewMD::CreateOrUpdateListItemViews( @@ -758,8 +801,7 @@ void NotificationViewMD::CreateOrUpdateListItemViews( void NotificationViewMD::CreateOrUpdateIconView( const Notification& notification) { if (notification.type() == NOTIFICATION_TYPE_PROGRESS || - notification.type() == NOTIFICATION_TYPE_MULTIPLE || - notification.type() == NOTIFICATION_TYPE_IMAGE) { + notification.type() == NOTIFICATION_TYPE_MULTIPLE) { DCHECK(!icon_view_ || right_content_->Contains(icon_view_)); delete icon_view_; icon_view_ = nullptr; @@ -771,17 +813,26 @@ void NotificationViewMD::CreateOrUpdateIconView( right_content_->AddChildView(icon_view_); } - gfx::ImageSkia icon = notification.icon().AsImageSkia(); + // If |use_image_as_icon| is set, use |image| as the icon on the right + // side, instead of |icon|. + gfx::ImageSkia icon; + if (notification.use_image_as_icon()) + icon = notification.image().AsImageSkia(); + else + icon = notification.icon().AsImageSkia(); icon_view_->SetImage(icon, icon.size()); + + // If |use_image_as_icon| is set, hide the icon on the right side when + // the notification is expanded. + hide_icon_on_expanded_ = notification.use_image_as_icon(); } void NotificationViewMD::CreateOrUpdateSmallIconView( const Notification& notification) { - gfx::ImageSkia icon = - notification.small_image().IsEmpty() - ? GetProductIcon() - : GetMaskedIcon(notification.small_image().AsImageSkia()); - header_row_->SetAppIcon(icon); + if (notification.small_image().IsEmpty()) + header_row_->ClearAppIcon(); + else + header_row_->SetAppIcon(notification.small_image().AsImageSkia()); } void NotificationViewMD::CreateOrUpdateImageView( @@ -829,6 +880,12 @@ void NotificationViewMD::CreateOrUpdateActionButtonViews( action_buttons_[i]->SchedulePaint(); action_buttons_[i]->Layout(); } + + // Change action button color to the accent color. + action_buttons_[i]->SetEnabledTextColors(notification.accent_color() == + SK_ColorTRANSPARENT + ? kActionButtonTextColor + : notification.accent_color()); } // Inherit mouse hover state when action button views reset. @@ -845,24 +902,6 @@ void NotificationViewMD::CreateOrUpdateActionButtonViews( } } -void NotificationViewMD::CreateOrUpdateCloseButtonView( - const Notification& notification) { - if (!notification.pinned()) { - header_row_->SetCloseButtonEnabled(true); - } else { - header_row_->SetCloseButtonEnabled(false); - } -} - -void NotificationViewMD::CreateOrUpdateSettingsButtonView( - const Notification& notification) { - if (notification.delegate() && - notification.delegate()->ShouldDisplaySettingsButton()) - header_row_->SetSettingsButtonEnabled(true); - else - header_row_->SetSettingsButtonEnabled(false); -} - bool NotificationViewMD::IsExpandable() { // Expandable if the message exceeds one line. if (message_view_ && message_view_->visible() && @@ -887,11 +926,7 @@ bool NotificationViewMD::IsExpandable() { } void NotificationViewMD::ToggleExpanded() { - expanded_ = !expanded_; - UpdateViewForExpandedState(expanded_); - content_row_->InvalidateLayout(); - if (controller()) - controller()->UpdateNotificationSize(notification_id()); + SetExpanded(!expanded_); } void NotificationViewMD::UpdateViewForExpandedState(bool expanded) { @@ -906,27 +941,61 @@ void NotificationViewMD::UpdateViewForExpandedState(bool expanded) { for (size_t i = kMaxLinesForMessageView; i < item_views_.size(); ++i) { item_views_[i]->SetVisible(expanded); } + if (status_view_) + status_view_->SetVisible(expanded); header_row_->SetOverflowIndicator( list_items_count_ - (expanded ? item_views_.size() : kMaxLinesForMessageView)); + + if (icon_view_) + icon_view_->SetVisible(!hide_icon_on_expanded_ || !expanded); + + if (icon_view_ && icon_view_->visible()) { + left_content_->SetBorder( + views::CreateEmptyBorder(kLeftContentPaddingWithIcon)); + + // TODO(tetsui): Workaround https://crbug.com/682266 by explicitly setting + // the width. + // Ideally, we should fix the original bug, but it seems there's no obvious + // solution for the bug according to https://crbug.com/678337#c7, we should + // ensure that the change won't break any of the users of BoxLayout class. + if (message_view_) + message_view_->SizeToFit(kMessageViewWidthWithIcon); + } else { + left_content_->SetBorder(views::CreateEmptyBorder(kLeftContentPadding)); + if (message_view_) + message_view_->SizeToFit(kMessageViewWidth); + } } +// TODO(yoshiki): Move this to the parent class (MessageView) and share the code +// among NotificationView and ArcNotificationView. void NotificationViewMD::UpdateControlButtonsVisibility() { - const bool target_visibility = IsMouseHovered() || HasFocus() || - (header_row_->IsExpandButtonEnabled() && - header_row_->expand_button()->HasFocus()) || - (header_row_->IsCloseButtonEnabled() && - header_row_->close_button()->HasFocus()) || - (header_row_->IsSettingsButtonEnabled() && - header_row_->settings_button()->HasFocus()); + const bool target_visibility = + IsMouseHovered() || control_buttons_view_->IsCloseButtonFocused() || + control_buttons_view_->IsSettingsButtonFocused(); - header_row_->SetControlButtonsVisible(target_visibility); + control_buttons_view_->SetVisible(target_visibility); } NotificationControlButtonsView* NotificationViewMD::GetControlButtonsView() const { - // TODO(yoshiki): have this view use NotificationControlButtonsView. - return nullptr; + return control_buttons_view_.get(); +} + +bool NotificationViewMD::IsExpanded() const { + return expanded_; +} + +void NotificationViewMD::SetExpanded(bool expanded) { + if (expanded_ == expanded) + return; + expanded_ = expanded; + + UpdateViewForExpandedState(expanded_); + content_row_->InvalidateLayout(); + if (controller()) + controller()->UpdateNotificationSize(notification_id()); } } // namespace message_center diff --git a/chromium/ui/message_center/views/notification_view_md.h b/chromium/ui/message_center/views/notification_view_md.h index 8d595d92f1f..51f8f723868 100644 --- a/chromium/ui/message_center/views/notification_view_md.h +++ b/chromium/ui/message_center/views/notification_view_md.h @@ -12,6 +12,7 @@ #include "ui/message_center/message_center_export.h" #include "ui/message_center/views/message_view.h" #include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/label_button.h" #include "ui/views/view_targeter_delegate.h" namespace views { @@ -32,6 +33,27 @@ class ItemView; class LargeImageContainerView; } +// This class is needed in addition to LabelButton mainly becuase we want to set +// visible_opacity of InkDropHighlight. +// This button capitalizes the given label string. +class NotificationButtonMD : public views::LabelButton { + public: + NotificationButtonMD(views::ButtonListener* listener, + const base::string16& text); + ~NotificationButtonMD() override; + + void SetText(const base::string16& text) override; + const char* GetClassName() const override; + + std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight() + const override; + + SkColor enabled_color_for_testing() { return label()->enabled_color(); } + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationButtonMD); +}; + // View that displays all current types of notification (web, basic, image, and // list) except the custom notification. Future notification types may be // handled by other classes, in which case instances of those classes would be @@ -50,7 +72,6 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD void OnFocus() override; void ScrollRectToVisible(const gfx::Rect& rect) override; gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; - void OnMouseMoved(const ui::MouseEvent& event) override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; @@ -61,6 +82,8 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD void RequestFocusOnCloseButton() override; void UpdateControlButtonsVisibility() override; NotificationControlButtonsView* GetControlButtonsView() const override; + bool IsExpanded() const override; + void SetExpanded(bool expanded) override; // views::ViewTargeterDelegate: views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override; @@ -71,9 +94,14 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonsStateTest); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonCountTest); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, ExpandLongMessage); + FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestAccentColor); + FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UseImageAsIcon); friend class NotificationViewMDTest; + void UpdateControlButtonsVisibilityWithNotification( + const Notification& notification); + void CreateOrUpdateViews(const Notification& notification); void CreateOrUpdateContextTitleView(const Notification& notification); @@ -81,21 +109,26 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD void CreateOrUpdateMessageView(const Notification& notification); void CreateOrUpdateCompactTitleMessageView(const Notification& notification); void CreateOrUpdateProgressBarView(const Notification& notification); + void CreateOrUpdateProgressStatusView(const Notification& notification); void CreateOrUpdateListItemViews(const Notification& notification); void CreateOrUpdateIconView(const Notification& notification); void CreateOrUpdateSmallIconView(const Notification& notification); void CreateOrUpdateImageView(const Notification& notification); void CreateOrUpdateActionButtonViews(const Notification& notification); - void CreateOrUpdateCloseButtonView(const Notification& notification); - void CreateOrUpdateSettingsButtonView(const Notification& notification); bool IsExpandable(); void ToggleExpanded(); void UpdateViewForExpandedState(bool expanded); + // View containing close and settings buttons + std::unique_ptr<NotificationControlButtonsView> control_buttons_view_; + // Whether this notification is expanded or not. bool expanded_ = false; + // Whether hiding icon on the right side when expanded. + bool hide_icon_on_expanded_ = false; + // Number of total list items in the given Notification class. int list_items_count_ = 0; @@ -114,9 +147,10 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD // Views which are dinamicallly created inside view hierarchy. views::Label* title_view_ = nullptr; BoundedLabel* message_view_ = nullptr; + views::Label* status_view_ = nullptr; ProportionalImageView* icon_view_ = nullptr; LargeImageContainerView* image_container_view_ = nullptr; - std::vector<views::LabelButton*> action_buttons_; + std::vector<NotificationButtonMD*> action_buttons_; std::vector<ItemView*> item_views_; views::ProgressBar* progress_bar_view_ = nullptr; CompactTitleMessageView* compact_title_message_view_ = nullptr; 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 f39b2d565fb..bc5af61e22d 100644 --- a/chromium/ui/message_center/views/notification_view_md_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_md_unittest.cc @@ -16,7 +16,9 @@ #include "ui/message_center/message_center_style.h" #include "ui/message_center/views/bounded_label.h" #include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/message_center/views/notification_header_view.h" +#include "ui/message_center/views/padded_button.h" #include "ui/message_center/views/proportional_image_view.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/label_button.h" @@ -78,7 +80,7 @@ class NotificationViewMDTest : public views::ViewsTestBase, void BeginScroll(); void EndScroll(); void ScrollBy(int dx); - views::ImageButton* GetCloseButton(); + views::View* GetCloseButton(); private: std::set<std::string> removed_ids_; @@ -263,8 +265,8 @@ void NotificationViewMDTest::ScrollBy(int dx) { DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, dx, 0)); } -views::ImageButton* NotificationViewMDTest::GetCloseButton() { - return notification_view()->header_row_->close_button(); +views::View* NotificationViewMDTest::GetCloseButton() { + return notification_view()->GetControlButtonsView()->close_button(); } /* Unit tests *****************************************************************/ @@ -299,7 +301,7 @@ TEST_F(NotificationViewMDTest, CreateOrUpdateTest) { TEST_F(NotificationViewMDTest, TestIconSizing) { // TODO(tetsui): Remove duplicated integer literal in CreateOrUpdateIconView. - const int kNotificationIconSize = 30; + const int kNotificationIconSize = 36; notification()->set_type(NOTIFICATION_TYPE_SIMPLE); ProportionalImageView* view = notification_view()->icon_view_; @@ -344,7 +346,7 @@ TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) { notification_view()->ToggleExpanded(); EXPECT_TRUE(notification_view()->actions_row_->visible()); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); // Now construct a mouse move event 1 pixel inside the boundary of the action @@ -356,12 +358,12 @@ TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); widget()->OnMouseEvent(&move); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); notification_view()->CreateOrUpdateViews(*notification()); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); // Now construct a mouse move event 1 pixel outside the boundary of the @@ -371,7 +373,7 @@ TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); widget()->OnMouseEvent(&move); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); } @@ -385,9 +387,9 @@ TEST_F(NotificationViewMDTest, UpdateButtonCountTest) { notification_view()->ToggleExpanded(); EXPECT_TRUE(notification_view()->actions_row_->visible()); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[1]->state()); // Now construct a mouse move event 1 pixel inside the boundary of the action @@ -401,15 +403,15 @@ TEST_F(NotificationViewMDTest, UpdateButtonCountTest) { views::test::WidgetTest::GetEventSink(widget())->OnEventFromSource(&move); EXPECT_FALSE(details.dispatcher_destroyed); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[1]->state()); notification()->set_buttons(CreateButtons(1)); notification_view()->UpdateWithNotification(*notification()); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); EXPECT_EQ(1u, notification_view()->action_buttons_.size()); @@ -420,7 +422,7 @@ TEST_F(NotificationViewMDTest, UpdateButtonCountTest) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); widget()->OnMouseEvent(&move); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); } @@ -491,10 +493,25 @@ TEST_F(NotificationViewMDTest, SlideOutPinned) { } TEST_F(NotificationViewMDTest, Pinned) { + // Visible at the initial state. + EXPECT_TRUE(GetCloseButton()); + EXPECT_TRUE(GetCloseButton()->visible()); + + // Pin. notification()->set_pinned(true); + UpdateNotificationViews(); + EXPECT_FALSE(GetCloseButton()); + // Unpin. + notification()->set_pinned(false); + UpdateNotificationViews(); + EXPECT_TRUE(GetCloseButton()); + EXPECT_TRUE(GetCloseButton()->visible()); + + // Pin again. + notification()->set_pinned(true); UpdateNotificationViews(); - EXPECT_FALSE(GetCloseButton()->visible()); + EXPECT_FALSE(GetCloseButton()); } #endif // defined(OS_CHROMEOS) @@ -532,4 +549,74 @@ TEST_F(NotificationViewMDTest, ExpandLongMessage) { notification_view()->GetPreferredSize().height()); } +TEST_F(NotificationViewMDTest, TestAccentColor) { + const SkColor kActionButtonTextColor = SkColorSetRGB(0x33, 0x67, 0xD6); + const SkColor kCustomAccentColor = SkColorSetRGB(0xea, 0x61, 0x0); + + notification()->set_buttons(CreateButtons(2)); + UpdateNotificationViews(); + widget()->Show(); + + // Action buttons are hidden by collapsed state. + if (!notification_view()->expanded_) + notification_view()->ToggleExpanded(); + EXPECT_TRUE(notification_view()->actions_row_->visible()); + + // By default, header does not have accent color (default grey), and + // buttons have default accent color. + EXPECT_EQ(message_center::kNotificationDefaultAccentColor, + notification_view()->header_row_->accent_color_for_testing()); + EXPECT_EQ( + kActionButtonTextColor, + notification_view()->action_buttons_[0]->enabled_color_for_testing()); + EXPECT_EQ( + kActionButtonTextColor, + notification_view()->action_buttons_[1]->enabled_color_for_testing()); + + // If custom accent color is set, the header and the buttons should have the + // same accent color. + notification()->set_accent_color(kCustomAccentColor); + UpdateNotificationViews(); + EXPECT_EQ(kCustomAccentColor, + notification_view()->header_row_->accent_color_for_testing()); + EXPECT_EQ( + kCustomAccentColor, + notification_view()->action_buttons_[0]->enabled_color_for_testing()); + EXPECT_EQ( + kCustomAccentColor, + notification_view()->action_buttons_[1]->enabled_color_for_testing()); +} + +TEST_F(NotificationViewMDTest, UseImageAsIcon) { + // TODO(tetsui): Remove duplicated integer literal in CreateOrUpdateIconView. + const int kNotificationIconSize = 30; + + notification()->set_type(NotificationType::NOTIFICATION_TYPE_IMAGE); + notification()->set_icon( + CreateTestImage(kNotificationIconSize, kNotificationIconSize)); + + // Test normal notification. + UpdateNotificationViews(); + EXPECT_FALSE(notification_view()->expanded_); + EXPECT_TRUE(notification_view()->icon_view_->visible()); + + // Icon on the right side is still visible when expanded. + notification_view()->ToggleExpanded(); + EXPECT_TRUE(notification_view()->expanded_); + EXPECT_TRUE(notification_view()->icon_view_->visible()); + + notification_view()->ToggleExpanded(); + EXPECT_FALSE(notification_view()->expanded_); + + // Test notification with use_image_as_icon e.g. screenshot preview. + notification()->set_use_image_as_icon(true); + UpdateNotificationViews(); + EXPECT_TRUE(notification_view()->icon_view_->visible()); + + // Icon on the right side is not visible when expanded. + notification_view()->ToggleExpanded(); + EXPECT_TRUE(notification_view()->expanded_); + EXPECT_FALSE(notification_view()->icon_view_->visible()); +} + } // namespace message_center diff --git a/chromium/ui/message_center/views/notification_view_unittest.cc b/chromium/ui/message_center/views/notification_view_unittest.cc index 4e5ac153ed8..1ecd9270ee0 100644 --- a/chromium/ui/message_center/views/notification_view_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_unittest.cc @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -38,6 +39,10 @@ #include "ui/views/test/widget_test.h" #include "ui/views/widget/widget_delegate.h" +#if defined(OS_WIN) +#include "ui/base/win/shell.h" +#endif + namespace message_center { // A test delegate used for tests that deal with the notification settings @@ -162,11 +167,11 @@ class NotificationViewTest : public views::ViewsTestBase, } } - PaddedButton* GetCloseButton() { + views::Button* GetCloseButton() { return notification_view()->control_buttons_view_->close_button(); } - PaddedButton* GetSettingsButton() { + views::Button* GetSettingsButton() { return notification_view()->control_buttons_view_->settings_button(); } @@ -238,8 +243,21 @@ void NotificationViewTest::SetUp() { notification_->set_image(CreateTestImage(320, 240)); // Then create a new NotificationView with that single notification. - notification_view_.reset(static_cast<NotificationView*>( - MessageViewFactory::Create(this, *notification_, true))); + notification_view_.reset(new NotificationView(this, *notification_)); + + // It depends on platform whether shadows are added. + // See MessageViewFactory::Create. + bool is_nested = true; +#if defined(OS_LINUX) + is_nested = false; +#endif +#if defined(OS_WIN) + if (!ui::win::IsAeroGlassEnabled()) + is_nested = false; +#endif + if (is_nested) + notification_view_->SetIsNested(); + notification_view_->set_owned_by_client(); views::Widget::InitParams init_params( @@ -463,7 +481,7 @@ TEST_F(NotificationViewTest, UpdateButtonsStateTest) { notification_view()->UpdateWithNotification(*notification()); widget()->Show(); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); // Now construct a mouse move event 1 pixel inside the boundary of the action @@ -475,12 +493,12 @@ TEST_F(NotificationViewTest, UpdateButtonsStateTest) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); widget()->OnMouseEvent(&move); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); notification_view()->UpdateWithNotification(*notification()); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); // Now construct a mouse move event 1 pixel outside the boundary of the @@ -490,7 +508,7 @@ TEST_F(NotificationViewTest, UpdateButtonsStateTest) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); widget()->OnMouseEvent(&move); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); } @@ -499,9 +517,9 @@ TEST_F(NotificationViewTest, UpdateButtonCountTest) { notification_view()->UpdateWithNotification(*notification()); widget()->Show(); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[1]->state()); // Now construct a mouse move event 1 pixel inside the boundary of the action @@ -515,15 +533,15 @@ TEST_F(NotificationViewTest, UpdateButtonCountTest) { views::test::WidgetTest::GetEventSink(widget())->OnEventFromSource(&move); EXPECT_FALSE(details.dispatcher_destroyed); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[1]->state()); notification()->set_buttons(CreateButtons(1)); notification_view()->UpdateWithNotification(*notification()); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, + EXPECT_EQ(views::Button::STATE_HOVERED, notification_view()->action_buttons_[0]->state()); EXPECT_EQ(1u, notification_view()->action_buttons_.size()); @@ -534,7 +552,7 @@ TEST_F(NotificationViewTest, UpdateButtonCountTest) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); widget()->OnMouseEvent(&move); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, + EXPECT_EQ(views::Button::STATE_NORMAL, notification_view()->action_buttons_[0]->state()); } @@ -552,7 +570,7 @@ TEST_F(NotificationViewTest, SettingsButtonTest) { widget()->Show(); EXPECT_TRUE(NULL != GetSettingsButton()); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, GetSettingsButton()->state()); + EXPECT_EQ(views::Button::STATE_NORMAL, GetSettingsButton()->state()); // Now construct a mouse move event 1 pixel inside the boundary of the action // button. @@ -565,7 +583,7 @@ TEST_F(NotificationViewTest, SettingsButtonTest) { views::test::WidgetTest::GetEventSink(widget())->OnEventFromSource(&move); EXPECT_FALSE(details.dispatcher_destroyed); - EXPECT_EQ(views::CustomButton::STATE_HOVERED, GetSettingsButton()->state()); + EXPECT_EQ(views::Button::STATE_HOVERED, GetSettingsButton()->state()); // Now construct a mouse move event 1 pixel outside the boundary of the // widget. @@ -574,7 +592,7 @@ TEST_F(NotificationViewTest, SettingsButtonTest) { ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); widget()->OnMouseEvent(&move); - EXPECT_EQ(views::CustomButton::STATE_NORMAL, GetSettingsButton()->state()); + EXPECT_EQ(views::Button::STATE_NORMAL, GetSettingsButton()->state()); } TEST_F(NotificationViewTest, ViewOrderingTest) { @@ -731,11 +749,31 @@ TEST_F(NotificationViewTest, SlideOutPinned) { EXPECT_FALSE(IsRemoved(notification_id)); } -TEST_F(NotificationViewTest, Pinned) { +TEST_F(NotificationViewTest, SlideOutForceDisablePinned) { + ui::ScopedAnimationDurationScaleMode zero_duration_scope( + ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); + notification()->set_pinned(true); + notification_view()->set_force_disable_pinned(); + UpdateNotificationViews(); + std::string notification_id = notification()->id(); + + BeginScroll(); + ScrollBy(-200); + EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_EQ(-200.f, GetNotificationSlideAmount()); + EndScroll(); + EXPECT_TRUE(IsRemoved(notification_id)); +} +TEST_F(NotificationViewTest, Pinned) { + notification()->set_pinned(true); UpdateNotificationViews(); EXPECT_EQ(NULL, GetCloseButton()); + + notification_view()->set_force_disable_pinned(); + UpdateNotificationViews(); + EXPECT_TRUE(GetCloseButton()); } #endif // defined(OS_CHROMEOS) diff --git a/chromium/ui/message_center/views/notifier_settings_view.cc b/chromium/ui/message_center/views/notifier_settings_view.cc index 7958f037b9f..639c3341439 100644 --- a/chromium/ui/message_center/views/notifier_settings_view.cc +++ b/chromium/ui/message_center/views/notifier_settings_view.cc @@ -234,7 +234,7 @@ NotifierSettingsView::NotifierButton::NotifierButton( NotifierSettingsProvider* provider, std::unique_ptr<Notifier> notifier, views::ButtonListener* listener) - : views::CustomButton(listener), + : views::Button(listener), provider_(provider), notifier_(std::move(notifier)), icon_view_(new views::ImageView()), @@ -337,7 +337,7 @@ void NotifierSettingsView::NotifierButton::ButtonPressed( // the state on NotifierSettingsView::ButtonPressed() too, so here change // back to the previous state. checkbox_->SetChecked(!checkbox_->checked()); - CustomButton::NotifyClick(event); + Button::NotifyClick(event); } else if (button == learn_more_) { DCHECK(provider_); provider_->OnNotifierAdvancedSettingsRequested(notifier_->notifier_id, diff --git a/chromium/ui/message_center/views/notifier_settings_view.h b/chromium/ui/message_center/views/notifier_settings_view.h index e6ed209b1b2..18d8659891d 100644 --- a/chromium/ui/message_center/views/notifier_settings_view.h +++ b/chromium/ui/message_center/views/notifier_settings_view.h @@ -12,7 +12,6 @@ #include "base/macros.h" #include "ui/message_center/message_center_export.h" #include "ui/message_center/notifier_settings.h" -#include "ui/message_center/views/message_bubble_base.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/combobox/combobox_listener.h" @@ -57,8 +56,8 @@ class MESSAGE_CENTER_EXPORT NotifierSettingsView private: FRIEND_TEST_ALL_PREFIXES(NotifierSettingsViewTest, TestLearnMoreButton); - class MESSAGE_CENTER_EXPORT NotifierButton : public views::CustomButton, - public views::ButtonListener { + class MESSAGE_CENTER_EXPORT NotifierButton : public views::Button, + public views::ButtonListener { public: NotifierButton(NotifierSettingsProvider* provider, std::unique_ptr<Notifier> notifier, diff --git a/chromium/ui/native_theme/common_theme.cc b/chromium/ui/native_theme/common_theme.cc index 96c817021b6..d299f91dbb7 100644 --- a/chromium/ui/native_theme/common_theme.cc +++ b/chromium/ui/native_theme/common_theme.cc @@ -35,7 +35,7 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, // FocusableBorder case NativeTheme::kColorId_UnfocusedBorderColor: - return SkColorSetA(SK_ColorBLACK, 0x33); + return SkColorSetA(SK_ColorBLACK, 0x4e); // Textfields case NativeTheme::kColorId_TextfieldDefaultColor: diff --git a/chromium/ui/native_theme/native_theme_features.cc b/chromium/ui/native_theme/native_theme_features.cc index 25cf50a4605..28d388ca3ed 100644 --- a/chromium/ui/native_theme/native_theme_features.cc +++ b/chromium/ui/native_theme/native_theme_features.cc @@ -20,6 +20,17 @@ constexpr base::FeatureState kOverlayScrollbarFeatureState = const base::Feature kOverlayScrollbar{"OverlayScrollbar", kOverlayScrollbarFeatureState}; +// Experiment: Enables will flash all scrollbars in page after any scroll +// update. +const base::Feature kOverlayScrollbarFlashAfterAnyScrollUpdate{ + "OverlayScrollbarFlashAfterAnyScrollUpdate", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// Experiment: Enables will flash scorllbar when user move mouse enter a +// scrollable area. +const base::Feature kOverlayScrollbarFlashWhenMouseEnter{ + "OverlayScrollbarFlashWhenMouseEnter", base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features namespace ui { @@ -28,4 +39,14 @@ bool IsOverlayScrollbarEnabled() { return base::FeatureList::IsEnabled(features::kOverlayScrollbar); } +bool OverlayScrollbarFlashAfterAnyScrollUpdate() { + return base::FeatureList::IsEnabled( + features::kOverlayScrollbarFlashAfterAnyScrollUpdate); +} + +bool OverlayScrollbarFlashWhenMouseEnter() { + return base::FeatureList::IsEnabled( + features::kOverlayScrollbarFlashWhenMouseEnter); +} + } // namespace ui diff --git a/chromium/ui/native_theme/native_theme_features.h b/chromium/ui/native_theme/native_theme_features.h index 85e898574c2..e6cbdec5944 100644 --- a/chromium/ui/native_theme/native_theme_features.h +++ b/chromium/ui/native_theme/native_theme_features.h @@ -13,12 +13,18 @@ namespace features { NATIVE_THEME_EXPORT extern const base::Feature kOverlayScrollbar; +NATIVE_THEME_EXPORT extern const base::Feature + kOverlayScrollbarFlashAfterAnyScrollUpdate; +NATIVE_THEME_EXPORT extern const base::Feature + kOverlayScrollbarFlashWhenMouseEnter; } // namespace features namespace ui { NATIVE_THEME_EXPORT bool IsOverlayScrollbarEnabled(); +NATIVE_THEME_EXPORT bool OverlayScrollbarFlashAfterAnyScrollUpdate(); +NATIVE_THEME_EXPORT bool OverlayScrollbarFlashWhenMouseEnter(); } // namespace ui diff --git a/chromium/ui/native_theme/native_theme_win.cc b/chromium/ui/native_theme/native_theme_win.cc index 3340f3744cf..649f3b21edc 100644 --- a/chromium/ui/native_theme/native_theme_win.cc +++ b/chromium/ui/native_theme/native_theme_win.cc @@ -457,8 +457,12 @@ SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const { // FocusableBorder case kColorId_FocusedBorderColor: + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) + break; return kFocusedBorderColor; case kColorId_UnfocusedBorderColor: + if (ui::MaterialDesignController::IsSecondaryUiMaterial()) + break; return kUnfocusedBorderColor; // Button diff --git a/chromium/ui/ozone/common/BUILD.gn b/chromium/ui/ozone/common/BUILD.gn index f079f53ef07..cc1ee3f2e29 100644 --- a/chromium/ui/ozone/common/BUILD.gn +++ b/chromium/ui/ozone/common/BUILD.gn @@ -8,8 +8,6 @@ assert(use_ozone) source_set("common") { sources = [ - "display_snapshot_proxy.cc", - "display_snapshot_proxy.h", "egl_util.cc", "egl_util.h", "gl_ozone_egl.cc", diff --git a/chromium/ui/ozone/common/display_snapshot_proxy.cc b/chromium/ui/ozone/common/display_snapshot_proxy.cc deleted file mode 100644 index 88ef6cc377f..00000000000 --- a/chromium/ui/ozone/common/display_snapshot_proxy.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/ozone/common/display_snapshot_proxy.h" - -#include <stddef.h> - -#include "base/memory/ptr_util.h" -#include "ui/display/types/display_mode.h" -#include "ui/ozone/common/gpu/ozone_gpu_message_params.h" - -namespace ui { - -namespace { - -bool SameModes(const DisplayMode_Params& lhs, const DisplayMode_Params& rhs) { - return lhs.size == rhs.size && lhs.is_interlaced == rhs.is_interlaced && - lhs.refresh_rate == rhs.refresh_rate; -} - -} // namespace - -DisplaySnapshotProxy::DisplaySnapshotProxy(const DisplaySnapshot_Params& params) - : DisplaySnapshotMojo( - params.display_id, - params.origin, - params.physical_size, - params.type, - params.is_aspect_preserving_scaling, - params.has_overscan, - params.has_color_correction_matrix, - params.display_name, - params.sys_path, - std::vector<std::unique_ptr<const display::DisplayMode>>(), - params.edid, - nullptr, - nullptr, - params.string_representation) { - for (size_t i = 0; i < params.modes.size(); ++i) { - modes_.push_back(base::MakeUnique<display::DisplayMode>( - params.modes[i].size, params.modes[i].is_interlaced, - params.modes[i].refresh_rate)); - - if (params.has_current_mode && - SameModes(params.modes[i], params.current_mode)) - current_mode_ = modes_.back().get(); - - if (params.has_native_mode && - SameModes(params.modes[i], params.native_mode)) - native_mode_ = modes_.back().get(); - } - - product_id_ = params.product_id; - maximum_cursor_size_ = params.maximum_cursor_size; -} - -DisplaySnapshotProxy::~DisplaySnapshotProxy() { -} - -} // namespace ui diff --git a/chromium/ui/ozone/common/display_snapshot_proxy.h b/chromium/ui/ozone/common/display_snapshot_proxy.h deleted file mode 100644 index a53e2121f67..00000000000 --- a/chromium/ui/ozone/common/display_snapshot_proxy.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_OZONE_COMMON_DISPLAY_SNAPSHOT_PROXY_H_ -#define UI_OZONE_COMMON_DISPLAY_SNAPSHOT_PROXY_H_ - -#include "base/macros.h" -#include "ui/display/types/display_snapshot_mojo.h" - -namespace ui { - -struct DisplaySnapshot_Params; - -class DisplaySnapshotProxy : public display::DisplaySnapshotMojo { - public: - DisplaySnapshotProxy(const DisplaySnapshot_Params& params); - ~DisplaySnapshotProxy() override; - - private: - DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotProxy); -}; - -} // namespace ui - -#endif // UI_OZONE_COMMON_DISPLAY_SNAPSHOT_PROXY_H_ diff --git a/chromium/ui/ozone/common/gl_ozone_osmesa.cc b/chromium/ui/ozone/common/gl_ozone_osmesa.cc index 2ee4dbba2d6..e7ccc733f59 100644 --- a/chromium/ui/ozone/common/gl_ozone_osmesa.cc +++ b/chromium/ui/ozone/common/gl_ozone_osmesa.cc @@ -4,6 +4,7 @@ #include "ui/ozone/common/gl_ozone_osmesa.h" +#include "build/build_config.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_context_osmesa.h" @@ -27,7 +28,14 @@ bool GLOzoneOSMesa::InitializeGLOneOffPlatform() { bool GLOzoneOSMesa::InitializeStaticGLBindings( gl::GLImplementation implementation) { +#if defined(OS_FUCHSIA) + // TODO(fuchsia): Enable this once there's EGL available, see + // https://crbug.com/750943. + NOTIMPLEMENTED(); + return false; +#else return gl::InitializeStaticGLBindingsOSMesaGL(); +#endif } void GLOzoneOSMesa::InitializeDebugGLBindings() { diff --git a/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.cc b/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.cc index 29692cacea4..79ddb77c77c 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.cc +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.cc @@ -31,23 +31,12 @@ OverlayCheck_Params::OverlayCheck_Params( format(candidate.format), display_rect(gfx::ToNearestRect(candidate.display_rect)), crop_rect(candidate.crop_rect), - plane_z_order(candidate.plane_z_order) {} + plane_z_order(candidate.plane_z_order), + is_overlay_candidate(candidate.overlay_handled) {} OverlayCheck_Params::OverlayCheck_Params(const OverlayCheck_Params& other) = default; OverlayCheck_Params::~OverlayCheck_Params() {} -bool OverlayCheck_Params::operator<(const OverlayCheck_Params& param) const { - int lwidth = buffer_size.width(); - int lheight = buffer_size.height(); - int rwidth = param.buffer_size.width(); - int rheight = param.buffer_size.height(); - - return std::tie(plane_z_order, format, display_rect, lwidth, lheight, - transform) < std::tie(param.plane_z_order, param.format, - param.display_rect, rwidth, rheight, - param.transform); -} - } // namespace ui 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 67d138ee680..204051857d5 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_message_params.h @@ -52,7 +52,6 @@ struct DisplaySnapshot_Params { bool has_native_mode = false; DisplayMode_Params native_mode; int64_t product_id = 0; - std::string string_representation; gfx::Size maximum_cursor_size; }; @@ -62,7 +61,6 @@ struct OverlayCheck_Params { OverlayCheck_Params(const OverlayCheck_Params& other); ~OverlayCheck_Params(); - bool operator<(const OverlayCheck_Params& plane) const; gfx::Size buffer_size; gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_NONE; diff --git a/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h b/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h index c20cc0813a5..b97616c4e65 100644 --- a/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h +++ b/chromium/ui/ozone/common/gpu/ozone_gpu_messages.h @@ -60,7 +60,6 @@ IPC_STRUCT_TRAITS_BEGIN(ui::DisplaySnapshot_Params) IPC_STRUCT_TRAITS_MEMBER(has_native_mode) IPC_STRUCT_TRAITS_MEMBER(native_mode) IPC_STRUCT_TRAITS_MEMBER(product_id) - IPC_STRUCT_TRAITS_MEMBER(string_representation) IPC_STRUCT_TRAITS_MEMBER(maximum_cursor_size) IPC_STRUCT_TRAITS_END() diff --git a/chromium/ui/ozone/demo/gl_renderer.cc b/chromium/ui/ozone/demo/gl_renderer.cc index 970a299bb50..53f5e96b9f9 100644 --- a/chromium/ui/ozone/demo/gl_renderer.cc +++ b/chromium/ui/ozone/demo/gl_renderer.cc @@ -30,7 +30,7 @@ bool GlRenderer::Initialize() { return false; } - surface_->Resize(size_, 1.f, true); + surface_->Resize(size_, 1.f, gl::GLSurface::ColorSpace::UNSPECIFIED, true); if (!context_->MakeCurrent(surface_.get())) { LOG(ERROR) << "Failed to make GL context current"; diff --git a/chromium/ui/ozone/demo/ozone_demo.cc b/chromium/ui/ozone/demo/ozone_demo.cc index bf9aa69977e..d5879e685d0 100644 --- a/chromium/ui/ozone/demo/ozone_demo.cc +++ b/chromium/ui/ozone/demo/ozone_demo.cc @@ -80,7 +80,7 @@ class RendererFactory { class WindowManager : public display::NativeDisplayObserver { public: - WindowManager(const base::Closure& quit_closure); + explicit WindowManager(const base::Closure& quit_closure); ~WindowManager() override; void Quit(); @@ -285,7 +285,6 @@ void WindowManager::OnConfigurationChanged() { } is_configuring_ = true; - delegate_->GrabServer(); delegate_->GetDisplays( base::Bind(&WindowManager::OnDisplaysAquired, base::Unretained(this))); } @@ -310,7 +309,6 @@ void WindowManager::OnDisplaysAquired( gfx::Rect(origin, display->native_mode()->size()))); origin.Offset(display->native_mode()->size().width(), 0); } - delegate_->UngrabServer(); is_configuring_ = false; if (should_configure_) { diff --git a/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc b/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc index f68bd5ddd70..2708ffc6085 100644 --- a/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc +++ b/chromium/ui/ozone/demo/surfaceless_gl_renderer.cc @@ -24,6 +24,36 @@ namespace ui { +namespace { + +OverlaySurfaceCandidate MakeOverlayCandidate(int z_order, + gfx::Rect bounds_rect, + gfx::RectF crop_rect) { + // The overlay checking interface is designed to satisfy the needs of CC which + // will be producing RectF target rectangles. But we use the bounds produced + // in RenderFrame for GLSurface::ScheduleOverlayPlane. + gfx::RectF display_rect(bounds_rect.x(), bounds_rect.y(), bounds_rect.width(), + bounds_rect.height()); + + OverlaySurfaceCandidate overlay_candidate; + + // The bounds rectangle of the candidate overlay buffer. + overlay_candidate.buffer_size = bounds_rect.size(); + // The same rectangle in floating point coordinates. + overlay_candidate.display_rect = display_rect; + + // Show the entire buffer by setting the crop to a unity square. + overlay_candidate.crop_rect = gfx::RectF(0, 0, 1, 1); + + // The demo overlay instance is always ontop and not clipped. Clipped quads + // cannot be placed in overlays. + overlay_candidate.is_clipped = false; + + return overlay_candidate; +} + +} // namespace + SurfacelessGlRenderer::BufferWrapper::BufferWrapper() { } @@ -107,66 +137,58 @@ bool SurfacelessGlRenderer::Initialize() { base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch("enable-overlay")) { gfx::Size overlay_size = gfx::Size(size_.width() / 8, size_.height() / 8); - overlay_buffer_.reset(new BufferWrapper()); - overlay_buffer_->Initialize(gfx::kNullAcceleratedWidget, overlay_size); - - glViewport(0, 0, overlay_size.width(), overlay_size.height()); - glClearColor(1.0, 1.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + for (size_t i = 0; i < arraysize(overlay_buffer_); ++i) { + overlay_buffer_[i].reset(new BufferWrapper()); + overlay_buffer_[i]->Initialize(gfx::kNullAcceleratedWidget, overlay_size); + + glViewport(0, 0, overlay_size.width(), overlay_size.height()); + glClearColor(i, 1.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } } + disable_primary_plane_ = command_line->HasSwitch("disable-primary-plane"); PostRenderFrameTask(gfx::SwapResult::SWAP_ACK); return true; } -// OverlayChecker demonstrates how to use the -// OverlayCandidatesOzone::CheckOverlaySupport to determine if a given overlay -// can be successfully displayed. -void SurfacelessGlRenderer::OverlayChecker(int z_order, - gfx::Rect bounds_rect, - gfx::RectF crop_rect) { - // The overlay checking interface is designed to satisfy the needs of CC which - // will be producing RectF target rectangles. But we use the bounds produced - // in RenderFrame for GLSurface::ScheduleOverlayPlane. - gfx::RectF display_rect(bounds_rect.x(), bounds_rect.y(), bounds_rect.width(), - bounds_rect.height()); - - OverlaySurfaceCandidate overlay_candidate; - - // The bounds rectangle of the candidate overlay buffer. - overlay_candidate.buffer_size = bounds_rect.size(); - // The same rectangle in floating point coordinates. - overlay_candidate.display_rect = display_rect; - - // Show the entire buffer by setting the crop to a unity square. - overlay_candidate.crop_rect = gfx::RectF(0, 0, 1, 1); - - // The clip region is the entire screen. - overlay_candidate.clip_rect = gfx::Rect(size_); - - // The demo overlay instance is always ontop and not clipped. Clipped quads - // cannot be placed in overlays. - overlay_candidate.is_clipped = false; - - OverlayCandidatesOzone::OverlaySurfaceCandidateList list; - list.push_back(overlay_candidate); - - // Ask ozone platform to determine if this rect can be placed in an overlay. - // Ozone will update the list and return it. - overlay_checker_->CheckOverlaySupport(&list); - - // Note what the checker decided. - // say more about it. - TRACE_EVENT2("hwoverlays", "SurfacelessGlRenderer::OverlayChecker", "canihaz", - list[0].overlay_handled, "display_rect", - list[0].display_rect.ToString()); -} - void SurfacelessGlRenderer::RenderFrame() { TRACE_EVENT0("ozone", "SurfacelessGlRenderer::RenderFrame"); float fraction = NextFraction(); + gfx::Rect overlay_rect; + + OverlayCandidatesOzone::OverlaySurfaceCandidateList overlay_list; + if (!disable_primary_plane_) { + overlay_list.push_back( + MakeOverlayCandidate(1, gfx::Rect(size_), gfx::RectF(0, 0, 1, 1))); + // We know at least the primary plane can be scanned out. + overlay_list.back().overlay_handled = true; + } + if (overlay_buffer_[0]) { + overlay_rect = gfx::Rect(overlay_buffer_[0]->size()); + + float steps_num = 5.0f; + float stepped_fraction = + std::floor((fraction + 0.5f / steps_num) * steps_num) / steps_num; + gfx::Vector2d offset( + stepped_fraction * (size_.width() - overlay_rect.width()), + (size_.height() - overlay_rect.height()) / 2); + overlay_rect += offset; + overlay_list.push_back( + MakeOverlayCandidate(1, overlay_rect, gfx::RectF(0, 0, 1, 1))); + } + + // The actual validation for a specific overlay configuration is done + // asynchronously and then cached inside overlay_checker_ once a reply + // is sent back. + // This means that the first few frames we call this method for a specific + // overlay_list, all the overlays but the primary plane, that we explicitly + // marked as handled, will be rejected even if they might be handled at a + // later time. + overlay_checker_->CheckOverlaySupport(&overlay_list); + context_->MakeCurrent(surface_.get()); buffers_[back_buffer_]->BindFramebuffer(); @@ -174,23 +196,17 @@ void SurfacelessGlRenderer::RenderFrame() { glClearColor(1 - fraction, 0.0, fraction, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - surface_->ScheduleOverlayPlane(0, gfx::OVERLAY_TRANSFORM_NONE, - buffers_[back_buffer_]->image(), - gfx::Rect(size_), gfx::RectF(0, 0, 1, 1)); - - if (overlay_buffer_) { - gfx::Rect overlay_rect(overlay_buffer_->size()); - gfx::Vector2d offset(fraction * (size_.width() - overlay_rect.width()), - (size_.height() - overlay_rect.height()) / 2); - overlay_rect += offset; - - // TODO(rjkroege): Overlay checking should gate the following and will - // be added after solving http://crbug.com/735640 - OverlayChecker(1, overlay_rect, gfx::RectF(0, 0, 1, 1)); + if (!disable_primary_plane_) { + CHECK(overlay_list.front().overlay_handled); + surface_->ScheduleOverlayPlane(0, gfx::OVERLAY_TRANSFORM_NONE, + buffers_[back_buffer_]->image(), + gfx::Rect(size_), gfx::RectF(0, 0, 1, 1)); + } + if (overlay_buffer_[0] && overlay_list.back().overlay_handled) { surface_->ScheduleOverlayPlane(1, gfx::OVERLAY_TRANSFORM_NONE, - overlay_buffer_->image(), overlay_rect, - gfx::RectF(0, 0, 1, 1)); + overlay_buffer_[back_buffer_]->image(), + overlay_rect, gfx::RectF(0, 0, 1, 1)); } back_buffer_ ^= 1; diff --git a/chromium/ui/ozone/demo/surfaceless_gl_renderer.h b/chromium/ui/ozone/demo/surfaceless_gl_renderer.h index 5467435c7f3..20e75cdee5b 100644 --- a/chromium/ui/ozone/demo/surfaceless_gl_renderer.h +++ b/chromium/ui/ozone/demo/surfaceless_gl_renderer.h @@ -32,7 +32,6 @@ class SurfacelessGlRenderer : public GlRenderer { // GlRenderer: void RenderFrame() override; void PostRenderFrameTask(gfx::SwapResult result) override; - void OverlayChecker(int z_order, gfx::Rect bounds_rect, gfx::RectF crop_rect); class BufferWrapper { public: @@ -57,7 +56,8 @@ class SurfacelessGlRenderer : public GlRenderer { std::unique_ptr<BufferWrapper> buffers_[2]; - std::unique_ptr<BufferWrapper> overlay_buffer_; + std::unique_ptr<BufferWrapper> overlay_buffer_[2]; + bool disable_primary_plane_ = false; std::unique_ptr<OverlayCandidatesOzone> overlay_checker_; 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 525ba66bb94..3b3eb346de5 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 @@ -40,7 +40,8 @@ class GLImageNativePixmapTestDelegate { surface_factory->CreateNativePixmap(gfx::kNullAcceleratedWidget, size, format, usage); DCHECK(pixmap); - if (usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE) { + if (usage == gfx::BufferUsage::GPU_READ_CPU_READ_WRITE || + usage == gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE) { auto client_pixmap = client_pixmap_factory_->ImportFromHandle( pixmap->ExportHandle(), size, usage); bool mapped = client_pixmap->Map(); @@ -67,7 +68,8 @@ class GLImageNativePixmapTestDelegate { const uint8_t* GetImageColor() { if (format == gfx::BufferFormat::R_8) { return kRed; - } else if (format == gfx::BufferFormat::YVU_420) { + } else if (format == gfx::BufferFormat::YVU_420 || + format == gfx::BufferFormat::YUV_420_BIPLANAR) { return kYvuColor; } return kGreen; @@ -87,7 +89,9 @@ INSTANTIATE_TYPED_TEST_CASE_P(GLImageNativePixmapScanout, using GLImageReadWriteType = testing::Types< GLImageNativePixmapTestDelegate<gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, - gfx::BufferFormat::R_8>>; + gfx::BufferFormat::R_8>, + GLImageNativePixmapTestDelegate<gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, + gfx::BufferFormat::YUV_420_BIPLANAR>>; using GLImageBindTestTypes = testing::Types< GLImageNativePixmapTestDelegate<gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, @@ -97,6 +101,8 @@ using GLImageBindTestTypes = testing::Types< GLImageNativePixmapTestDelegate<gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gfx::BufferFormat::YVU_420>, GLImageNativePixmapTestDelegate<gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, + gfx::BufferFormat::YUV_420_BIPLANAR>, + GLImageNativePixmapTestDelegate<gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gfx::BufferFormat::YUV_420_BIPLANAR>>; // These tests are disabled since the trybots are running with Ozone X11 diff --git a/chromium/ui/ozone/platform/cast/DEPS b/chromium/ui/ozone/platform/cast/DEPS index a967e61a1a2..ffc9eafa338 100644 --- a/chromium/ui/ozone/platform/cast/DEPS +++ b/chromium/ui/ozone/platform/cast/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+chromecast/base/cast_features.h", "+chromecast/base/chromecast_switches.h", "+chromecast/chromecast_features.h", "+chromecast/public" diff --git a/chromium/ui/ozone/platform/cast/gl_surface_cast.cc b/chromium/ui/ozone/platform/cast/gl_surface_cast.cc index 04d7d64c2d1..28822a35841 100644 --- a/chromium/ui/ozone/platform/cast/gl_surface_cast.cc +++ b/chromium/ui/ozone/platform/cast/gl_surface_cast.cc @@ -4,16 +4,25 @@ #include "ui/ozone/platform/cast/gl_surface_cast.h" +#include "base/feature_list.h" #include "base/memory/ptr_util.h" +#include "chromecast/base/cast_features.h" #include "ui/gfx/vsync_provider.h" #include "ui/ozone/common/egl_util.h" #include "ui/ozone/platform/cast/gl_ozone_egl_cast.h" namespace { -// Target fixed 30fps. +// Target fixed 30fps, or 60fps if doing triple-buffer 720p. // TODO(halliwell): We might need to customize this value on various devices // or make it dynamic that throttles framerate if device is overheating. -const base::TimeDelta kVSyncInterval = base::TimeDelta::FromSeconds(2) / 59.9; +base::TimeDelta GetVSyncInterval() { + if (base::FeatureList::IsEnabled(chromecast::kTripleBuffer720)) { + return base::TimeDelta::FromSeconds(1) / 59.9; + } else { + return base::TimeDelta::FromSeconds(2) / 59.9; + } +} + } // namespace namespace ui { @@ -23,7 +32,7 @@ GLSurfaceCast::GLSurfaceCast(gfx::AcceleratedWidget widget, : NativeViewGLSurfaceEGL( parent->GetNativeWindow(), base::MakeUnique<gfx::FixedVSyncProvider>(base::TimeTicks(), - kVSyncInterval)), + GetVSyncInterval())), widget_(widget), parent_(parent), supports_swap_buffer_with_bounds_( @@ -66,9 +75,11 @@ gfx::SwapResult GLSurfaceCast::SwapBuffersWithBounds( bool GLSurfaceCast::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { return parent_->ResizeDisplay(size) && - NativeViewGLSurfaceEGL::Resize(size, scale_factor, has_alpha); + NativeViewGLSurfaceEGL::Resize(size, scale_factor, color_space, + has_alpha); } bool GLSurfaceCast::ScheduleOverlayPlane(int z_order, diff --git a/chromium/ui/ozone/platform/cast/gl_surface_cast.h b/chromium/ui/ozone/platform/cast/gl_surface_cast.h index 2fb3fe98fcb..e93119af556 100644 --- a/chromium/ui/ozone/platform/cast/gl_surface_cast.h +++ b/chromium/ui/ozone/platform/cast/gl_surface_cast.h @@ -28,6 +28,7 @@ class GLSurfaceCast : public gl::NativeViewGLSurfaceEGL { const std::vector<gfx::Rect>& rects) override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool ScheduleOverlayPlane(int z_order, gfx::OverlayTransform transform, diff --git a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc index 6cc1392991a..522a837c062 100644 --- a/chromium/ui/ozone/platform/cast/surface_factory_cast.cc +++ b/chromium/ui/ozone/platform/cast/surface_factory_cast.cc @@ -29,8 +29,8 @@ class DummySurface : public SurfaceOzoneCanvas { sk_sp<SkSurface> GetSurface() override { return surface_; } void ResizeCanvas(const gfx::Size& viewport_size) override { - surface_ = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul( - viewport_size.width(), viewport_size.height())); + surface_ = + SkSurface::MakeNull(viewport_size.width(), viewport_size.height()); } void PresentCanvas(const gfx::Rect& damage) override {} diff --git a/chromium/ui/ozone/platform/drm/BUILD.gn b/chromium/ui/ozone/platform/drm/BUILD.gn index 05f649dac31..732e2d52f39 100644 --- a/chromium/ui/ozone/platform/drm/BUILD.gn +++ b/chromium/ui/ozone/platform/drm/BUILD.gn @@ -16,8 +16,6 @@ source_set("gbm") { "common/drm_util.h", "common/scoped_drm_types.cc", "common/scoped_drm_types.h", - "cursor_proxy_mojo.cc", - "cursor_proxy_mojo.h", "gpu/crtc_controller.cc", "gpu/crtc_controller.h", "gpu/drm_buffer.cc", @@ -103,8 +101,10 @@ source_set("gbm") { "host/drm_window_host_manager.h", "host/gpu_thread_adapter.h", "host/gpu_thread_observer.h", - "mus_thread_proxy.cc", - "mus_thread_proxy.h", + "host/host_cursor_proxy.cc", + "host/host_cursor_proxy.h", + "host/host_drm_device.cc", + "host/host_drm_device.h", "ozone_platform_gbm.cc", "ozone_platform_gbm.h", ] diff --git a/chromium/ui/ozone/platform/drm/OWNERS b/chromium/ui/ozone/platform/drm/OWNERS index 0d77c0eae94..363a93a5f95 100644 --- a/chromium/ui/ozone/platform/drm/OWNERS +++ b/chromium/ui/ozone/platform/drm/OWNERS @@ -1 +1,2 @@ +dcastagna@chromium.org dnicoara@chromium.org diff --git a/chromium/ui/ozone/platform/drm/common/display_types.h b/chromium/ui/ozone/platform/drm/common/display_types.h index 5d0b1acf653..da8b5c07533 100644 --- a/chromium/ui/ozone/platform/drm/common/display_types.h +++ b/chromium/ui/ozone/platform/drm/common/display_types.h @@ -6,13 +6,13 @@ #define UI_OZONE_PLATFORM_DRM_COMMON_DISPLAY_TYPES_H_ namespace display { -class DisplaySnapshotMojo; +class DisplaySnapshot; } // namespace display namespace ui { using MovableDisplaySnapshots = - std::vector<std::unique_ptr<display::DisplaySnapshotMojo>>; + std::vector<std::unique_ptr<display::DisplaySnapshot>>; } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.cc b/chromium/ui/ozone/platform/drm/common/drm_util.cc index 97130378d93..b452b33122b 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util.cc @@ -16,9 +16,8 @@ #include "base/containers/flat_map.h" #include "base/memory/ptr_util.h" #include "ui/display/types/display_mode.h" -#include "ui/display/types/display_snapshot_mojo.h" +#include "ui/display/types/display_snapshot.h" #include "ui/display/util/edid_parser.h" -#include "ui/ozone/common/display_snapshot_proxy.h" #if !defined(DRM_FORMAT_R16) // TODO(riju): crbug.com/733703 @@ -204,6 +203,12 @@ bool HasColorCorrectionMatrix(int fd, drmModeCrtc* crtc) { return false; } +bool DisplayModeEquals(const DisplayMode_Params& lhs, + const DisplayMode_Params& rhs) { + return lhs.size == rhs.size && lhs.is_interlaced == rhs.is_interlaced && + lhs.refresh_rate == rhs.refresh_rate; +} + } // namespace DisplayMode_Params GetDisplayModeParams(const display::DisplayMode& mode) { @@ -427,7 +432,6 @@ std::vector<DisplaySnapshot_Params> CreateParamsFromSnapshot( } p.product_id = d->product_id(); - p.string_representation = d->ToString(); p.maximum_cursor_size = d->maximum_cursor_size(); params.push_back(p); @@ -435,6 +439,29 @@ std::vector<DisplaySnapshot_Params> CreateParamsFromSnapshot( return params; } +std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshotFromParams( + const DisplaySnapshot_Params& params) { + display::DisplaySnapshot::DisplayModeList modes; + const display::DisplayMode* current_mode = nullptr; + const display::DisplayMode* native_mode = nullptr; + + // Find pointers to current and native mode in the copied data. + for (auto& mode : params.modes) { + modes.push_back(CreateDisplayModeFromParams(mode)); + if (params.has_current_mode && DisplayModeEquals(mode, params.current_mode)) + current_mode = modes.back().get(); + if (params.has_native_mode && DisplayModeEquals(mode, params.native_mode)) + native_mode = modes.back().get(); + } + + return std::make_unique<display::DisplaySnapshot>( + params.display_id, params.origin, params.physical_size, params.type, + params.is_aspect_preserving_scaling, params.has_overscan, + params.has_color_correction_matrix, params.display_name, params.sys_path, + std::move(modes), params.edid, current_mode, native_mode, + params.product_id, params.maximum_cursor_size); +} + int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format) { switch (format) { case gfx::BufferFormat::R_8: @@ -522,8 +549,57 @@ MovableDisplaySnapshots CreateMovableDisplaySnapshotsFromParams( const std::vector<DisplaySnapshot_Params>& displays) { MovableDisplaySnapshots snapshots; for (const auto& d : displays) - snapshots.push_back(base::MakeUnique<DisplaySnapshotProxy>(d)); + snapshots.push_back(CreateDisplaySnapshotFromParams(d)); return snapshots; } +OverlaySurfaceCandidateList CreateOverlaySurfaceCandidateListFrom( + const std::vector<OverlayCheck_Params>& params) { + OverlaySurfaceCandidateList candidates; + for (auto& p : params) { + OverlaySurfaceCandidate osc; + osc.transform = p.transform; + osc.buffer_size = p.buffer_size; + osc.format = p.format; + osc.display_rect = gfx::RectF(p.display_rect); + osc.crop_rect = p.crop_rect; + osc.plane_z_order = p.plane_z_order; + osc.overlay_handled = p.is_overlay_candidate; + candidates.push_back(osc); + } + + return candidates; +} + +std::vector<OverlayCheck_Params> CreateParamsFromOverlaySurfaceCandidate( + const OverlaySurfaceCandidateList& candidates) { + std::vector<OverlayCheck_Params> overlay_params; + for (auto& candidate : candidates) { + overlay_params.push_back(OverlayCheck_Params(candidate)); + } + + return overlay_params; +} + +OverlayStatusList CreateOverlayStatusListFrom( + const std::vector<OverlayCheckReturn_Params>& params) { + OverlayStatusList returns; + for (auto& p : params) { + returns.push_back(p.status); + } + + return returns; +} + +std::vector<OverlayCheckReturn_Params> CreateParamsFromOverlayStatusList( + const OverlayStatusList& returns) { + std::vector<OverlayCheckReturn_Params> params; + for (auto& s : returns) { + OverlayCheckReturn_Params p; + p.status = s; + params.push_back(p); + } + return params; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/common/drm_util.h b/chromium/ui/ozone/platform/drm/common/drm_util.h index 9fdfb016244..593be0c7333 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util.h +++ b/chromium/ui/ozone/platform/drm/common/drm_util.h @@ -12,7 +12,6 @@ #include "base/files/file_path.h" #include "base/macros.h" -#include "ui/display/types/display_mode.h" #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" #include "ui/ozone/platform/drm/common/display_types.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" @@ -21,6 +20,7 @@ typedef struct _drmModeModeInfo drmModeModeInfo; namespace display { class DisplayMode; +class DisplaySnapshot; } // namespace display namespace gfx { @@ -72,6 +72,9 @@ DisplaySnapshot_Params CreateDisplaySnapshotParams( std::vector<DisplaySnapshot_Params> CreateParamsFromSnapshot( const MovableDisplaySnapshots& displays); +std::unique_ptr<display::DisplaySnapshot> CreateDisplaySnapshotFromParams( + const DisplaySnapshot_Params& params); + int GetFourCCFormatFromBufferFormat(gfx::BufferFormat format); gfx::BufferFormat GetBufferFormatFromFourCCFormat(int format); @@ -96,6 +99,18 @@ bool ModeIsInterlaced(const drmModeModeInfo& mode); MovableDisplaySnapshots CreateMovableDisplaySnapshotsFromParams( const std::vector<DisplaySnapshot_Params>& displays); +OverlaySurfaceCandidateList CreateOverlaySurfaceCandidateListFrom( + const std::vector<OverlayCheck_Params>& params); + +std::vector<OverlayCheck_Params> CreateParamsFromOverlaySurfaceCandidate( + const OverlaySurfaceCandidateList& candidates); + +OverlayStatusList CreateOverlayStatusListFrom( + const std::vector<OverlayCheckReturn_Params>& params); + +std::vector<OverlayCheckReturn_Params> CreateParamsFromOverlayStatusList( + const OverlayStatusList& returns); + } // namespace ui #endif // UI_OZONE_PLATFORM_DRM_COMMON_DRM_UTIL_H_ 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 f8a4f5e1a04..88835c29d4e 100644 --- a/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc +++ b/chromium/ui/ozone/platform/drm/common/drm_util_unittest.cc @@ -4,8 +4,10 @@ #include "ui/ozone/platform/drm/common/drm_util.h" +#include <map> + #include "testing/gtest/include/gtest/gtest.h" -#include "ui/display/types/display_snapshot_mojo.h" +#include "ui/display/types/display_snapshot.h" #include "ui/gfx/geometry/size.h" #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" @@ -31,7 +33,6 @@ bool operator==(const ui::DisplaySnapshot_Params& a, 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.string_representation == b.string_representation && a.maximum_cursor_size == b.maximum_cursor_size; } @@ -63,7 +64,6 @@ void DetailedCompare(const ui::DisplaySnapshot_Params& a, 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.string_representation, b.string_representation); EXPECT_EQ(a.maximum_cursor_size, b.maximum_cursor_size); } @@ -104,7 +104,6 @@ TEST_F(DrmUtilTest, RoundTripDisplaySnapshot) { fp.has_native_mode = true; fp.native_mode = MakeDisplay(1.1); fp.product_id = 7; - fp.string_representation = "bending glass display"; fp.maximum_cursor_size = gfx::Size(103, 44); sp.display_id = 1002; @@ -123,7 +122,6 @@ TEST_F(DrmUtilTest, RoundTripDisplaySnapshot) { sp.has_native_mode = true; sp.native_mode = MakeDisplay(500.2); sp.product_id = 8; - sp.string_representation = "rigid glass display"; sp.maximum_cursor_size = gfx::Size(500, 44); ep.display_id = 2002; @@ -142,7 +140,6 @@ TEST_F(DrmUtilTest, RoundTripDisplaySnapshot) { ep.current_mode = MakeDisplay(1000.2); ep.has_native_mode = false; ep.product_id = 9; - ep.string_representation = "fluted glass display"; ep.maximum_cursor_size = gfx::Size(1000, 44); orig_params.push_back(fp); @@ -162,4 +159,46 @@ TEST_F(DrmUtilTest, RoundTripDisplaySnapshot) { EXPECT_EQ(ep, roundtrip_params[2]); } +TEST_F(DrmUtilTest, OverlaySurfaceCandidate) { + OverlaySurfaceCandidateList input; + + OverlaySurfaceCandidate input_osc; + input_osc.transform = gfx::OVERLAY_TRANSFORM_FLIP_VERTICAL; + input_osc.format = gfx::BufferFormat::YUV_420_BIPLANAR; + input_osc.buffer_size = gfx::Size(100, 50); + input_osc.display_rect = gfx::RectF(1., 2., 3., 4.); + input_osc.crop_rect = gfx::RectF(10., 20., 30., 40.); + input_osc.clip_rect = gfx::Rect(10, 20, 30, 40); + input_osc.is_clipped = true; + input_osc.plane_z_order = 42; + input_osc.overlay_handled = true; + + input.push_back(input_osc); + + // Roundtrip the conversions. + auto output = CreateOverlaySurfaceCandidateListFrom( + CreateParamsFromOverlaySurfaceCandidate(input)); + + EXPECT_EQ(input.size(), output.size()); + OverlaySurfaceCandidate output_osc = output[0]; + + EXPECT_EQ(input_osc.transform, output_osc.transform); + EXPECT_EQ(input_osc.format, output_osc.format); + EXPECT_EQ(input_osc.buffer_size, output_osc.buffer_size); + EXPECT_EQ(input_osc.display_rect, output_osc.display_rect); + EXPECT_EQ(input_osc.crop_rect, output_osc.crop_rect); + EXPECT_EQ(input_osc.plane_z_order, output_osc.plane_z_order); + EXPECT_EQ(input_osc.overlay_handled, output_osc.overlay_handled); + + EXPECT_FALSE(input < output); + EXPECT_FALSE(output < input); + + std::map<OverlaySurfaceCandidateList, int> map; + map[input] = 42; + const auto& iter = map.find(output); + + EXPECT_NE(map.end(), iter); + EXPECT_EQ(42, iter->second); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc index 68d504aaecd..ae4f9b02266 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc @@ -81,20 +81,19 @@ bool CrtcController::SchedulePageFlip( DCHECK(!page_flip_request_.get() || test_only); DCHECK(!is_disabled_); const OverlayPlane* primary = OverlayPlane::GetPrimaryPlane(overlays); - if (!primary) { - LOG(ERROR) << "No primary plane to display on crtc " << crtc_; - page_flip_request->Signal(gfx::SwapResult::SWAP_ACK); - return true; - } - DCHECK(primary->buffer.get()); - - if (primary->buffer->GetSize() != gfx::Size(mode_.hdisplay, mode_.vdisplay)) { - VLOG(2) << "Trying to pageflip a buffer with the wrong size. Expected " - << mode_.hdisplay << "x" << mode_.vdisplay << " got " - << primary->buffer->GetSize().ToString() << " for" - << " crtc=" << crtc_ << " connector=" << connector_; - page_flip_request->Signal(gfx::SwapResult::SWAP_ACK); - return true; + if (primary) { + DCHECK(primary->buffer.get()); + // TODO(dcastagna): Get rid of this. Scaling on the primary plane is + // supported on all the devices. + if (primary->buffer->GetSize() != + gfx::Size(mode_.hdisplay, mode_.vdisplay)) { + VLOG(2) << "Trying to pageflip a buffer with the wrong size. Expected " + << mode_.hdisplay << "x" << mode_.vdisplay << " got " + << primary->buffer->GetSize().ToString() << " for" + << " crtc=" << crtc_ << " connector=" << connector_; + page_flip_request->Signal(gfx::SwapResult::SWAP_ACK); + return true; + } } if (!drm_->plane_manager()->AssignOverlayPlanes(plane_list, overlays, crtc_, @@ -125,12 +124,8 @@ std::vector<uint64_t> CrtcController::GetFormatModifiers(uint32_t format) { } void CrtcController::OnPageFlipEvent(unsigned int frame, - unsigned int seconds, - unsigned int useconds) { - time_of_last_flip_ = - static_cast<uint64_t>(seconds) * base::Time::kMicrosecondsPerSecond + - useconds; - + base::TimeTicks timestamp) { + time_of_last_flip_ = timestamp; SignalPageFlipRequest(gfx::SwapResult::SWAP_ACK); } diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h index 1bdae0385b0..5222b63ee36 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "base/time/time.h" #include "ui/gfx/swap_result.h" #include "ui/ozone/platform/drm/common/scoped_drm_types.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" @@ -39,7 +40,7 @@ class CrtcController : public base::SupportsWeakPtr<CrtcController> { uint32_t connector() const { return connector_; } const scoped_refptr<DrmDevice>& drm() const { return drm_; } bool is_disabled() const { return is_disabled_; } - uint64_t time_of_last_flip() const { return time_of_last_flip_; } + base::TimeTicks time_of_last_flip() const { return time_of_last_flip_; } // Perform the initial modesetting operation using |plane| as the buffer for // the primary plane. The CRTC configuration is specified by |mode|. @@ -76,12 +77,7 @@ class CrtcController : public base::SupportsWeakPtr<CrtcController> { // Called when the page flip event occurred. The event is provided by the // kernel when a VBlank event finished. This allows the controller to // update internal state and propagate the update to the surface. - // The tuple (seconds, useconds) represents the event timestamp. |seconds| - // represents the number of seconds while |useconds| represents the - // microseconds (< 1 second) in the timestamp. - void OnPageFlipEvent(unsigned int frame, - unsigned int seconds, - unsigned int useconds); + void OnPageFlipEvent(unsigned int frame, base::TimeTicks timestamp); bool SetCursor(const scoped_refptr<ScanoutBuffer>& buffer); bool MoveCursor(const gfx::Point& location); @@ -110,7 +106,7 @@ class CrtcController : public base::SupportsWeakPtr<CrtcController> { bool is_disabled_ = true; // The time of the last page flip event as reported by the kernel callback. - uint64_t time_of_last_flip_ = 0; + base::TimeTicks time_of_last_flip_; DISALLOW_COPY_AND_ASSIGN(CrtcController); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc index ed132f15e55..315b1c0879e 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc @@ -30,10 +30,9 @@ namespace ui { namespace { -typedef base::Callback<void(uint32_t /* frame */, - uint32_t /* seconds */, - uint32_t /* useconds */, - uint64_t /* id */)> DrmEventHandler; +using DrmEventHandler = base::Callback<void(uint32_t /* frame */, + base::TimeTicks /* timestamp */, + uint64_t /* id */)>; bool DrmCreateDumbBuffer(int fd, const SkImageInfo& info, @@ -97,8 +96,17 @@ bool ProcessDrmEvent(int fd, const DrmEventHandler& callback) { TRACE_EVENT_INSTANT1("benchmark,drm", "DrmEventFlipComplete", TRACE_EVENT_SCOPE_THREAD, "data", std::move(drm_data)); - callback.Run(vblank.sequence, vblank.tv_sec, vblank.tv_usec, - vblank.user_data); + // Warning: It is generally unsafe to manufacture TimeTicks values; but + // here it is required for interfacing with libdrm. Assumption: libdrm + // is providing the timestamp from the CLOCK_MONOTONIC POSIX clock. + DCHECK_EQ(base::TimeTicks::GetClock(), + base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC); + const base::TimeTicks timestamp = + base::TimeTicks() + base::TimeDelta::FromMicroseconds( + static_cast<int64_t>(vblank.tv_sec) * + base::Time::kMicrosecondsPerSecond + + vblank.tv_usec); + callback.Run(vblank.sequence, timestamp, vblank.user_data); } break; case DRM_EVENT_VBLANK: break; @@ -298,10 +306,7 @@ class DrmDevice::PageFlipManager { PageFlipManager() : next_id_(0) {} ~PageFlipManager() {} - void OnPageFlip(uint32_t frame, - uint32_t seconds, - uint32_t useconds, - uint64_t id) { + void OnPageFlip(uint32_t frame, base::TimeTicks timestamp, uint64_t id) { auto it = std::find_if(callbacks_.begin(), callbacks_.end(), FindCallback(id)); if (it == callbacks_.end()) { @@ -315,7 +320,7 @@ class DrmDevice::PageFlipManager { return; callbacks_.erase(it); - callback.Run(frame, seconds, useconds); + callback.Run(frame, timestamp); } uint64_t GetNextId() { return next_id_++; } diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.h b/chromium/ui/ozone/platform/drm/gpu/drm_device.h index 132b5dc5ec2..4e197848279 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.h @@ -15,6 +15,7 @@ #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/time/time.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/overlay_transform.h" @@ -39,9 +40,9 @@ class HardwareDisplayPlaneManager; // would be called. In unit tests this interface would be stubbed. class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { public: - typedef base::Callback<void(unsigned int /* frame */, - unsigned int /* seconds */, - unsigned int /* useconds */)> PageFlipCallback; + using PageFlipCallback = + base::Callback<void(unsigned int /* frame */, + base::TimeTicks /* timestamp */)>; DrmDevice(const base::FilePath& device_path, base::File file, diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc index 6b8142bc7df..da86d3bde63 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc @@ -38,8 +38,7 @@ DrmDeviceManager::~DrmDeviceManager() { } bool DrmDeviceManager::AddDrmDevice(const base::FilePath& path, - const base::FileDescriptor& fd) { - base::File file(fd.fd); + base::File file) { auto it = std::find_if(devices_.begin(), devices_.end(), FindByDevicePath(path)); if (it != devices_.end()) { diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h index 52f59ad845b..a1241b7d92d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h @@ -9,13 +9,13 @@ #include <memory> #include <vector> +#include "base/files/file.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "ui/gfx/native_widget_types.h" namespace base { class FilePath; -struct FileDescriptor; } namespace ui { @@ -33,7 +33,7 @@ class DrmDeviceManager { ~DrmDeviceManager(); // The first device registered is assumed to be the primary device. - bool AddDrmDevice(const base::FilePath& path, const base::FileDescriptor& fd); + bool AddDrmDevice(const base::FilePath& path, base::File file); void RemoveDrmDevice(const base::FilePath& path); // Updates the device associated with |widget|. diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc index eb333ee7d06..f8d5daa2aeb 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc @@ -14,8 +14,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" #include "ui/display/types/display_mode.h" -#include "ui/display/types/display_snapshot_mojo.h" -#include "ui/ozone/common/display_snapshot_proxy.h" +#include "ui/display/types/display_snapshot.h" #include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/drm_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_device_generator.h" @@ -77,7 +76,7 @@ class GbmDeviceGenerator : public DrmDeviceGenerator { } // namespace -DrmThread::DrmThread() : base::Thread("DrmThread") {} +DrmThread::DrmThread() : base::Thread("DrmThread"), binding_(this) {} DrmThread::~DrmThread() { Stop(); @@ -117,16 +116,24 @@ void DrmThread::CreateBuffer(gfx::AcceleratedWidget widget, uint32_t flags = 0; switch (usage) { case gfx::BufferUsage::GPU_READ: + flags = GBM_BO_USE_TEXTURING; break; case gfx::BufferUsage::SCANOUT: - flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; + break; + case gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE: + flags = GBM_BO_USE_LINEAR | GBM_BO_USE_CAMERA_WRITE | GBM_BO_USE_SCANOUT | + GBM_BO_USE_TEXTURING; break; case gfx::BufferUsage::SCANOUT_CPU_READ_WRITE: - flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR; + flags = GBM_BO_USE_LINEAR | GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; + break; + case gfx::BufferUsage::SCANOUT_VDA_WRITE: + flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; break; case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE: case gfx::BufferUsage::GPU_READ_CPU_READ_WRITE_PERSISTENT: - flags = GBM_BO_USE_LINEAR; + flags = GBM_BO_USE_LINEAR | GBM_BO_USE_TEXTURING; break; } @@ -140,11 +147,21 @@ void DrmThread::CreateBuffer(gfx::AcceleratedWidget widget, if (window && window->GetController()) modifiers = window->GetController()->GetFormatModifiers(fourcc_format); - if (modifiers.size() > 0 && !(flags & GBM_BO_USE_LINEAR)) - *buffer = GbmBuffer::CreateBufferWithModifiers(gbm, fourcc_format, size, - flags, modifiers); - else - *buffer = GbmBuffer::CreateBuffer(gbm, fourcc_format, size, flags); + // NOTE: BufferUsage::SCANOUT is used to create buffers that will be + // explicitly set via kms on a CRTC (e.g: BufferQueue buffers), therefore + // allocation should fail if it's not possible to allocate a BO_USE_SCANOUT + // buffer in that case. + bool retry_without_scanout = usage != gfx::BufferUsage::SCANOUT; + do { + if (modifiers.size() > 0 && !(flags & GBM_BO_USE_LINEAR)) + *buffer = GbmBuffer::CreateBufferWithModifiers(gbm, fourcc_format, size, + flags, modifiers); + else + *buffer = GbmBuffer::CreateBuffer(gbm, fourcc_format, size, flags); + retry_without_scanout = + retry_without_scanout && !*buffer && (flags & GBM_BO_USE_SCANOUT); + flags &= ~GBM_BO_USE_SCANOUT; + } while (retry_without_scanout); } void DrmThread::CreateBufferFromFds( @@ -189,19 +206,19 @@ void DrmThread::GetVSyncParameters( window->GetVSyncParameters(callback); } -void DrmThread::CreateWindow(gfx::AcceleratedWidget widget) { +void DrmThread::CreateWindow(const gfx::AcceleratedWidget& widget) { std::unique_ptr<DrmWindow> window( new DrmWindow(widget, device_manager_.get(), screen_manager_.get())); window->Initialize(buffer_generator_.get()); screen_manager_->AddWindow(widget, std::move(window)); } -void DrmThread::DestroyWindow(gfx::AcceleratedWidget widget) { +void DrmThread::DestroyWindow(const gfx::AcceleratedWidget& widget) { std::unique_ptr<DrmWindow> window = screen_manager_->RemoveWindow(widget); window->Shutdown(); } -void DrmThread::SetWindowBounds(gfx::AcceleratedWidget widget, +void DrmThread::SetWindowBounds(const gfx::AcceleratedWidget& widget, const gfx::Rect& bounds) { screen_manager_->GetWindow(widget)->SetBounds(bounds); } @@ -220,17 +237,18 @@ void DrmThread::MoveCursor(const gfx::AcceleratedWidget& widget, } void DrmThread::CheckOverlayCapabilities( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays, - base::OnceCallback<void(gfx::AcceleratedWidget, - const std::vector<OverlayCheck_Params>&, - const std::vector<OverlayCheckReturn_Params>&)> - callback) { + const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& overlays, + base::OnceCallback<void(const gfx::AcceleratedWidget&, + const OverlaySurfaceCandidateList&, + const OverlayStatusList&)> callback) { TRACE_EVENT0("drm,hwoverlays", "DrmThread::CheckOverlayCapabilities"); + auto params = CreateParamsFromOverlaySurfaceCandidate(overlays); std::move(callback).Run( widget, overlays, - screen_manager_->GetWindow(widget)->TestPageFlip(overlays)); + CreateOverlayStatusListFrom( + screen_manager_->GetWindow(widget)->TestPageFlip(params))); } void DrmThread::RefreshNativeDisplays( @@ -265,9 +283,8 @@ void DrmThread::RelinquishDisplayControl( std::move(callback).Run(true); } -void DrmThread::AddGraphicsDevice(const base::FilePath& path, - const base::FileDescriptor& fd) { - device_manager_->AddDrmDevice(path, fd); +void DrmThread::AddGraphicsDevice(const base::FilePath& path, base::File file) { + device_manager_->AddDrmDevice(path, std::move(file)); } void DrmThread::RemoveGraphicsDevice(const base::FilePath& path) { @@ -298,10 +315,24 @@ void DrmThread::SetColorCorrection( correction_matrix); } +void DrmThread::StartDrmDevice(StartDrmDeviceCallback callback) { + // We currently assume that |Init| always succeeds so return true to indicate + // when the DRM thread has completed launching. In particular, the invocation + // of the callback in the client triggers the invocation of DRM thread + // readiness observers. + std::move(callback).Run(true); +} + // DrmThread requires a BindingSet instead of a simple Binding because it will // be used from multiple threads in multiple processes. -void DrmThread::AddBinding(ozone::mojom::DeviceCursorRequest request) { +void DrmThread::AddBindingCursorDevice( + ozone::mojom::DeviceCursorRequest request) { bindings_.AddBinding(this, std::move(request)); } +void DrmThread::AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request) { + TRACE_EVENT0("drm", "DrmThread::AddBindingDrmDevice"); + binding_.Bind(std::move(request)); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h index 8fdecc94db7..973409b073d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h @@ -9,6 +9,7 @@ #include <memory> +#include "base/files/file.h" #include "base/files/scoped_file.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" @@ -20,10 +21,11 @@ #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" #include "ui/ozone/platform/drm/common/display_types.h" #include "ui/ozone/public/interfaces/device_cursor.mojom.h" +#include "ui/ozone/public/interfaces/drm_device.mojom.h" #include "ui/ozone/public/swap_completion_callback.h" namespace base { -struct FileDescriptor; +class FilePath; } namespace display { @@ -53,14 +55,17 @@ struct OverlayPlane; // (for example jank in the cursor if the GPU main thread is performing heavy // operations). The inverse is also true as blocking operations on the DRM // thread (such as modesetting) no longer block the GPU main thread. -class DrmThread : public base::Thread, public ozone::mojom::DeviceCursor { +class DrmThread : public base::Thread, + public ozone::mojom::DeviceCursor, + public ozone::mojom::DrmDevice { public: DrmThread(); ~DrmThread() override; void Start(); - // Must be called on the DRM thread. + // Must be called on the DRM thread. All methods for use from the GPU thread. + // DrmThreadProxy (on GPU)thread) is the client for these methods. void CreateBuffer(gfx::AcceleratedWidget widget, const gfx::Size& size, gfx::BufferFormat format, @@ -72,9 +77,12 @@ class DrmThread : public base::Thread, public ozone::mojom::DeviceCursor { std::vector<base::ScopedFD>&& fds, const std::vector<gfx::NativePixmapPlane>& planes, scoped_refptr<GbmBuffer>* buffer); - void GetScanoutFormats(gfx::AcceleratedWidget widget, std::vector<gfx::BufferFormat>* scanout_formats); + void AddBindingCursorDevice(ozone::mojom::DeviceCursorRequest request); + void AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request); + + // DrmWindowProxy (on GPU thread) is the client for these methods. void SchedulePageFlip(gfx::AcceleratedWidget widget, const std::vector<OverlayPlane>& planes, SwapCompletionOnceCallback callback); @@ -82,53 +90,56 @@ class DrmThread : public base::Thread, public ozone::mojom::DeviceCursor { gfx::AcceleratedWidget widget, const gfx::VSyncProvider::UpdateVSyncCallback& callback); - void CreateWindow(gfx::AcceleratedWidget widget); - void DestroyWindow(gfx::AcceleratedWidget widget); - void SetWindowBounds(gfx::AcceleratedWidget widget, const gfx::Rect& bounds); - void SetCursor(const gfx::AcceleratedWidget& widget, - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& location, - int32_t frame_delay_ms) override; - void MoveCursor(const gfx::AcceleratedWidget& widget, - const gfx::Point& location) override; - void CheckOverlayCapabilities( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays, - base::OnceCallback<void(gfx::AcceleratedWidget, - const std::vector<OverlayCheck_Params>&, - const std::vector<OverlayCheckReturn_Params>&)> - callback); + // ozone::mojom::DrmDevice + void StartDrmDevice(StartDrmDeviceCallback callback) override; + void CreateWindow(const gfx::AcceleratedWidget& widget) override; + void DestroyWindow(const gfx::AcceleratedWidget& widget) override; + void SetWindowBounds(const gfx::AcceleratedWidget& widget, + const gfx::Rect& bounds) override; + void TakeDisplayControl(base::OnceCallback<void(bool)> callback) override; + void RelinquishDisplayControl( + base::OnceCallback<void(bool)> callback) override; void RefreshNativeDisplays( - base::OnceCallback<void(MovableDisplaySnapshots)> callback); - void ConfigureNativeDisplay(int64_t id, - std::unique_ptr<display::DisplayMode> mode, - const gfx::Point& origin, - base::OnceCallback<void(int64_t, bool)> callback); - void DisableNativeDisplay(int64_t id, - base::OnceCallback<void(int64_t, bool)> callback); - void TakeDisplayControl(base::OnceCallback<void(bool)> callback); - void RelinquishDisplayControl(base::OnceCallback<void(bool)> callback); - void AddGraphicsDevice(const base::FilePath& path, - const base::FileDescriptor& fd); - void RemoveGraphicsDevice(const base::FilePath& path); - void GetHDCPState( - int64_t display_id, - base::OnceCallback<void(int64_t, bool, display::HDCPState)> callback); + base::OnceCallback<void(MovableDisplaySnapshots)> callback) override; + void AddGraphicsDevice(const base::FilePath& path, base::File file) override; + void RemoveGraphicsDevice(const base::FilePath& path) override; + void DisableNativeDisplay( + int64_t id, + base::OnceCallback<void(int64_t, bool)> callback) override; + void ConfigureNativeDisplay( + int64_t id, + std::unique_ptr<display::DisplayMode> mode, + const gfx::Point& origin, + base::OnceCallback<void(int64_t, bool)> callback) override; + void GetHDCPState(int64_t display_id, + base::OnceCallback<void(int64_t, bool, display::HDCPState)> + callback) override; void SetHDCPState(int64_t display_id, display::HDCPState state, - base::OnceCallback<void(int64_t, bool)> callback); + base::OnceCallback<void(int64_t, bool)> callback) override; void SetColorCorrection( int64_t display_id, const std::vector<display::GammaRampRGBEntry>& degamma_lut, const std::vector<display::GammaRampRGBEntry>& gamma_lut, - const std::vector<float>& correction_matrix); + const std::vector<float>& correction_matrix) override; + void CheckOverlayCapabilities( + const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& overlays, + base::OnceCallback<void(const gfx::AcceleratedWidget&, + const OverlaySurfaceCandidateList&, + const OverlayStatusList&)> callback) override; + + // ozone::mojom::DeviceCursor + void SetCursor(const gfx::AcceleratedWidget& widget, + const std::vector<SkBitmap>& bitmaps, + const gfx::Point& location, + int32_t frame_delay_ms) override; + void MoveCursor(const gfx::AcceleratedWidget& widget, + const gfx::Point& location) override; // base::Thread: void Init() override; - // Mojo support for DeviceCursorRequest. - void AddBinding(ozone::mojom::DeviceCursorRequest request); - private: std::unique_ptr<DrmDeviceManager> device_manager_; std::unique_ptr<ScanoutBufferGenerator> buffer_generator_; @@ -139,6 +150,9 @@ class DrmThread : public base::Thread, public ozone::mojom::DeviceCursor { // requests from two different client threads. mojo::BindingSet<ozone::mojom::DeviceCursor> bindings_; + // The mojo implementation of DrmDevice can use a simple binding. + mojo::Binding<ozone::mojom::DrmDevice> binding_; + DISALLOW_COPY_AND_ASSIGN(DrmThread); }; 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 b1add6a3d74..fb46660ae63 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 @@ -8,7 +8,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "ipc/ipc_message.h" #include "ipc/ipc_sender.h" -#include "ui/display/types/display_snapshot_mojo.h" +#include "ui/display/types/display_snapshot.h" #include "ui/ozone/common/gpu/ozone_gpu_messages.h" #include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/drm_thread_proxy.h" @@ -109,8 +109,9 @@ void DrmThreadMessageProxy::OnCursorMove(gfx::AcceleratedWidget widget, void DrmThreadMessageProxy::OnCheckOverlayCapabilities( gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays) { + const std::vector<OverlayCheck_Params>& param_overlays) { DCHECK(drm_thread_->IsRunning()); + auto overlays = CreateOverlaySurfaceCandidateListFrom(param_overlays); auto callback = base::BindOnce(&DrmThreadMessageProxy::OnCheckOverlayCapabilitiesCallback, weak_ptr_factory_.GetWeakPtr()); @@ -191,9 +192,11 @@ void DrmThreadMessageProxy::OnAddGraphicsDevice( const base::FilePath& path, const base::FileDescriptor& fd) { DCHECK(drm_thread_->IsRunning()); + base::File file(fd.fd); drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::AddGraphicsDevice, - base::Unretained(drm_thread_), path, fd)); + FROM_HERE, + base::Bind(&DrmThread::AddGraphicsDevice, base::Unretained(drm_thread_), + path, Passed(&file))); } void DrmThreadMessageProxy::OnRemoveGraphicsDevice(const base::FilePath& path) { @@ -239,11 +242,13 @@ void DrmThreadMessageProxy::OnSetColorCorrection( } void DrmThreadMessageProxy::OnCheckOverlayCapabilitiesCallback( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays, - const std::vector<OverlayCheckReturn_Params>& returns) const { - sender_->Send( - new OzoneHostMsg_OverlayCapabilitiesReceived(widget, overlays, returns)); + const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& candidates, + const OverlayStatusList& returns) const { + auto param_overlays = CreateParamsFromOverlaySurfaceCandidate(candidates); + auto param_returns = CreateParamsFromOverlayStatusList(returns); + sender_->Send(new OzoneHostMsg_OverlayCapabilitiesReceived( + widget, param_overlays, param_returns)); } void DrmThreadMessageProxy::OnRefreshNativeDisplaysCallback( diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h index da749ed08e9..c1ec3150c6f 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h @@ -15,6 +15,7 @@ #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/drm/common/display_types.h" #include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h" +#include "ui/ozone/public/overlay_surface_candidate.h" namespace base { struct FileDescriptor; @@ -30,7 +31,6 @@ namespace ui { class DrmThread; struct DisplayMode_Params; struct OverlayCheck_Params; -struct OverlayCheckReturn_Params; class DrmThreadMessageProxy : public IPC::MessageFilter, public InterThreadMessagingProxy { @@ -80,9 +80,9 @@ class DrmThreadMessageProxy : public IPC::MessageFilter, const std::vector<float>& correction_matrix); void OnCheckOverlayCapabilitiesCallback( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays, - const std::vector<OverlayCheckReturn_Params>& returns) const; + const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& overlays, + const OverlayStatusList& returns) const; void OnRefreshNativeDisplaysCallback(MovableDisplaySnapshots displays) const; void OnConfigureNativeDisplayCallback(int64_t display_id, bool success) const; void OnDisableNativeDisplayCallback(int64_t display_id, bool success) const; 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 dccef01e783..8d15f12294c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.cc @@ -22,6 +22,10 @@ void DrmThreadProxy::BindThreadIntoMessagingProxy( messaging_proxy->SetDrmThread(&drm_thread_); } +void DrmThreadProxy::StartDrmThread() { + drm_thread_.Start(); +} + std::unique_ptr<DrmWindowProxy> DrmThreadProxy::CreateDrmWindowProxy( gfx::AcceleratedWidget widget) { return base::MakeUnique<DrmWindowProxy>(widget, &drm_thread_); @@ -63,11 +67,20 @@ void DrmThreadProxy::GetScanoutFormats( widget, scanout_formats)); } -void DrmThreadProxy::AddBinding(ozone::mojom::DeviceCursorRequest request) { +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))); +} + +void DrmThreadProxy::AddBindingDrmDevice( + ozone::mojom::DrmDeviceRequest request) { drm_thread_.task_runner()->PostTask( FROM_HERE, - base::Bind(&DrmThread::AddBinding, base::Unretained(&drm_thread_), - base::Passed(&request))); + base::Bind(&DrmThread::AddBindingDrmDevice, + base::Unretained(&drm_thread_), base::Passed(&request))); } } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h index 4ae9ca2de11..c8a0b4b9c0b 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_proxy.h @@ -27,6 +27,8 @@ class DrmThreadProxy { void BindThreadIntoMessagingProxy(InterThreadMessagingProxy* messaging_proxy); + void StartDrmThread(); + std::unique_ptr<DrmWindowProxy> CreateDrmWindowProxy( gfx::AcceleratedWidget widget); @@ -45,7 +47,8 @@ class DrmThreadProxy { void GetScanoutFormats(gfx::AcceleratedWidget widget, std::vector<gfx::BufferFormat>* scanout_formats); - void AddBinding(ozone::mojom::DeviceCursorRequest request); + void AddBindingCursorDevice(ozone::mojom::DeviceCursorRequest request); + void AddBindingDrmDevice(ozone::mojom::DrmDeviceRequest request); private: DrmThread drm_thread_; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc index 29447bc6307..82b86176fe6 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc @@ -162,18 +162,11 @@ void DrmWindow::GetVSyncParameters( // If we're in mirror mode the 2 CRTCs should have similar modes with the same // refresh rates. CrtcController* crtc = controller_->crtc_controllers()[0].get(); - // The value is invalid, so we can't update the parameters. - if (controller_->GetTimeOfLastFlip() == 0 || crtc->mode().vrefresh == 0) - return; - - // Stores the time of the last refresh. - base::TimeTicks timebase = - base::TimeTicks::FromInternalValue(controller_->GetTimeOfLastFlip()); - // Stores the refresh rate. - base::TimeDelta interval = - base::TimeDelta::FromSeconds(1) / crtc->mode().vrefresh; - - callback.Run(timebase, interval); + const base::TimeTicks last_flip = controller_->GetTimeOfLastFlip(); + if (last_flip == base::TimeTicks() || crtc->mode().vrefresh == 0) + return; // The value is invalid, so we can't update the parameters. + callback.Run(last_flip, + base::TimeDelta::FromSeconds(1) / crtc->mode().vrefresh); } void DrmWindow::ResetCursor(bool bitmap_only) { diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc index d9b42c934b7..624bcc99d48 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_buffer.cc @@ -259,8 +259,9 @@ scoped_refptr<GbmBuffer> GbmBuffer::CreateBufferFromFds( DCHECK_EQ(planes[0].offset, 0); // Try to use scanout if supported. - bool try_scanout = gbm_device_is_format_supported( - gbm->device(), format, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + int gbm_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING; + bool try_scanout = + gbm_device_is_format_supported(gbm->device(), format, gbm_flags); gbm_bo* bo = nullptr; if (try_scanout) { @@ -280,18 +281,18 @@ scoped_refptr<GbmBuffer> GbmBuffer::CreateBufferFromFds( // The fd passed to gbm_bo_import is not ref-counted and need to be // kept open for the lifetime of the buffer. bo = gbm_bo_import(gbm->device(), GBM_BO_IMPORT_FD_PLANAR, &fd_data, - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + gbm_flags); if (!bo) { LOG(ERROR) << "nullptr returned from gbm_bo_import"; return nullptr; } + } else { + gbm_flags &= ~GBM_BO_USE_SCANOUT; } - uint32_t flags = GBM_BO_USE_RENDERING; - if (try_scanout) - flags |= GBM_BO_USE_SCANOUT; - scoped_refptr<GbmBuffer> buffer(new GbmBuffer( - gbm, bo, format, flags, 0, 0, std::move(fds), size, std::move(planes))); + scoped_refptr<GbmBuffer> buffer(new GbmBuffer(gbm, bo, format, gbm_flags, 0, + 0, std::move(fds), size, + std::move(planes))); 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 649f4b051fb..a0cebe6ab50 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc @@ -46,12 +46,14 @@ bool GbmSurface::OnMakeCurrent(gl::GLContext* context) { bool GbmSurface::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { if (size == GetSize()) return true; // Alpha value isn't actually used in allocating buffers yet, so always use // true instead. - return GbmSurfaceless::Resize(size, scale_factor, true) && CreatePixmaps(); + return GbmSurfaceless::Resize(size, scale_factor, color_space, true) && + CreatePixmaps(); } bool GbmSurface::SupportsPostSubBuffer() { diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surface.h index ba38c01ac0d..fedff501c49 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface.h @@ -31,6 +31,7 @@ class GbmSurface : public GbmSurfaceless { bool OnMakeCurrent(gl::GLContext* context) override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; bool SupportsPostSubBuffer() override; void SwapBuffersAsync(const SwapCompletionCallback& callback) 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 46218658981..37f9b17c6c6 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc @@ -72,6 +72,13 @@ void HardwareDisplayController::Disable() { for (const auto& controller : crtc_controllers_) controller->Disable(); + for (const auto& planes : owned_hardware_planes_) { + DrmDevice* drm = planes.first; + HardwareDisplayPlaneList* plane_list = planes.second.get(); + bool ret = drm->plane_manager()->DisableOverlayPlanes(plane_list); + LOG_IF(ERROR, !ret) << "Can't disable overlays when disabling HDC."; + } + is_disabled_ = true; } @@ -107,7 +114,7 @@ bool HardwareDisplayController::ActualSchedulePageFlip( [](const OverlayPlane& l, const OverlayPlane& r) { return l.z_order < r.z_order; }); - if (pending_planes.front().z_order != 0) { + if (pending_planes.front().z_order < 0) { std::move(callback).Run(gfx::SwapResult::SWAP_FAILED); return false; } @@ -283,8 +290,8 @@ gfx::Size HardwareDisplayController::GetModeSize() const { crtc_controllers_[0]->mode().vdisplay); } -uint64_t HardwareDisplayController::GetTimeOfLastFlip() const { - uint64_t time = 0; +base::TimeTicks HardwareDisplayController::GetTimeOfLastFlip() const { + base::TimeTicks time; for (const auto& controller : crtc_controllers_) { if (time < controller->time_of_last_flip()) time = controller->time_of_last_flip(); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h index 31777d2c932..c80aba42352 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h @@ -17,6 +17,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/time/time.h" #include "ui/gfx/swap_result.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" #include "ui/ozone/platform/drm/gpu/overlay_plane.h" @@ -149,7 +150,7 @@ class HardwareDisplayController { gfx::Point origin() const { return origin_; } void set_origin(const gfx::Point& origin) { origin_ = origin; } - uint64_t GetTimeOfLastFlip() const; + base::TimeTicks GetTimeOfLastFlip() const; const std::vector<std::unique_ptr<CrtcController>>& crtc_controllers() const { return crtc_controllers_; 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 0dbd989f9c8..05da38c50f3 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 @@ -400,7 +400,7 @@ TEST_F(HardwareDisplayControllerTest, FailPageFlipping) { EXPECT_EQ(1, page_flips_); } -TEST_F(HardwareDisplayControllerTest, FailPageFlippingDueToNoPrimaryPlane) { +TEST_F(HardwareDisplayControllerTest, CheckNoPrimaryPlane) { ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>( new ui::MockScanoutBuffer(kDefaultModeSize)), 1, gfx::OVERLAY_TRANSFORM_NONE, @@ -413,7 +413,7 @@ TEST_F(HardwareDisplayControllerTest, FailPageFlippingDueToNoPrimaryPlane) { base::Unretained(this))); drm_->RunCallbacks(); - EXPECT_EQ(gfx::SwapResult::SWAP_FAILED, last_swap_result_); + EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); } @@ -452,3 +452,33 @@ TEST_F(HardwareDisplayControllerTest, RemoveCrtcMidPageFlip) { EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); EXPECT_EQ(1, page_flips_); } + +TEST_F(HardwareDisplayControllerTest, Disable) { + ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>( + new ui::MockScanoutBuffer(kDefaultModeSize))); + EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode)); + + ui::OverlayPlane plane2(new ui::MockScanoutBuffer(kOverlaySize), 1, + gfx::OVERLAY_TRANSFORM_NONE, gfx::Rect(kOverlaySize), + gfx::RectF(kDefaultModeSizeF)); + std::vector<ui::OverlayPlane> planes; + planes.push_back(plane1); + planes.push_back(plane2); + + controller_->SchedulePageFlip( + planes, base::Bind(&HardwareDisplayControllerTest::PageFlipCallback, + base::Unretained(this))); + drm_->RunCallbacks(); + EXPECT_EQ(gfx::SwapResult::SWAP_ACK, last_swap_result_); + EXPECT_EQ(1, page_flips_); + + controller_->Disable(); + + int planes_in_use = 0; + for (const auto& plane : drm_->plane_manager()->planes()) { + if (plane->in_use()) + planes_in_use++; + } + // Only the primary plane is in use. + ASSERT_EQ(1, planes_in_use); +} diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h index 90050f98a89..0cf28072e60 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h @@ -45,6 +45,7 @@ class HardwareDisplayPlane { uint32_t plane_id() const { return plane_id_; } Type type() const { return type_; } + void set_type(const Type type) { type_ = type; } void set_owning_crtc(uint32_t crtc) { owning_crtc_ = crtc; } uint32_t owning_crtc() const { return owning_crtc_; } 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 15538c5e85f..2a9f0bd1246 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 @@ -89,6 +89,10 @@ class HardwareDisplayPlaneManager { virtual bool Commit(HardwareDisplayPlaneList* plane_list, bool test_only) = 0; + // Disable all the overlay planes previously submitted and now stored in + // plane_list->old_plane_list. + virtual bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) = 0; + const std::vector<std::unique_ptr<HardwareDisplayPlane>>& planes() { return planes_; } 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 67bea1230f5..eb0655d9a58 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 @@ -17,12 +17,11 @@ namespace { void AtomicPageFlipCallback(std::vector<base::WeakPtr<CrtcController>> crtcs, unsigned int frame, - unsigned int seconds, - unsigned int useconds) { + base::TimeTicks timestamp) { for (auto& crtc : crtcs) { auto* crtc_ptr = crtc.get(); if (crtc_ptr) - crtc_ptr->OnPageFlipEvent(frame, seconds, useconds); + crtc_ptr->OnPageFlipEvent(frame, timestamp); } } @@ -75,8 +74,12 @@ bool HardwareDisplayPlaneManagerAtomic::Commit( if (!drm_->CommitProperties(plane_list->atomic_property_set.get(), flags, crtcs.size(), base::Bind(&AtomicPageFlipCallback, crtcs))) { - PLOG(ERROR) << "Failed to commit properties. test_only:" << std::boolalpha - << test_only << " error"; + if (!test_only) { + PLOG(ERROR) << "Failed to commit properties for page flip."; + } else { + VPLOG(2) << "Failed to commit properties for MODE_ATOMIC_TEST_ONLY."; + } + ResetCurrentPlaneList(plane_list); return false; } @@ -86,6 +89,33 @@ bool HardwareDisplayPlaneManagerAtomic::Commit( return true; } +bool HardwareDisplayPlaneManagerAtomic::DisableOverlayPlanes( + HardwareDisplayPlaneList* plane_list) { + for (HardwareDisplayPlane* plane : plane_list->old_plane_list) { + if (plane->type() != HardwareDisplayPlane::kOverlay) + continue; + plane->set_in_use(false); + plane->set_owning_crtc(0); + + HardwareDisplayPlaneAtomic* atomic_plane = + static_cast<HardwareDisplayPlaneAtomic*>(plane); + atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(), 0, 0, + gfx::Rect(), gfx::Rect(), + gfx::OVERLAY_TRANSFORM_NONE); + } + // The list of crtcs is only useful if flags contains DRM_MODE_PAGE_FLIP_EVENT + // to get the pageflip callback. In this case we don't need to be notified + // at the next page flip, so the list of crtcs can be empty. + std::vector<base::WeakPtr<CrtcController>> crtcs; + bool ret = drm_->CommitProperties(plane_list->atomic_property_set.get(), + DRM_MODE_ATOMIC_NONBLOCK, crtcs.size(), + base::Bind(&AtomicPageFlipCallback, crtcs)); + PLOG_IF(ERROR, !ret) << "Failed to commit properties for page flip."; + + plane_list->atomic_property_set.reset(drmModeAtomicAlloc()); + return ret; +} + bool HardwareDisplayPlaneManagerAtomic::SetPlaneData( HardwareDisplayPlaneList* plane_list, HardwareDisplayPlane* hw_plane, diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h index 7847d43700c..968ff37897e 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 @@ -20,6 +20,7 @@ class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { // HardwareDisplayPlaneManager: bool Commit(HardwareDisplayPlaneList* plane_list, bool test_only) override; + bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override; private: bool SetPlaneData(HardwareDisplayPlaneList* plane_list, 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 501dfabcf6d..50d4334dc1e 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 @@ -98,6 +98,17 @@ bool HardwareDisplayPlaneManagerLegacy::Commit( return ret; } +bool HardwareDisplayPlaneManagerLegacy::DisableOverlayPlanes( + HardwareDisplayPlaneList* plane_list) { + // We're never going to ship legacy pageflip with overlays enabled. + DCHECK(std::find_if(plane_list->old_plane_list.begin(), + plane_list->old_plane_list.end(), + [](HardwareDisplayPlane* plane) { + return plane->type() == HardwareDisplayPlane::kOverlay; + }) == plane_list->old_plane_list.end()); + return true; +} + bool HardwareDisplayPlaneManagerLegacy::SetPlaneData( HardwareDisplayPlaneList* plane_list, HardwareDisplayPlane* hw_plane, diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h index 56c55766e0f..1f9786028b7 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 @@ -20,6 +20,7 @@ class HardwareDisplayPlaneManagerLegacy : public HardwareDisplayPlaneManager { // HardwareDisplayPlaneManager: bool Commit(HardwareDisplayPlaneList* plane_list, bool test_only) override; + bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override; protected: bool SetPlaneData(HardwareDisplayPlaneList* plane_list, diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc index c294771a5f5..c3845221469 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc @@ -126,7 +126,7 @@ bool MockDrmDevice::PageFlip(uint32_t crtc_id, current_framebuffer_ = framebuffer; if (page_flip_expectation_) { if (use_sync_flips_) - callback.Run(0, 0, 0); + callback.Run(0, base::TimeTicks()); else callbacks_.push(callback); } @@ -241,7 +241,7 @@ void MockDrmDevice::RunCallbacks() { while (!callbacks_.empty()) { PageFlipCallback callback = callbacks_.front(); callbacks_.pop(); - callback.Run(0, 0, 0); + callback.Run(0, base::TimeTicks()); } } diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.cc b/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.cc index ec26ff89a11..68e7659b836 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.cc @@ -28,6 +28,8 @@ MockHardwareDisplayPlaneManager::MockHardwareDisplayPlaneManager( plane->Initialize(drm, std::vector<uint32_t>(1, DRM_FORMAT_XRGB8888), std::vector<drm_format_modifier>(1, linear_modifier), false, true); + plane->set_type(i ? HardwareDisplayPlane::kOverlay + : HardwareDisplayPlane::kPrimary); planes_.push_back(std::move(plane)); } } @@ -99,6 +101,18 @@ void MockHardwareDisplayPlaneManager::SetCrtcInfo( ResetPlaneCount(); } +bool MockHardwareDisplayPlaneManager::DisableOverlayPlanes( + HardwareDisplayPlaneList* plane_list) { + for (HardwareDisplayPlane* plane : plane_list->old_plane_list) { + if (plane->type() != HardwareDisplayPlane::kOverlay) + continue; + plane->set_in_use(false); + plane->set_owning_crtc(0); + } + + return true; +} + bool MockHardwareDisplayPlaneManager::SetPlaneData( HardwareDisplayPlaneList* plane_list, HardwareDisplayPlane* hw_plane, diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.h b/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.h index 60f159e517f..dc567a9f4a0 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/mock_hardware_display_plane_manager.h @@ -33,6 +33,7 @@ class MockHardwareDisplayPlaneManager void SetPlaneProperties(const std::vector<FakePlaneInfo>& planes); void SetCrtcInfo(const std::vector<uint32_t>& crtcs); + bool DisableOverlayPlanes(HardwareDisplayPlaneList* plane_list) override; bool SetPlaneData(HardwareDisplayPlaneList* plane_list, HardwareDisplayPlane* hw_plane, const OverlayPlane& overlay, 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 463c108f9c2..824287f2382 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.cc @@ -9,7 +9,7 @@ #include "base/memory/ptr_util.h" #include "base/threading/thread_task_runner_handle.h" #include "ui/display/types/display_mode.h" -#include "ui/ozone/common/display_snapshot_proxy.h" +#include "ui/display/types/display_snapshot.h" #include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/host/gpu_thread_adapter.h" @@ -19,7 +19,7 @@ DrmDisplayHost::DrmDisplayHost(GpuThreadAdapter* sender, const DisplaySnapshot_Params& params, bool is_dummy) : sender_(sender), - snapshot_(new DisplaySnapshotProxy(params)), + snapshot_(CreateDisplaySnapshotFromParams(params)), is_dummy_(is_dummy) { sender_->AddGpuThreadObserver(this); } @@ -31,7 +31,7 @@ DrmDisplayHost::~DrmDisplayHost() { void DrmDisplayHost::UpdateDisplaySnapshot( const DisplaySnapshot_Params& params) { - snapshot_ = base::MakeUnique<DisplaySnapshotProxy>(params); + snapshot_ = CreateDisplaySnapshotFromParams(params); } void DrmDisplayHost::Configure(const display::DisplayMode* mode, 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 8411915dfde..f160a2ce760 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 @@ -288,8 +288,7 @@ void DrmDisplayHostManager::OnAddGraphicsDevice( std::unique_ptr<DrmDeviceHandle> handle) { if (handle->IsValid()) { drm_devices_[dev_path] = sys_path; - proxy_->GpuAddGraphicsDevice(sys_path, - base::FileDescriptor(handle->PassFD())); + proxy_->GpuAddGraphicsDevice(sys_path, handle->PassFD()); NotifyDisplayDelegate(); } @@ -332,7 +331,7 @@ void DrmDisplayHostManager::OnGpuProcessLaunched() { // Send the primary device first since this is used to initialize graphics // state. proxy_->GpuAddGraphicsDevice(drm_devices_[primary_graphics_card_path_], - base::FileDescriptor(handle->PassFD())); + handle->PassFD()); } void DrmDisplayHostManager::OnGpuThreadReady() { 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 ccbcbeb21c7..52e08ed5cc4 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 @@ -9,6 +9,7 @@ #include "base/trace_event/trace_event.h" #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" #include "ui/ozone/common/gpu/ozone_gpu_messages.h" +#include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/host/drm_cursor.h" #include "ui/ozone/platform/drm/host/drm_display_host_manager.h" #include "ui/ozone/platform/drm/host/drm_overlay_candidates_host.h" @@ -244,10 +245,10 @@ bool DrmGpuPlatformSupportHost::GpuRelinquishDisplayControl() { return Send(new OzoneGpuMsg_RelinquishDisplayControl()); } -bool DrmGpuPlatformSupportHost::GpuAddGraphicsDevice( - const base::FilePath& path, - const base::FileDescriptor& fd) { - IPC::Message* message = new OzoneGpuMsg_AddGraphicsDevice(path, fd); +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 @@ -294,14 +295,17 @@ bool DrmGpuPlatformSupportHost::OnMessageReceivedForDrmOverlayManager( void DrmGpuPlatformSupportHost::OnOverlayResult( gfx::AcceleratedWidget widget, const std::vector<OverlayCheck_Params>& params, - const std::vector<OverlayCheckReturn_Params>& returns) { - overlay_manager_->GpuSentOverlayResult(widget, params, returns); + const std::vector<OverlayCheckReturn_Params>& param_returns) { + auto candidates = CreateOverlaySurfaceCandidateListFrom(params); + auto returns = CreateOverlayStatusListFrom(param_returns); + overlay_manager_->GpuSentOverlayResult(widget, candidates, returns); } bool DrmGpuPlatformSupportHost::GpuCheckOverlayCapabilities( gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& new_params) { - return Send(new OzoneGpuMsg_CheckOverlayCapabilities(widget, new_params)); + const OverlaySurfaceCandidateList& candidates) { + auto params = CreateParamsFromOverlaySurfaceCandidate(candidates); + return Send(new OzoneGpuMsg_CheckOverlayCapabilities(widget, params)); } // DrmDisplayHost 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 5611b961d74..826e09d949c 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 @@ -63,7 +63,7 @@ class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost, bool GpuRefreshNativeDisplays() override; bool GpuRelinquishDisplayControl() override; bool GpuAddGraphicsDevice(const base::FilePath& path, - const base::FileDescriptor& fd) override; + base::ScopedFD fd) override; bool GpuRemoveGraphicsDevice(const base::FilePath& path) override; // Methods needed for DrmOverlayManager. @@ -74,7 +74,7 @@ class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost, // Services needed by DrmOverlayManager bool GpuCheckOverlayCapabilities( gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& new_params) override; + const OverlaySurfaceCandidateList& new_params) override; // Services needed by DrmDisplayHost bool GpuConfigureNativeDisplay(int64_t display_id, diff --git a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc index ffaad3c5107..f96d2d9bbd9 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.cc @@ -34,12 +34,6 @@ void DrmNativeDisplayDelegate::Initialize() { display_manager_->AddDelegate(this); } -void DrmNativeDisplayDelegate::GrabServer() { -} - -void DrmNativeDisplayDelegate::UngrabServer() { -} - void DrmNativeDisplayDelegate::TakeDisplayControl( const display::DisplayControlCallback& callback) { display_manager_->TakeDisplayControl(callback); @@ -50,23 +44,11 @@ void DrmNativeDisplayDelegate::RelinquishDisplayControl( display_manager_->RelinquishDisplayControl(callback); } -void DrmNativeDisplayDelegate::SyncWithServer() { -} - -void DrmNativeDisplayDelegate::SetBackgroundColor(uint32_t color_argb) { -} - -void DrmNativeDisplayDelegate::ForceDPMSOn() { -} - void DrmNativeDisplayDelegate::GetDisplays( const display::GetDisplaysCallback& callback) { display_manager_->UpdateDisplays(callback); } -void DrmNativeDisplayDelegate::AddMode(const display::DisplaySnapshot& output, - const display::DisplayMode* mode) {} - void DrmNativeDisplayDelegate::Configure( const display::DisplaySnapshot& output, const display::DisplayMode* mode, @@ -76,9 +58,6 @@ void DrmNativeDisplayDelegate::Configure( display->Configure(mode, origin, callback); } -void DrmNativeDisplayDelegate::CreateFrameBuffer(const gfx::Size& size) { -} - void DrmNativeDisplayDelegate::GetHDCPState( const display::DisplaySnapshot& output, const display::GetHDCPStateCallback& callback) { @@ -94,19 +73,6 @@ void DrmNativeDisplayDelegate::SetHDCPState( display->SetHDCPState(state, callback); } -std::vector<display::ColorCalibrationProfile> -DrmNativeDisplayDelegate::GetAvailableColorCalibrationProfiles( - const display::DisplaySnapshot& output) { - return std::vector<display::ColorCalibrationProfile>(); -} - -bool DrmNativeDisplayDelegate::SetColorCalibrationProfile( - const display::DisplaySnapshot& output, - display::ColorCalibrationProfile new_profile) { - NOTIMPLEMENTED(); - return false; -} - bool DrmNativeDisplayDelegate::SetColorCorrection( const display::DisplaySnapshot& output, const std::vector<display::GammaRampRGBEntry>& degamma_lut, diff --git a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h index e37a37366b9..0b1d5336aff 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h +++ b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h @@ -17,7 +17,7 @@ class DrmDisplayHostManager; class DrmNativeDisplayDelegate : public display::NativeDisplayDelegate { public: - DrmNativeDisplayDelegate(DrmDisplayHostManager* display_manager); + explicit DrmNativeDisplayDelegate(DrmDisplayHostManager* display_manager); ~DrmNativeDisplayDelegate() override; void OnConfigurationChanged(); @@ -25,34 +25,20 @@ class DrmNativeDisplayDelegate : public display::NativeDisplayDelegate { // display::NativeDisplayDelegate overrides: void Initialize() override; - void GrabServer() override; - void UngrabServer() override; void TakeDisplayControl( const display::DisplayControlCallback& callback) override; void RelinquishDisplayControl( const display::DisplayControlCallback& callback) override; - void SyncWithServer() override; - void SetBackgroundColor(uint32_t color_argb) override; - void ForceDPMSOn() override; void GetDisplays(const display::GetDisplaysCallback& callback) override; - void AddMode(const display::DisplaySnapshot& output, - const display::DisplayMode* mode) override; void Configure(const display::DisplaySnapshot& output, const display::DisplayMode* mode, const gfx::Point& origin, const display::ConfigureCallback& callback) override; - void CreateFrameBuffer(const gfx::Size& size) override; void GetHDCPState(const display::DisplaySnapshot& output, const display::GetHDCPStateCallback& callback) override; void SetHDCPState(const display::DisplaySnapshot& output, display::HDCPState state, const display::SetHDCPStateCallback& callback) override; - std::vector<display::ColorCalibrationProfile> - GetAvailableColorCalibrationProfiles( - const display::DisplaySnapshot& output) override; - bool SetColorCalibrationProfile( - const display::DisplaySnapshot& output, - display::ColorCalibrationProfile new_profile) override; bool SetColorCorrection( const display::DisplaySnapshot& output, const std::vector<display::GammaRampRGBEntry>& degamma_lut, 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 2d144393cb4..34099f1cc3e 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.cc @@ -12,6 +12,7 @@ #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" #include "ui/ozone/platform/drm/host/drm_overlay_candidates_host.h" #include "ui/ozone/platform/drm/host/drm_window_host.h" #include "ui/ozone/platform/drm/host/drm_window_host_manager.h" @@ -49,13 +50,14 @@ void DrmOverlayManager::CheckOverlaySupport( OverlayCandidatesOzone::OverlaySurfaceCandidateList* candidates, gfx::AcceleratedWidget widget) { TRACE_EVENT0("hwoverlays", "DrmOverlayManager::CheckOverlaySupport"); - std::vector<OverlayCheck_Params> overlay_params; + + OverlaySurfaceCandidateList result_candidates; for (auto& candidate : *candidates) { // Reject candidates that don't fall on a pixel boundary. if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f)) { DCHECK(candidate.plane_z_order != 0); - overlay_params.push_back(OverlayCheck_Params()); - overlay_params.back().is_overlay_candidate = false; + result_candidates.push_back(OverlaySurfaceCandidate()); + result_candidates.back().overlay_handled = false; continue; } @@ -64,29 +66,29 @@ void DrmOverlayManager::CheckOverlaySupport( if (candidate.plane_z_order == 0) candidate.buffer_size = gfx::ToNearestRect(candidate.display_rect).size(); - overlay_params.push_back(OverlayCheck_Params(candidate)); + result_candidates.push_back(OverlaySurfaceCandidate(candidate)); + // Start out hoping that we can have an overlay. + result_candidates.back().overlay_handled = true; if (!CanHandleCandidate(candidate, widget)) { DCHECK(candidate.plane_z_order != 0); - overlay_params.back().is_overlay_candidate = false; + result_candidates.back().overlay_handled = false; } } size_t size = candidates->size(); - auto iter = cache_.Get(overlay_params); + auto iter = cache_.Get(result_candidates); if (iter == cache_.end()) { // We can skip GPU side validation in case all candidates are invalid. bool needs_gpu_validation = std::any_of( - overlay_params.begin(), overlay_params.end(), - [](OverlayCheck_Params& c) { return c.is_overlay_candidate; }); + result_candidates.begin(), result_candidates.end(), + [](OverlaySurfaceCandidate& c) { return c.overlay_handled; }); OverlayValidationCacheValue value; value.request_num = 0; - value.returns.resize(overlay_params.size()); - for (size_t i = 0; i < value.returns.size(); ++i) { - value.returns[i].status = - needs_gpu_validation ? OVERLAY_STATUS_PENDING : OVERLAY_STATUS_NOT; - } - iter = cache_.Put(overlay_params, value); + value.status.resize(result_candidates.size(), needs_gpu_validation + ? OVERLAY_STATUS_PENDING + : OVERLAY_STATUS_NOT); + iter = cache_.Put(result_candidates, value); } OverlayValidationCacheValue& value = iter->second; @@ -94,20 +96,19 @@ void DrmOverlayManager::CheckOverlaySupport( value.request_num++; } else if (value.request_num == kThrottleRequestSize) { value.request_num++; - if (value.returns.back().status == OVERLAY_STATUS_PENDING) - SendOverlayValidationRequest(overlay_params, widget); + if (value.status.back() == OVERLAY_STATUS_PENDING) + SendOverlayValidationRequest(result_candidates, widget); } else { // We haven't received an answer yet. - if (value.returns.back().status == OVERLAY_STATUS_PENDING) + if (value.status.back() == OVERLAY_STATUS_PENDING) return; - const std::vector<OverlayCheckReturn_Params>& returns = value.returns; - DCHECK(size == returns.size()); + const std::vector<OverlayStatus>& status = value.status; + DCHECK(size == status.size()); for (size_t i = 0; i < size; i++) { - DCHECK(returns[i].status == OVERLAY_STATUS_ABLE || - returns[i].status == OVERLAY_STATUS_NOT); - candidates->at(i).overlay_handled = - returns[i].status == OVERLAY_STATUS_ABLE; + DCHECK(status[i] == OVERLAY_STATUS_ABLE || + status[i] == OVERLAY_STATUS_NOT); + candidates->at(i).overlay_handled = status[i] == OVERLAY_STATUS_ABLE; } } } @@ -125,25 +126,25 @@ DrmOverlayManager::OverlayValidationCacheValue::~OverlayValidationCacheValue() = default; void DrmOverlayManager::SendOverlayValidationRequest( - const std::vector<OverlayCheck_Params>& new_params, + const OverlaySurfaceCandidateList& candidates, gfx::AcceleratedWidget widget) const { if (!proxy_->IsConnected()) return; TRACE_EVENT_ASYNC_BEGIN0( "hwoverlays", "DrmOverlayManager::SendOverlayValidationRequest", this); - proxy_->GpuCheckOverlayCapabilities(widget, new_params); + proxy_->GpuCheckOverlayCapabilities(widget, candidates); } void DrmOverlayManager::GpuSentOverlayResult( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& params, - const std::vector<OverlayCheckReturn_Params>& returns) { + const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& candidates, + const OverlayStatusList& returns) { TRACE_EVENT_ASYNC_END0( "hwoverlays", "DrmOverlayManager::SendOverlayValidationRequest response", this); - auto iter = cache_.Peek(params); + auto iter = cache_.Peek(candidates); if (iter != cache_.end()) { - iter->second.returns = returns; + iter->second.status = returns; } } diff --git a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h index 6ad846997a2..c1915d5714e 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h @@ -35,10 +35,9 @@ class DrmOverlayManager : public OverlayManagerOzone { // Communication-free implementations of actions performed in response to // messages from the GPU thread. - void GpuSentOverlayResult( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& params, - const std::vector<OverlayCheckReturn_Params>& returns); + void GpuSentOverlayResult(const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& params, + const OverlayStatusList& returns); // Service method for DrmOverlayCandidatesHost void CheckOverlaySupport( @@ -54,11 +53,11 @@ class DrmOverlayManager : public OverlayManagerOzone { OverlayValidationCacheValue(const OverlayValidationCacheValue&); ~OverlayValidationCacheValue(); int request_num = 0; - std::vector<OverlayCheckReturn_Params> returns; + std::vector<OverlayStatus> status; }; void SendOverlayValidationRequest( - const std::vector<OverlayCheck_Params>& new_params, + const OverlaySurfaceCandidateList& candidates, gfx::AcceleratedWidget widget) const; bool CanHandleCandidate(const OverlaySurfaceCandidate& candidate, gfx::AcceleratedWidget widget) const; @@ -66,9 +65,9 @@ class DrmOverlayManager : public OverlayManagerOzone { GpuThreadAdapter* proxy_; // Not owned. DrmWindowHostManager* window_manager_; // Not owned. - // List of all OverlayCheck_Params instances which have been requested + // List of all OverlaySurfaceCandidate instances which have been requested // for validation and/or validated. - base::MRUCache<std::vector<OverlayCheck_Params>, OverlayValidationCacheValue> + base::MRUCache<OverlaySurfaceCandidateList, OverlayValidationCacheValue> cache_; DISALLOW_COPY_AND_ASSIGN(DrmOverlayManager); diff --git a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h index 4d63d010dfd..f275e66709a 100644 --- a/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h +++ b/chromium/ui/ozone/platform/drm/host/gpu_thread_adapter.h @@ -38,7 +38,7 @@ class GpuThreadAdapter { virtual bool GpuRefreshNativeDisplays() = 0; virtual bool GpuRelinquishDisplayControl() = 0; virtual bool GpuAddGraphicsDevice(const base::FilePath& path, - const base::FileDescriptor& fd) = 0; + base::ScopedFD fd) = 0; virtual bool GpuRemoveGraphicsDevice(const base::FilePath& path) = 0; // Methods for DrmOverlayManager. @@ -49,7 +49,7 @@ class GpuThreadAdapter { // Services needed by DrmOverlayManager virtual bool GpuCheckOverlayCapabilities( gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& new_params) = 0; + const OverlaySurfaceCandidateList& overlays) = 0; // Services needed by DrmDisplayHost virtual bool GpuConfigureNativeDisplay( diff --git a/chromium/ui/ozone/platform/drm/cursor_proxy_mojo.cc b/chromium/ui/ozone/platform/drm/host/host_cursor_proxy.cc index c48ddcad7c4..ba5baeec450 100644 --- a/chromium/ui/ozone/platform/drm/cursor_proxy_mojo.cc +++ b/chromium/ui/ozone/platform/drm/host/host_cursor_proxy.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/ozone/platform/drm/cursor_proxy_mojo.h" +#include "ui/ozone/platform/drm/host/host_cursor_proxy.h" #include "services/service_manager/public/cpp/connector.h" #include "services/ui/public/interfaces/constants.mojom.h" @@ -10,15 +10,15 @@ namespace ui { // We assume that this is invoked only on the UI thread. -CursorProxyMojo::CursorProxyMojo(service_manager::Connector* connector) +HostCursorProxy::HostCursorProxy(service_manager::Connector* connector) : connector_(connector->Clone()) { ui_thread_ref_ = base::PlatformThread::CurrentRef(); connector->BindInterface(ui::mojom::kServiceName, &main_cursor_ptr_); } -CursorProxyMojo::~CursorProxyMojo() {} +HostCursorProxy::~HostCursorProxy() {} -void CursorProxyMojo::CursorSet(gfx::AcceleratedWidget widget, +void HostCursorProxy::CursorSet(gfx::AcceleratedWidget widget, const std::vector<SkBitmap>& bitmaps, const gfx::Point& location, int frame_delay_ms) { @@ -30,7 +30,7 @@ void CursorProxyMojo::CursorSet(gfx::AcceleratedWidget widget, } } -void CursorProxyMojo::Move(gfx::AcceleratedWidget widget, +void HostCursorProxy::Move(gfx::AcceleratedWidget widget, const gfx::Point& location) { InitializeOnEvdevIfNecessary(); if (ui_thread_ref_ == base::PlatformThread::CurrentRef()) { @@ -40,12 +40,12 @@ void CursorProxyMojo::Move(gfx::AcceleratedWidget widget, } } -// Evdev runs this method on starting. But if a CursorProxyMojo is created long +// Evdev runs this method on starting. But if a HostCursorProxy is created long // after Evdev has started (e.g. if the Viz process crashes (and the -// |CursorProxyMojo| self-destructs and then a new |CursorProxyMojo| is built +// |HostCursorProxy| self-destructs and then a new |HostCursorProxy| is built // when the GpuThread/DrmThread pair are once again running), we need to run it // on cursor motions. -void CursorProxyMojo::InitializeOnEvdevIfNecessary() { +void HostCursorProxy::InitializeOnEvdevIfNecessary() { if (ui_thread_ref_ != base::PlatformThread::CurrentRef()) { connector_->BindInterface(ui::mojom::kServiceName, &evdev_cursor_ptr_); } diff --git a/chromium/ui/ozone/platform/drm/cursor_proxy_mojo.h b/chromium/ui/ozone/platform/drm/host/host_cursor_proxy.h index e1bceff9208..42feece0e0e 100644 --- a/chromium/ui/ozone/platform/drm/cursor_proxy_mojo.h +++ b/chromium/ui/ozone/platform/drm/host/host_cursor_proxy.h @@ -2,11 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_OZONE_PLATFORM_DRM_CURSOR_PROXY_MOJO_H_ -#define UI_OZONE_PLATFORM_DRM_CURSOR_PROXY_MOJO_H_ +#ifndef UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_ +#define UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_ #include "ui/gfx/native_widget_types.h" -#include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h" #include "ui/ozone/platform/drm/host/drm_cursor.h" #include "ui/ozone/public/interfaces/device_cursor.mojom.h" @@ -21,10 +20,10 @@ namespace ui { // pointer control via Mojo-style IPC. This code runs only in the mus-ws (i.e. // it's the client) and sends mouse pointer control messages to a less // priviledged process. -class CursorProxyMojo : public DrmCursorProxy { +class HostCursorProxy : public DrmCursorProxy { public: - explicit CursorProxyMojo(service_manager::Connector* connector); - ~CursorProxyMojo() override; + explicit HostCursorProxy(service_manager::Connector* connector); + ~HostCursorProxy() override; private: // DrmCursorProxy. @@ -42,9 +41,9 @@ class CursorProxyMojo : public DrmCursorProxy { ui::ozone::mojom::DeviceCursorPtr evdev_cursor_ptr_; base::PlatformThreadRef ui_thread_ref_; - DISALLOW_COPY_AND_ASSIGN(CursorProxyMojo); + DISALLOW_COPY_AND_ASSIGN(HostCursorProxy); }; } // namespace ui -#endif // UI_OZONE_PLATFORM_DRM_CURSOR_PROXY_MOJO_H_ +#endif // UI_OZONE_PLATFORM_DRM_HOST_HOST_CURSOR_PROXY_H_ diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.cc b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc new file mode 100644 index 00000000000..628b3c23a16 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.cc @@ -0,0 +1,348 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/ozone/platform/drm/host/host_drm_device.h" + +#include "base/bind.h" +#include "base/run_loop.h" +#include "base/single_thread_task_runner.h" +#include "base/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 "ui/display/types/display_snapshot.h" +#include "ui/ozone/platform/drm/common/drm_util.h" +#include "ui/ozone/platform/drm/host/drm_display_host_manager.h" +#include "ui/ozone/platform/drm/host/drm_overlay_manager.h" +#include "ui/ozone/platform/drm/host/host_cursor_proxy.h" + +namespace ui { + +HostDrmDevice::HostDrmDevice(DrmCursor* cursor, + service_manager::Connector* connector) + : cursor_(cursor), connector_(connector), weak_ptr_factory_(this) { + // Bind the viz process pointer here. + // TODO(rjkroege): Reconnect on error as that would indicate that the Viz + // process has failed. + connector->BindInterface(ui::mojom::kServiceName, &drm_device_ptr_); +} + +HostDrmDevice::~HostDrmDevice() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + for (GpuThreadObserver& observer : gpu_thread_observers_) + observer.OnGpuThreadRetired(); +} + +void HostDrmDevice::AsyncStartDrmDevice() { + auto callback = base::BindOnce(&HostDrmDevice::OnDrmServiceStartedCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_device_ptr_->StartDrmDevice(std::move(callback)); +} + +void HostDrmDevice::BlockingStartDrmDevice() { + // Wait until startup related tasks posted to this thread that must precede + // blocking on + base::RunLoop().RunUntilIdle(); + + bool success; + drm_device_ptr_->StartDrmDevice(&success); + CHECK(success) + << "drm thread failed to successfully start in single process mode."; + if (!connected_) + OnDrmServiceStartedCallback(true); + return; +} + +void HostDrmDevice::OnDrmServiceStartedCallback(bool success) { + // This can be called multiple times in the course of single-threaded startup. + if (connected_) + return; + if (success == true) { + connected_ = true; + RunObservers(); + } + // TODO(rjkroege): Handle failure of launching a viz process. +} + +void HostDrmDevice::ProvideManagers(DrmDisplayHostManager* display_manager, + DrmOverlayManager* overlay_manager) { + display_manager_ = display_manager; + overlay_manager_ = overlay_manager; +} + +void HostDrmDevice::RunObservers() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + for (GpuThreadObserver& observer : gpu_thread_observers_) { + observer.OnGpuProcessLaunched(); + observer.OnGpuThreadReady(); + } + + // The cursor is special since it will process input events on the IO thread + // and can by-pass the UI thread. This means that we need to special case it + // and notify it after all other observers/handlers are notified. + cursor_->SetDrmCursorProxy(base::MakeUnique<HostCursorProxy>(connector_)); + + // TODO(rjkroege): Call ResetDrmCursorProxy when the mojo connection to the + // DRM thread is broken. +} + +void HostDrmDevice::AddGpuThreadObserver(GpuThreadObserver* observer) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + gpu_thread_observers_.AddObserver(observer); + if (IsConnected()) { + observer->OnGpuProcessLaunched(); + observer->OnGpuThreadReady(); + } +} + +void HostDrmDevice::RemoveGpuThreadObserver(GpuThreadObserver* observer) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + gpu_thread_observers_.RemoveObserver(observer); +} + +bool HostDrmDevice::IsConnected() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + + // TODO(rjkroege): Need to set to connected_ to false when we lose the Viz + // process connection. + return connected_; +} + +// Services needed for DrmDisplayHostMananger. +void HostDrmDevice::RegisterHandlerForDrmDisplayHostManager( + DrmDisplayHostManager* handler) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_ = handler; +} + +void HostDrmDevice::UnRegisterHandlerForDrmDisplayHostManager() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_ = nullptr; +} + +bool HostDrmDevice::GpuCreateWindow(gfx::AcceleratedWidget widget) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + + drm_device_ptr_->CreateWindow(widget); + return true; +} + +bool HostDrmDevice::GpuDestroyWindow(gfx::AcceleratedWidget widget) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + + drm_device_ptr_->DestroyWindow(widget); + return true; +} + +bool HostDrmDevice::GpuWindowBoundsChanged(gfx::AcceleratedWidget widget, + const gfx::Rect& bounds) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + + drm_device_ptr_->SetWindowBounds(widget, bounds); + return true; +} + +// Services needed for DrmOverlayManager. +void HostDrmDevice::RegisterHandlerForDrmOverlayManager( + DrmOverlayManager* handler) { + // TODO(rjkroege): Permit overlay manager to run in viz when the display + // compositor runs in viz. + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + overlay_manager_ = handler; +} + +void HostDrmDevice::UnRegisterHandlerForDrmOverlayManager() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + overlay_manager_ = nullptr; +} + +bool HostDrmDevice::GpuCheckOverlayCapabilities( + gfx::AcceleratedWidget widget, + const OverlaySurfaceCandidateList& overlays) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + + auto callback = + base::BindOnce(&HostDrmDevice::GpuCheckOverlayCapabilitiesCallback, + weak_ptr_factory_.GetWeakPtr()); + + drm_device_ptr_->CheckOverlayCapabilities(widget, overlays, + std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuRefreshNativeDisplays() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + auto callback = + base::BindOnce(&HostDrmDevice::GpuRefreshNativeDisplaysCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_device_ptr_->RefreshNativeDisplays(std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuConfigureNativeDisplay(int64_t id, + const DisplayMode_Params& pmode, + const gfx::Point& origin) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + + // TODO(rjkroege): Remove the use of mode here. + auto mode = CreateDisplayModeFromParams(pmode); + auto callback = + base::BindOnce(&HostDrmDevice::GpuConfigureNativeDisplayCallback, + weak_ptr_factory_.GetWeakPtr()); + + drm_device_ptr_->ConfigureNativeDisplay(id, std::move(mode), origin, + std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuDisableNativeDisplay(int64_t id) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + auto callback = + base::BindOnce(&HostDrmDevice::GpuDisableNativeDisplayCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_device_ptr_->DisableNativeDisplay(id, std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuTakeDisplayControl() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + auto callback = base::BindOnce(&HostDrmDevice::GpuTakeDisplayControlCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_device_ptr_->TakeDisplayControl(std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuRelinquishDisplayControl() { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + auto callback = + base::BindOnce(&HostDrmDevice::GpuRelinquishDisplayControlCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_device_ptr_->TakeDisplayControl(std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuAddGraphicsDevice(const base::FilePath& path, + base::ScopedFD fd) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + base::File file(fd.release()); + drm_device_ptr_->AddGraphicsDevice(path, std::move(file)); + return true; +} + +bool HostDrmDevice::GpuRemoveGraphicsDevice(const base::FilePath& path) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + drm_device_ptr_->RemoveGraphicsDevice(std::move(path)); + return true; +} + +bool HostDrmDevice::GpuGetHDCPState(int64_t display_id) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + auto callback = base::BindOnce(&HostDrmDevice::GpuGetHDCPStateCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_device_ptr_->GetHDCPState(display_id, std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuSetHDCPState(int64_t display_id, + display::HDCPState state) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + auto callback = base::BindOnce(&HostDrmDevice::GpuSetHDCPStateCallback, + weak_ptr_factory_.GetWeakPtr()); + drm_device_ptr_->SetHDCPState(display_id, state, std::move(callback)); + return true; +} + +bool HostDrmDevice::GpuSetColorCorrection( + int64_t id, + const std::vector<display::GammaRampRGBEntry>& degamma_lut, + const std::vector<display::GammaRampRGBEntry>& gamma_lut, + const std::vector<float>& correction_matrix) { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + if (!IsConnected()) + return false; + + drm_device_ptr_->SetColorCorrection(id, degamma_lut, gamma_lut, + correction_matrix); + + return true; +} + +void HostDrmDevice::GpuCheckOverlayCapabilitiesCallback( + const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& overlays, + const OverlayStatusList& returns) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + overlay_manager_->GpuSentOverlayResult(widget, overlays, returns); +} + +void HostDrmDevice::GpuConfigureNativeDisplayCallback(int64_t display_id, + bool success) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_->GpuConfiguredDisplay(display_id, success); +} + +// TODO(rjkroege): Remove the unnecessary conversion back into params. +void HostDrmDevice::GpuRefreshNativeDisplaysCallback( + std::vector<std::unique_ptr<display::DisplaySnapshot>> displays) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_->GpuHasUpdatedNativeDisplays( + CreateParamsFromSnapshot(displays)); +} + +void HostDrmDevice::GpuDisableNativeDisplayCallback(int64_t display_id, + bool success) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_->GpuConfiguredDisplay(display_id, success); +} + +void HostDrmDevice::GpuTakeDisplayControlCallback(bool success) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_->GpuTookDisplayControl(success); +} + +void HostDrmDevice::GpuRelinquishDisplayControlCallback(bool success) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_->GpuRelinquishedDisplayControl(success); +} + +void HostDrmDevice::GpuGetHDCPStateCallback(int64_t display_id, + bool success, + display::HDCPState state) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_->GpuReceivedHDCPState(display_id, success, state); +} + +void HostDrmDevice::GpuSetHDCPStateCallback(int64_t display_id, + bool success) const { + DCHECK_CALLED_ON_VALID_THREAD(on_window_server_thread_); + display_manager_->GpuUpdatedHDCPState(display_id, success); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/mus_thread_proxy.h b/chromium/ui/ozone/platform/drm/host/host_drm_device.h index 63350ba6a54..47c5b18170d 100644 --- a/chromium/ui/ozone/platform/drm/mus_thread_proxy.h +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.h @@ -2,26 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_ -#define UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_ +#ifndef UI_OZONE_PLATFORM_DRM_HOST_HOST_DRM_DEVICE_H_ +#define UI_OZONE_PLATFORM_DRM_HOST_HOST_DRM_DEVICE_H_ #include "base/callback.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" #include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" #include "ui/gfx/native_widget_types.h" -#include "ui/ozone/platform/drm/gpu/inter_thread_messaging_proxy.h" #include "ui/ozone/platform/drm/host/drm_cursor.h" #include "ui/ozone/platform/drm/host/gpu_thread_adapter.h" #include "ui/ozone/public/interfaces/device_cursor.mojom.h" - -namespace base { -class SingleThreadTaskRunner; -} +#include "ui/ozone/public/interfaces/drm_device.mojom.h" namespace display { -class DisplaySnapshotMojo; +class DisplaySnapshot; } namespace service_manager { @@ -29,32 +26,30 @@ class Connector; } namespace ui { - class DrmDisplayHostManager; class DrmOverlayManager; -class DrmThread; class GpuThreadObserver; -class MusThreadProxy; - -// In Mus, the window server thread (analogous to Chrome's UI thread), GPU and -// DRM threads coexist in a single Mus process. The |MusThreadProxy| connects -// these threads together via cross-thread calls. -class MusThreadProxy : public GpuThreadAdapter, - public InterThreadMessagingProxy, - public DrmCursorProxy { + +// This is the Viz host-side library for the DRM device service provided by the +// viz process. +class HostDrmDevice : public GpuThreadAdapter { public: - MusThreadProxy(DrmCursor* cursor, service_manager::Connector* connector); - ~MusThreadProxy() override; + HostDrmDevice(DrmCursor* cursor, service_manager::Connector* connector); + ~HostDrmDevice() override; + + // Start the DRM service. Runs the |OnDrmServiceStartedCallback| when the + // service has launched and initiates the remaining startup. + void AsyncStartDrmDevice(); + + // Blocks until the DRM service has come up. Use this entry point only when + // supporting launch of the service where the ozone UI and GPU + // reponsibilities are performed by the same underlying thread. + void BlockingStartDrmDevice(); - void StartDrmThread(); void ProvideManagers(DrmDisplayHostManager* display_manager, DrmOverlayManager* overlay_manager); - // InterThreadMessagingProxy. - void SetDrmThread(DrmThread* thread) override; - - // This is the core functionality. They are invoked when we have a main - // thread, a gpu thread and we have called initialize on both. + // GpuThreadAdapter void AddGpuThreadObserver(GpuThreadObserver* observer) override; void RemoveGpuThreadObserver(GpuThreadObserver* observer) override; bool IsConnected() override; @@ -68,16 +63,15 @@ class MusThreadProxy : public GpuThreadAdapter, bool GpuRefreshNativeDisplays() override; bool GpuRelinquishDisplayControl() override; bool GpuAddGraphicsDevice(const base::FilePath& path, - const base::FileDescriptor& fd) override; + base::ScopedFD fd) override; bool GpuRemoveGraphicsDevice(const base::FilePath& path) override; // Services needed for DrmOverlayManager. void RegisterHandlerForDrmOverlayManager(DrmOverlayManager* handler) override; void UnRegisterHandlerForDrmOverlayManager() override; - bool GpuCheckOverlayCapabilities( gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& new_params) override; + const OverlaySurfaceCandidateList& new_params) override; // Services needed by DrmDisplayHost bool GpuConfigureNativeDisplay(int64_t display_id, @@ -98,29 +92,21 @@ class MusThreadProxy : public GpuThreadAdapter, bool GpuWindowBoundsChanged(gfx::AcceleratedWidget widget, const gfx::Rect& bounds) override; - // DrmCursorProxy. - void CursorSet(gfx::AcceleratedWidget window, - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& point, - int frame_delay_ms) override; - void Move(gfx::AcceleratedWidget window, const gfx::Point& point) override; - void InitializeOnEvdevIfNecessary() override; - private: + void OnDrmServiceStartedCallback(bool success); + void PollForSingleThreadReady(int previous_delay); void RunObservers(); - void DispatchObserversFromDrmThread(); void GpuCheckOverlayCapabilitiesCallback( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays, - const std::vector<OverlayCheckReturn_Params>& returns) const; + const gfx::AcceleratedWidget& widget, + const OverlaySurfaceCandidateList& overlays, + const OverlayStatusList& returns) const; void GpuConfigureNativeDisplayCallback(int64_t display_id, bool success) const; void GpuRefreshNativeDisplaysCallback( - std::vector<std::unique_ptr<display::DisplaySnapshotMojo>> displays) - const; + std::vector<std::unique_ptr<display::DisplaySnapshot>> displays) const; void GpuDisableNativeDisplayCallback(int64_t display_id, bool success) const; void GpuTakeDisplayControlCallback(bool success) const; void GpuRelinquishDisplayControlCallback(bool success) const; @@ -129,28 +115,24 @@ class MusThreadProxy : public GpuThreadAdapter, display::HDCPState state) const; void GpuSetHDCPStateCallback(int64_t display_id, bool success) const; - scoped_refptr<base::SingleThreadTaskRunner> ws_task_runner_; - - DrmThread* drm_thread_; // Not owned. - - // Guards for multi-theaded access to drm_thread_. - base::Lock lock_; + // Mojo implementation of the DrmDevice. + ui::ozone::mojom::DrmDevicePtr drm_device_ptr_; DrmDisplayHostManager* display_manager_; // Not owned. DrmOverlayManager* overlay_manager_; // Not owned. DrmCursor* cursor_; // Not owned. service_manager::Connector* connector_; + THREAD_CHECKER(on_window_server_thread_); + bool connected_ = false; base::ObserverList<GpuThreadObserver> gpu_thread_observers_; - base::ThreadChecker on_window_server_thread_; - - base::WeakPtrFactory<MusThreadProxy> weak_ptr_factory_; + base::WeakPtrFactory<HostDrmDevice> weak_ptr_factory_; - DISALLOW_COPY_AND_ASSIGN(MusThreadProxy); + DISALLOW_COPY_AND_ASSIGN(HostDrmDevice); }; } // namespace ui -#endif // UI_OZONE_PLATFORM_DRM_MUS_THREAD_PROXY_H_ +#endif // UI_OZONE_PLATFORM_DRM_HOST_HOST_DRM_DEVICE_H_ diff --git a/chromium/ui/ozone/platform/drm/mus_thread_proxy.cc b/chromium/ui/ozone/platform/drm/mus_thread_proxy.cc deleted file mode 100644 index 5a15795de7e..00000000000 --- a/chromium/ui/ozone/platform/drm/mus_thread_proxy.cc +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/ozone/platform/drm/mus_thread_proxy.h" - -#include "base/bind.h" -#include "base/single_thread_task_runner.h" -#include "base/task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "ui/display/types/display_snapshot_mojo.h" -#include "ui/ozone/platform/drm/common/drm_util.h" -#include "ui/ozone/platform/drm/cursor_proxy_mojo.h" -#include "ui/ozone/platform/drm/gpu/drm_thread.h" -#include "ui/ozone/platform/drm/gpu/proxy_helpers.h" -#include "ui/ozone/platform/drm/host/drm_display_host_manager.h" -#include "ui/ozone/platform/drm/host/drm_overlay_manager.h" - -namespace ui { - -namespace { - -// Forwarding proxy to handle ownership semantics. -class CursorProxyThread : public DrmCursorProxy { - public: - explicit CursorProxyThread(MusThreadProxy* mus_thread_proxy); - ~CursorProxyThread() override; - - private: - // DrmCursorProxy. - void CursorSet(gfx::AcceleratedWidget window, - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& point, - int frame_delay_ms) override; - void Move(gfx::AcceleratedWidget window, const gfx::Point& point) override; - void InitializeOnEvdevIfNecessary() override; - MusThreadProxy* const mus_thread_proxy_; // Not owned. - DISALLOW_COPY_AND_ASSIGN(CursorProxyThread); -}; - -CursorProxyThread::CursorProxyThread(MusThreadProxy* mus_thread_proxy) - : mus_thread_proxy_(mus_thread_proxy) {} -CursorProxyThread::~CursorProxyThread() {} - -void CursorProxyThread::CursorSet(gfx::AcceleratedWidget window, - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& point, - int frame_delay_ms) { - mus_thread_proxy_->CursorSet(window, bitmaps, point, frame_delay_ms); -} -void CursorProxyThread::Move(gfx::AcceleratedWidget window, - const gfx::Point& point) { - mus_thread_proxy_->Move(window, point); -} -void CursorProxyThread::InitializeOnEvdevIfNecessary() { - mus_thread_proxy_->InitializeOnEvdevIfNecessary(); -} - -} // namespace - -MusThreadProxy::MusThreadProxy(DrmCursor* cursor, - service_manager::Connector* connector) - : ws_task_runner_(base::ThreadTaskRunnerHandle::Get()), - drm_thread_(nullptr), - cursor_(cursor), - connector_(connector), - weak_ptr_factory_(this) {} - -MusThreadProxy::~MusThreadProxy() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - for (GpuThreadObserver& observer : gpu_thread_observers_) - observer.OnGpuThreadRetired(); -} - -// This is configured on the GPU thread. -void MusThreadProxy::SetDrmThread(DrmThread* thread) { - base::AutoLock acquire(lock_); - drm_thread_ = thread; -} - -void MusThreadProxy::ProvideManagers(DrmDisplayHostManager* display_manager, - DrmOverlayManager* overlay_manager) { - display_manager_ = display_manager; - overlay_manager_ = overlay_manager; -} - -// Runs on Gpu thread. -void MusThreadProxy::StartDrmThread() { - DCHECK(drm_thread_); - drm_thread_->Start(); - - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&MusThreadProxy::DispatchObserversFromDrmThread, - base::Unretained(this))); -} - -void MusThreadProxy::DispatchObserversFromDrmThread() { - ws_task_runner_->PostTask(FROM_HERE, base::Bind(&MusThreadProxy::RunObservers, - base::Unretained(this))); -} - -void MusThreadProxy::RunObservers() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - for (GpuThreadObserver& observer : gpu_thread_observers_) { - // TODO(rjkroege): This needs to be different when gpu process split - // happens. - observer.OnGpuProcessLaunched(); - observer.OnGpuThreadReady(); - } - - // The cursor is special since it will process input events on the IO thread - // and can by-pass the UI thread. This means that we need to special case it - // and notify it after all other observers/handlers are notified. - if (connector_ == nullptr) { - // CursorProxyThread does not need to use delegate because the non-mojo - // MusThreadProxy is only used in tests that do not operate the cursor. - // Future refactoring will unify the mojo and in-process modes. - cursor_->SetDrmCursorProxy(base::MakeUnique<CursorProxyThread>(this)); - } else { - cursor_->SetDrmCursorProxy(base::MakeUnique<CursorProxyMojo>(connector_)); - } - - // TODO(rjkroege): Call ResetDrmCursorProxy when the mojo connection to the - // DRM thread is broken. -} - -void MusThreadProxy::AddGpuThreadObserver(GpuThreadObserver* observer) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - - gpu_thread_observers_.AddObserver(observer); - if (IsConnected()) { - // TODO(rjkroege): This needs to be different when gpu process split - // happens. - observer->OnGpuProcessLaunched(); - observer->OnGpuThreadReady(); - } -} - -void MusThreadProxy::RemoveGpuThreadObserver(GpuThreadObserver* observer) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - gpu_thread_observers_.RemoveObserver(observer); -} - -bool MusThreadProxy::IsConnected() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - base::AutoLock acquire(lock_); - if (drm_thread_) - return drm_thread_->IsRunning(); - return false; -} - -// Services needed for DrmDisplayHostMananger. -void MusThreadProxy::RegisterHandlerForDrmDisplayHostManager( - DrmDisplayHostManager* handler) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_ = handler; -} - -void MusThreadProxy::UnRegisterHandlerForDrmDisplayHostManager() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_ = nullptr; -} - -bool MusThreadProxy::GpuCreateWindow(gfx::AcceleratedWidget widget) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::CreateWindow, - base::Unretained(drm_thread_), widget)); - return true; -} - -bool MusThreadProxy::GpuDestroyWindow(gfx::AcceleratedWidget widget) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::DestroyWindow, - base::Unretained(drm_thread_), widget)); - return true; -} - -bool MusThreadProxy::GpuWindowBoundsChanged(gfx::AcceleratedWidget widget, - const gfx::Rect& bounds) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::SetWindowBounds, - base::Unretained(drm_thread_), widget, bounds)); - return true; -} - -// Services needed for DrmCursorProxy. -void MusThreadProxy::CursorSet(gfx::AcceleratedWidget widget, - const std::vector<SkBitmap>& bitmaps, - const gfx::Point& location, - int frame_delay_ms) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return; - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::Bind(&DrmThread::SetCursor, base::Unretained(drm_thread_), widget, - bitmaps, location, frame_delay_ms)); -} - -void MusThreadProxy::Move(gfx::AcceleratedWidget widget, - const gfx::Point& location) { - // NOTE: Input events skip the main thread to avoid jank. - if (!drm_thread_ || !drm_thread_->IsRunning()) - return; - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::MoveCursor, - base::Unretained(drm_thread_), widget, location)); -} - -void MusThreadProxy::InitializeOnEvdevIfNecessary() {} - -// Services needed for DrmOverlayManager. -void MusThreadProxy::RegisterHandlerForDrmOverlayManager( - DrmOverlayManager* handler) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - overlay_manager_ = handler; -} - -void MusThreadProxy::UnRegisterHandlerForDrmOverlayManager() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - overlay_manager_ = nullptr; -} - -bool MusThreadProxy::GpuCheckOverlayCapabilities( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - auto callback = - base::BindOnce(&MusThreadProxy::GpuCheckOverlayCapabilitiesCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&DrmThread::CheckOverlayCapabilities, - base::Unretained(drm_thread_), widget, overlays, - std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuRefreshNativeDisplays() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - auto callback = - base::BindOnce(&MusThreadProxy::GpuRefreshNativeDisplaysCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&DrmThread::RefreshNativeDisplays, - base::Unretained(drm_thread_), std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuConfigureNativeDisplay(int64_t id, - const DisplayMode_Params& pmode, - const gfx::Point& origin) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - - auto mode = CreateDisplayModeFromParams(pmode); - auto callback = - base::BindOnce(&MusThreadProxy::GpuConfigureNativeDisplayCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&DrmThread::ConfigureNativeDisplay, - base::Unretained(drm_thread_), id, std::move(mode), origin, - std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuDisableNativeDisplay(int64_t id) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - auto callback = - base::BindOnce(&MusThreadProxy::GpuDisableNativeDisplayCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&DrmThread::DisableNativeDisplay, - base::Unretained(drm_thread_), id, - std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuTakeDisplayControl() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - auto callback = base::BindOnce(&MusThreadProxy::GpuTakeDisplayControlCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&DrmThread::TakeDisplayControl, - base::Unretained(drm_thread_), std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuRelinquishDisplayControl() { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - auto callback = - base::BindOnce(&MusThreadProxy::GpuRelinquishDisplayControlCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&DrmThread::RelinquishDisplayControl, - base::Unretained(drm_thread_), std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuAddGraphicsDevice(const base::FilePath& path, - const base::FileDescriptor& fd) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::AddGraphicsDevice, - base::Unretained(drm_thread_), path, fd)); - return true; -} - -bool MusThreadProxy::GpuRemoveGraphicsDevice(const base::FilePath& path) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::Bind(&DrmThread::RemoveGraphicsDevice, - base::Unretained(drm_thread_), path)); - return true; -} - -bool MusThreadProxy::GpuGetHDCPState(int64_t display_id) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - auto callback = base::BindOnce(&MusThreadProxy::GpuGetHDCPStateCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&DrmThread::GetHDCPState, base::Unretained(drm_thread_), - display_id, std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuSetHDCPState(int64_t display_id, - display::HDCPState state) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - auto callback = base::BindOnce(&MusThreadProxy::GpuSetHDCPStateCallback, - weak_ptr_factory_.GetWeakPtr()); - auto safe_callback = CreateSafeOnceCallback(std::move(callback)); - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&DrmThread::SetHDCPState, base::Unretained(drm_thread_), - display_id, state, std::move(safe_callback))); - return true; -} - -bool MusThreadProxy::GpuSetColorCorrection( - int64_t id, - const std::vector<display::GammaRampRGBEntry>& degamma_lut, - const std::vector<display::GammaRampRGBEntry>& gamma_lut, - const std::vector<float>& correction_matrix) { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - if (!drm_thread_ || !drm_thread_->IsRunning()) - return false; - drm_thread_->task_runner()->PostTask( - FROM_HERE, - base::Bind(&DrmThread::SetColorCorrection, base::Unretained(drm_thread_), - id, degamma_lut, gamma_lut, correction_matrix)); - return true; -} - -void MusThreadProxy::GpuCheckOverlayCapabilitiesCallback( - gfx::AcceleratedWidget widget, - const std::vector<OverlayCheck_Params>& overlays, - const std::vector<OverlayCheckReturn_Params>& returns) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - overlay_manager_->GpuSentOverlayResult(widget, overlays, returns); -} - -void MusThreadProxy::GpuConfigureNativeDisplayCallback(int64_t display_id, - bool success) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_->GpuConfiguredDisplay(display_id, success); -} - -void MusThreadProxy::GpuRefreshNativeDisplaysCallback( - std::vector<std::unique_ptr<display::DisplaySnapshotMojo>> displays) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_->GpuHasUpdatedNativeDisplays( - CreateParamsFromSnapshot(displays)); -} - -void MusThreadProxy::GpuDisableNativeDisplayCallback(int64_t display_id, - bool success) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_->GpuConfiguredDisplay(display_id, success); -} - -void MusThreadProxy::GpuTakeDisplayControlCallback(bool success) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_->GpuTookDisplayControl(success); -} - -void MusThreadProxy::GpuRelinquishDisplayControlCallback(bool success) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_->GpuRelinquishedDisplayControl(success); -} - -void MusThreadProxy::GpuGetHDCPStateCallback(int64_t display_id, - bool success, - display::HDCPState state) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_->GpuReceivedHDCPState(display_id, success, state); -} - -void MusThreadProxy::GpuSetHDCPStateCallback(int64_t display_id, - bool success) const { - DCHECK(on_window_server_thread_.CalledOnValidThread()); - display_manager_->GpuUpdatedHDCPState(display_id, success); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc index 109c5a97ed2..cc0bb2f5720 100644 --- a/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc +++ b/chromium/ui/ozone/platform/drm/ozone_platform_gbm.cc @@ -17,6 +17,8 @@ #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" #include "services/service_manager/public/cpp/binder_registry.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" @@ -25,7 +27,6 @@ #include "ui/events/ozone/evdev/event_factory_evdev.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #include "ui/ozone/platform/drm/common/drm_util.h" -#include "ui/ozone/platform/drm/cursor_proxy_mojo.h" #include "ui/ozone/platform/drm/gpu/drm_device_generator.h" #include "ui/ozone/platform/drm/gpu/drm_device_manager.h" #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h" @@ -42,7 +43,7 @@ #include "ui/ozone/platform/drm/host/drm_overlay_manager.h" #include "ui/ozone/platform/drm/host/drm_window_host.h" #include "ui/ozone/platform/drm/host/drm_window_host_manager.h" -#include "ui/ozone/platform/drm/mus_thread_proxy.h" +#include "ui/ozone/platform/drm/host/host_drm_device.h" #include "ui/ozone/public/cursor_factory_ozone.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/ozone_platform.h" @@ -81,7 +82,8 @@ class GlApiLoader { class OzonePlatformGbm : public OzonePlatform { public: - OzonePlatformGbm() : using_mojo_(false), single_process_(false) {} + OzonePlatformGbm() + : using_mojo_(false), single_process_(false), weak_factory_(this) {} ~OzonePlatformGbm() override {} // OzonePlatform: @@ -106,26 +108,45 @@ class OzonePlatformGbm : public OzonePlatform { std::unique_ptr<SystemInputInjector> CreateSystemInputInjector() override { return event_factory_ozone_->CreateSystemInputInjector(); } + void AddInterfaces( service_manager::BinderRegistryWithArgs< const service_manager::BindSourceInfo&>* registry) override { registry->AddInterface<ozone::mojom::DeviceCursor>( - base::Bind(&OzonePlatformGbm::Create, base::Unretained(this)), + base::Bind(&OzonePlatformGbm::CreateDeviceCursorBinding, + weak_factory_.GetWeakPtr()), + gpu_task_runner_); + + registry->AddInterface<ozone::mojom::DrmDevice>( + base::Bind(&OzonePlatformGbm::CreateDrmDeviceBinding, + weak_factory_.GetWeakPtr()), gpu_task_runner_); } - void Create(ozone::mojom::DeviceCursorRequest request, - const service_manager::BindSourceInfo& source_info) { + void CreateDeviceCursorBinding( + ozone::mojom::DeviceCursorRequest request, + const service_manager::BindSourceInfo& source_info) { if (drm_thread_proxy_) - drm_thread_proxy_->AddBinding(std::move(request)); + drm_thread_proxy_->AddBindingCursorDevice(std::move(request)); else pending_cursor_requests_.push_back(std::move(request)); } + + // service_manager::InterfaceFactory<ozone::mojom::DrmDevice>: + void CreateDrmDeviceBinding( + ozone::mojom::DrmDeviceRequest request, + const service_manager::BindSourceInfo& source_info) { + if (drm_thread_proxy_) + drm_thread_proxy_->AddBindingDrmDevice(std::move(request)); + else + pending_gpu_adapter_requests_.push_back(std::move(request)); + } + std::unique_ptr<PlatformWindow> CreatePlatformWindow( PlatformWindowDelegate* delegate, const gfx::Rect& bounds) override { GpuThreadAdapter* adapter = gpu_platform_support_host_.get(); if (using_mojo_ || single_process_) { - adapter = mus_thread_proxy_.get(); + adapter = host_drm_device_.get(); } std::unique_ptr<DrmWindowHost> platform_window(new DrmWindowHost( @@ -140,18 +161,18 @@ class OzonePlatformGbm : public OzonePlatform { } void InitializeUI(const InitParams& args) override { // Ozone drm can operate in three modes configured at runtime: - // 1. legacy mode where browser and gpu components communicate + // 1. legacy mode where host and viz components communicate // via param traits IPC. - // 2. single-process mode where browser and gpu components - // communicate via PostTask. - // 3. mojo mode where browser and gpu components communicate + // 2. single-process mode where host and viz components + // communicate via in-process mojo. + // 3. multi-process mode where host and viz components communicate // via mojo IPC. - // Currently, mojo mode uses mojo in a single process but this is - // an interim implementation detail that will be eliminated in a - // future CL. single_process_ = args.single_process; using_mojo_ = args.connector != nullptr; - DCHECK(!(using_mojo_ && single_process_)); + host_thread_ = base::PlatformThread::CurrentRef(); + + DCHECK(!(using_mojo_ && !single_process_)) + << "Multiprocess Mojo is not supported yet."; device_manager_ = CreateDeviceManager(); window_manager_.reset(new DrmWindowHostManager()); @@ -170,18 +191,13 @@ class OzonePlatformGbm : public OzonePlatform { GpuThreadAdapter* adapter; - // TODO(rjkroege): Once mus is split, only do this for single_process. - if (single_process_ || using_mojo_) + if (single_process_) gl_api_loader_.reset(new GlApiLoader()); if (using_mojo_) { - mus_thread_proxy_ = - base::MakeUnique<MusThreadProxy>(cursor_.get(), args.connector); - adapter = mus_thread_proxy_.get(); - } else if (single_process_) { - mus_thread_proxy_ = - base::MakeUnique<MusThreadProxy>(cursor_.get(), nullptr); - adapter = mus_thread_proxy_.get(); + host_drm_device_ = + base::MakeUnique<HostDrmDevice>(cursor_.get(), args.connector); + adapter = host_drm_device_.get(); } else { gpu_platform_support_host_.reset( new DrmGpuPlatformSupportHost(cursor_.get())); @@ -195,24 +211,27 @@ class OzonePlatformGbm : public OzonePlatform { event_factory_ozone_->input_controller())); cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone); - if (using_mojo_ || single_process_) { - mus_thread_proxy_->ProvideManagers(display_manager_.get(), - overlay_manager_.get()); + if (using_mojo_) { + host_drm_device_->ProvideManagers(display_manager_.get(), + overlay_manager_.get()); + host_drm_device_->AsyncStartDrmDevice(); } } + void InitializeGPU(const InitParams& args) override { // TODO(rjkroege): services/ui should initialize this with a connector. // However, in-progress refactorings in services/ui make it difficult to // require this at present. Set using_mojo_ like below once this is // complete. + // TODO(rjk): Make it possible to turn this on. // using_mojo_ = args.connector != nullptr; - gpu_task_runner_ = base::ThreadTaskRunnerHandle::Get(); - InterThreadMessagingProxy* itmp; - if (using_mojo_ || single_process_) { - itmp = mus_thread_proxy_.get(); - } else { + + if (!single_process_) gl_api_loader_.reset(new GlApiLoader()); + + InterThreadMessagingProxy* itmp; + if (!using_mojo_) { scoped_refptr<DrmThreadMessageProxy> message_proxy( new DrmThreadMessageProxy()); itmp = message_proxy.get(); @@ -222,40 +241,77 @@ class OzonePlatformGbm : public OzonePlatform { // NOTE: Can't start the thread here since this is called before sandbox // initialization in multi-process Chrome. In mus, we start the DRM thread. drm_thread_proxy_.reset(new DrmThreadProxy()); - drm_thread_proxy_->BindThreadIntoMessagingProxy(itmp); surface_factory_.reset(new GbmSurfaceFactory(drm_thread_proxy_.get())); - if (using_mojo_ || single_process_) { - mus_thread_proxy_->StartDrmThread(); + if (!using_mojo_) { + drm_thread_proxy_->BindThreadIntoMessagingProxy(itmp); + } else { + drm_thread_proxy_->StartDrmThread(); } + + // When the viz process (and hence the gpu portion of ozone/gbm) is + // operating in a single process, the AddInterfaces method is best + // invoked before the GPU thread launches. As a result, requests to add + // mojom bindings to the as yet un-launched service will fail so we queue + // incoming binding requests until the GPU thread is running and play them + // back here. for (auto& request : pending_cursor_requests_) - drm_thread_proxy_->AddBinding(std::move(request)); + drm_thread_proxy_->AddBindingCursorDevice(std::move(request)); pending_cursor_requests_.clear(); + for (auto& request : pending_gpu_adapter_requests_) + drm_thread_proxy_->AddBindingDrmDevice(std::move(request)); + pending_gpu_adapter_requests_.clear(); + + // If InitializeGPU and InitializeUI are invoked on the same thread, startup + // sequencing is complicated because tasks are queued on the unbound mojo + // pipe connecting the UI (the host) to the DRM thread before the DRM thread + // is launched above. Special case this sequence vis the + // BlockingStartDrmDevice API. + // TODO(rjkroege): In a future when we have completed splitting Viz, it will + // be possible to simplify this logic. + if (using_mojo_ && single_process_ && + host_thread_ == base::PlatformThread::CurrentRef()) { + CHECK(host_drm_device_) + << "Mojo single-process mode requires a HostDrmDevice."; + host_drm_device_->BlockingStartDrmDevice(); + } } private: bool using_mojo_; bool single_process_; - - // Bridges the DRM, GPU and main threads in mus. This must be destroyed last. - std::unique_ptr<MusThreadProxy> mus_thread_proxy_; + base::PlatformThreadRef host_thread_; // Objects in the GPU process. std::unique_ptr<DrmThreadProxy> drm_thread_proxy_; std::unique_ptr<GlApiLoader> gl_api_loader_; std::unique_ptr<GbmSurfaceFactory> surface_factory_; scoped_refptr<IPC::MessageFilter> gpu_message_filter_; - // TODO(sad): Once the mus gpu process split happens, this can go away. - std::vector<ozone::mojom::DeviceCursorRequest> pending_cursor_requests_; scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_; + // TODO(rjkroege,sadrul): Provide a more elegant solution for this issue when + // running in single process mode. + std::vector<ozone::mojom::DeviceCursorRequest> pending_cursor_requests_; + std::vector<ozone::mojom::DrmDeviceRequest> pending_gpu_adapter_requests_; + + // gpu_platform_support_host_ is the IPC bridge to the GPU process while + // host_drm_device_ is the mojo bridge to the Viz process. Only one can be in + // use at any time. + // TODO(rjkroege): Remove gpu_platform_support_host_ once ozone/drm with mojo + // has reached the stable channel. + // A raw pointer to either |gpu_platform_support_host_| or |host_drm_device_| + // is passed to |display_manager_| and |overlay_manager_| in IntializeUI. + // To avoid a use after free, the following two members should be declared + // before the two managers, so that they're deleted after them. + std::unique_ptr<DrmGpuPlatformSupportHost> gpu_platform_support_host_; + std::unique_ptr<HostDrmDevice> host_drm_device_; + // Objects in the Browser process. std::unique_ptr<DeviceManager> device_manager_; std::unique_ptr<BitmapCursorFactoryOzone> cursor_factory_ozone_; std::unique_ptr<DrmWindowHostManager> window_manager_; std::unique_ptr<DrmCursor> cursor_; std::unique_ptr<EventFactoryEvdev> event_factory_ozone_; - std::unique_ptr<DrmGpuPlatformSupportHost> gpu_platform_support_host_; std::unique_ptr<DrmDisplayHostManager> display_manager_; std::unique_ptr<DrmOverlayManager> overlay_manager_; @@ -263,6 +319,8 @@ class OzonePlatformGbm : public OzonePlatform { XkbEvdevCodes xkb_evdev_code_converter_; #endif + base::WeakPtrFactory<OzonePlatformGbm> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(OzonePlatformGbm); }; diff --git a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc index d3c9f35a05f..40583a3527a 100644 --- a/chromium/ui/ozone/platform/headless/headless_surface_factory.cc +++ b/chromium/ui/ozone/platform/headless/headless_surface_factory.cc @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/task_scheduler/post_task.h" +#include "build/build_config.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" #include "ui/gfx/codec/png_codec.h" @@ -108,14 +109,21 @@ HeadlessSurfaceFactory::HeadlessSurfaceFactory() HeadlessSurfaceFactory::HeadlessSurfaceFactory( HeadlessWindowManager* window_manager) - : window_manager_(window_manager), - osmesa_implementation_(base::MakeUnique<GLOzoneOSMesa>()) {} + : window_manager_(window_manager) { +#if !defined(OS_FUCHSIA) + osmesa_implementation_ = base::MakeUnique<GLOzoneOSMesa>(); +#endif +} HeadlessSurfaceFactory::~HeadlessSurfaceFactory() {} std::vector<gl::GLImplementation> HeadlessSurfaceFactory::GetAllowedGLImplementations() { +#if defined(OS_FUCHSIA) + return std::vector<gl::GLImplementation>{gl::kGLImplementationStubGL}; +#else return std::vector<gl::GLImplementation>{gl::kGLImplementationOSMesaGL}; +#endif } GLOzone* HeadlessSurfaceFactory::GetGLOzone( diff --git a/chromium/ui/ozone/platform/headless/headless_window_manager.h b/chromium/ui/ozone/platform/headless/headless_window_manager.h index d57f091e888..f0ee9120034 100644 --- a/chromium/ui/ozone/platform/headless/headless_window_manager.h +++ b/chromium/ui/ozone/platform/headless/headless_window_manager.h @@ -9,8 +9,8 @@ #include <memory> +#include "base/containers/id_map.h" #include "base/files/file_path.h" -#include "base/id_map.h" #include "base/macros.h" #include "base/threading/thread_checker.h" #include "ui/gfx/native_widget_types.h" @@ -43,7 +43,7 @@ class HeadlessWindowManager { private: base::FilePath location_; - IDMap<HeadlessWindow*> windows_; + base::IDMap<HeadlessWindow*> windows_; base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(HeadlessWindowManager); diff --git a/chromium/ui/ozone/platform/wayland/gl_surface_wayland.cc b/chromium/ui/ozone/platform/wayland/gl_surface_wayland.cc index d1e64cc216c..5827383eefe 100644 --- a/chromium/ui/ozone/platform/wayland/gl_surface_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gl_surface_wayland.cc @@ -35,6 +35,7 @@ GLSurfaceWayland::GLSurfaceWayland(WaylandEglWindowPtr egl_window) bool GLSurfaceWayland::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { if (size_ == size) return true; diff --git a/chromium/ui/ozone/platform/wayland/gl_surface_wayland.h b/chromium/ui/ozone/platform/wayland/gl_surface_wayland.h index ca27fbf7133..27b81f0e192 100644 --- a/chromium/ui/ozone/platform/wayland/gl_surface_wayland.h +++ b/chromium/ui/ozone/platform/wayland/gl_surface_wayland.h @@ -34,6 +34,7 @@ class GLSurfaceWayland : public gl::NativeViewGLSurfaceEGL { // gl::GLSurface: bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; EGLConfig GetConfig() override; diff --git a/chromium/ui/ozone/platform/wayland/wayland_window.cc b/chromium/ui/ozone/platform/wayland/wayland_window.cc index 85cfee522e4..a7f4313bd13 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_window.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_window.cc @@ -96,7 +96,7 @@ gfx::Rect WaylandWindow::GetBounds() { void WaylandWindow::SetTitle(const base::string16& title) { DCHECK(xdg_surface_); - xdg_surface_set_title(xdg_surface_.get(), UTF16ToUTF8(title).c_str()); + xdg_surface_set_title(xdg_surface_.get(), base::UTF16ToUTF8(title).c_str()); connection_->ScheduleFlush(); } diff --git a/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.cc b/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.cc index 0fc232ee590..ea4348778d7 100644 --- a/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.cc +++ b/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.cc @@ -80,6 +80,7 @@ EGLConfig GLSurfaceEGLOzoneX11::GetConfig() { bool GLSurfaceEGLOzoneX11::Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) { if (size == GetSize()) return true; diff --git a/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h b/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h index ec044651872..dcb419083b9 100644 --- a/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h +++ b/chromium/ui/ozone/platform/x11/gl_surface_egl_ozone_x11.h @@ -21,6 +21,7 @@ class GLSurfaceEGLOzoneX11 : public gl::NativeViewGLSurfaceEGL { EGLConfig GetConfig() override; bool Resize(const gfx::Size& size, float scale_factor, + ColorSpace color_space, bool has_alpha) override; private: diff --git a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc index 072c5b02551..58176cc0fbc 100644 --- a/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc +++ b/chromium/ui/ozone/platform/x11/ozone_platform_x11.cc @@ -9,7 +9,6 @@ #include <memory> #include <utility> -#include "base/command_line.h" #include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" @@ -32,16 +31,6 @@ namespace ui { namespace { -// Returns true if a flag is present that will cause Ozone UI and GPU to run in -// the same process. -// TODO(kylechar): Remove --mojo-platform-channel-handle when mus-ws process -// split happens. -bool HasSingleProcessFlag() { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - return command_line->HasSwitch("mojo-platform-channel-handle") || - command_line->HasSwitch("single-process"); -} - // Singleton OzonePlatform implementation for X11 platform. class OzonePlatformX11 : public OzonePlatform { public: @@ -106,7 +95,7 @@ class OzonePlatformX11 : public OzonePlatform { // In single process mode either the UI thread will create an event source // or it's a test and an event source isn't desired. - if (!params.single_process && !HasSingleProcessFlag()) + if (!params.single_process) CreatePlatformEventSource(); surface_factory_ozone_ = base::MakeUnique<X11SurfaceFactory>(); @@ -127,7 +116,7 @@ class OzonePlatformX11 : public OzonePlatform { return; // In single process mode XInitThreads() must be the first Xlib call. - if (params.single_process || HasSingleProcessFlag()) + if (params.single_process) XInitThreads(); ui::SetDefaultX11ErrorHandlers(); diff --git a/chromium/ui/ozone/public/input_controller.cc b/chromium/ui/ozone/public/input_controller.cc index 5aa300240b4..7ee1a3712b6 100644 --- a/chromium/ui/ozone/public/input_controller.cc +++ b/chromium/ui/ozone/public/input_controller.cc @@ -40,6 +40,7 @@ class StubInputController : public InputController { void SetNaturalScroll(bool enabled) override; void SetMouseSensitivity(int value) override; void SetPrimaryButtonRight(bool right) override; + void SetMouseReverseScroll(bool enabled) override; void SetTapToClickPaused(bool state) override; void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) override; void GetTouchEventLog(const base::FilePath& out_dir, @@ -121,6 +122,9 @@ void StubInputController::SetMouseSensitivity(int value) { void StubInputController::SetPrimaryButtonRight(bool right) { } +void StubInputController::SetMouseReverseScroll(bool enabled) { +} + void StubInputController::SetTapToClickPaused(bool state) { } diff --git a/chromium/ui/ozone/public/input_controller.h b/chromium/ui/ozone/public/input_controller.h index cdec1d54b65..f791a8b5022 100644 --- a/chromium/ui/ozone/public/input_controller.h +++ b/chromium/ui/ozone/public/input_controller.h @@ -65,6 +65,7 @@ class OZONE_BASE_EXPORT InputController { // Mouse settings. virtual void SetMouseSensitivity(int value) = 0; virtual void SetPrimaryButtonRight(bool right) = 0; + virtual void SetMouseReverseScroll(bool enabled) = 0; // Touch log collection. virtual void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) = 0; diff --git a/chromium/ui/ozone/public/interfaces/BUILD.gn b/chromium/ui/ozone/public/interfaces/BUILD.gn index 48221f5185c..f66bf8a1248 100644 --- a/chromium/ui/ozone/public/interfaces/BUILD.gn +++ b/chromium/ui/ozone/public/interfaces/BUILD.gn @@ -7,11 +7,14 @@ import("//mojo/public/tools/bindings/mojom.gni") mojom("interfaces") { sources = [ "device_cursor.mojom", + "drm_device.mojom", "overlay_surface_candidate.mojom", ] public_deps = [ + "//mojo/common:common_custom_types", "//skia/public/interfaces:interfaces", + "//ui/display/mojo:interfaces", "//ui/gfx/geometry/mojo", "//ui/gfx/mojo", ] diff --git a/chromium/ui/ozone/public/interfaces/drm_device.mojom b/chromium/ui/ozone/public/interfaces/drm_device.mojom new file mode 100644 index 00000000000..eede42437b0 --- /dev/null +++ b/chromium/ui/ozone/public/interfaces/drm_device.mojom @@ -0,0 +1,86 @@ +// 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. + +module ui.ozone.mojom; + +import "mojo/common/file.mojom"; +import "mojo/common/file_path.mojom"; +import "ui/display/mojo/display_constants.mojom"; +import "ui/display/mojo/display_mode.mojom"; +import "ui/display/mojo/display_snapshot.mojom"; +import "ui/display/mojo/gamma_ramp_rgb_entry.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; +import "ui/gfx/mojo/accelerated_widget.mojom"; +import "ui/ozone/public/interfaces/overlay_surface_candidate.mojom"; + + +// The viz process on CrOS implements the DrmDevice +// service to let the viz host and clients manage DRM displays. +// All functions in DrmDevice are implemented by the lower privilege viz +// process. +interface DrmDevice { + // Starts the DRM service and returns true on success. + [Sync] + StartDrmDevice() => (bool success); + + // Creates scanout capable DRM buffers to back |widget|. + CreateWindow(gfx.mojom.AcceleratedWidget widget); + + // Destroys the DRM buffers backing |widget|. + DestroyWindow(gfx.mojom.AcceleratedWidget widget); + + // Sets the size of the DRM buffer for |widget|. + SetWindowBounds(gfx.mojom.AcceleratedWidget widget, gfx.mojom.Rect bounds); + + // Takes control of the display and invoke a provided callback with a boolean + // status. + TakeDisplayControl() => (bool success); + + // Releases control of the display and invoke a provided callback with a + // boolean status. + RelinquishDisplayControl() => (bool success); + + // Requests a callback providing a list of the available displays. + RefreshNativeDisplays() => + (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); + + // Instructs the GPU to abandon a DRM device. + RemoveGraphicsDevice(mojo.common.mojom.FilePath path); + + // Instructs the GPU to disable a DRM device. + DisableNativeDisplay(int64 display_id) => (int64 display_id, bool success); + + // Configures a DRM display returning true on success. + ConfigureNativeDisplay(int64 display_id, + display.mojom.DisplayMode display_mode, + gfx.mojom.Point point) => + (int64 display_id, bool success); + + // Gets or sets high-definition content protection (HDCP) (DRM as in + // digital rights management) state. + GetHDCPState(int64 display_id) => + (int64 display_id, bool success, display.mojom.HDCPState state); + SetHDCPState(int64 display_id, display.mojom.HDCPState state) => + (int64 display_id, bool success); + + // Sets a color correction gamma table. + SetColorCorrection(int64 display_id, + array<display.mojom.GammaRampRGBEntry> degamma_lut, + array<display.mojom.GammaRampRGBEntry> gamma_lut, + array<float> correction_matrix); + + // Verifies if the display controller can successfully scanout the given set + // of OverlaySurfaceCandidates and return the status associated with each + // candidate. + CheckOverlayCapabilities(gfx.mojom.AcceleratedWidget widget, + array<ui.ozone.mojom.OverlaySurfaceCandidate> candidates) => + (gfx.mojom.AcceleratedWidget widget, + array<ui.ozone.mojom.OverlaySurfaceCandidate> candidates, + array<ui.ozone.mojom.OverlayStatus> status); +}; + diff --git a/chromium/ui/ozone/public/overlay_candidates_ozone.h b/chromium/ui/ozone/public/overlay_candidates_ozone.h index 760721feea0..8ccc8610215 100644 --- a/chromium/ui/ozone/public/overlay_candidates_ozone.h +++ b/chromium/ui/ozone/public/overlay_candidates_ozone.h @@ -17,7 +17,7 @@ namespace ui { // class from SurfaceFactoryOzone given an AcceleratedWidget. class OZONE_BASE_EXPORT OverlayCandidatesOzone { public: - typedef std::vector<OverlaySurfaceCandidate> OverlaySurfaceCandidateList; + using OverlaySurfaceCandidateList = std::vector<OverlaySurfaceCandidate>; // A list of possible overlay candidates is presented to this function. // The expected result is that those candidates that can be in a separate diff --git a/chromium/ui/ozone/public/overlay_surface_candidate.cc b/chromium/ui/ozone/public/overlay_surface_candidate.cc index 3612d21810f..9065638d37a 100644 --- a/chromium/ui/ozone/public/overlay_surface_candidate.cc +++ b/chromium/ui/ozone/public/overlay_surface_candidate.cc @@ -4,6 +4,8 @@ #include "ui/ozone/public/overlay_surface_candidate.h" +#include "ui/gfx/geometry/rect_conversions.h" + namespace ui { OverlaySurfaceCandidate::OverlaySurfaceCandidate() : is_clipped(false) {} @@ -13,4 +15,18 @@ OverlaySurfaceCandidate::OverlaySurfaceCandidate( OverlaySurfaceCandidate::~OverlaySurfaceCandidate() {} +bool OverlaySurfaceCandidate::operator<( + const OverlaySurfaceCandidate& param) const { + int lwidth = buffer_size.width(); + int lheight = buffer_size.height(); + int rwidth = param.buffer_size.width(); + int rheight = param.buffer_size.height(); + gfx::Rect lrect = gfx::ToNearestRect(display_rect); + gfx::Rect rrect = gfx::ToNearestRect(param.display_rect); + + return std::tie(plane_z_order, format, lrect, lwidth, lheight, transform) < + std::tie(param.plane_z_order, param.format, rrect, rwidth, rheight, + param.transform); +} + } // namespace ui diff --git a/chromium/ui/ozone/public/overlay_surface_candidate.h b/chromium/ui/ozone/public/overlay_surface_candidate.h index 904d69f5df8..aee7095af0c 100644 --- a/chromium/ui/ozone/public/overlay_surface_candidate.h +++ b/chromium/ui/ozone/public/overlay_surface_candidate.h @@ -5,6 +5,8 @@ #ifndef UI_OZONE_PUBLIC_OVERLAY_SURFACE_CANDIDATE_H_ #define UI_OZONE_PUBLIC_OVERLAY_SURFACE_CANDIDATE_H_ +#include <vector> + #include "ui/gfx/buffer_types.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect_f.h" @@ -27,6 +29,8 @@ class OZONE_BASE_EXPORT OverlaySurfaceCandidate { OverlaySurfaceCandidate(const OverlaySurfaceCandidate& other); ~OverlaySurfaceCandidate(); + bool operator<(const OverlaySurfaceCandidate& plane) const; + // Transformation to apply to layer during composition. gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_NONE; // Format of the buffer to composite. @@ -52,6 +56,9 @@ class OZONE_BASE_EXPORT OverlaySurfaceCandidate { bool overlay_handled = false; }; +using OverlaySurfaceCandidateList = std::vector<OverlaySurfaceCandidate>; +using OverlayStatusList = std::vector<OverlayStatus>; + } // namespace ui #endif // UI_OZONE_PUBLIC_OVERLAY_SURFACE_CANDIDATE_H_ diff --git a/chromium/ui/platform_window/android/platform_ime_controller_android.cc b/chromium/ui/platform_window/android/platform_ime_controller_android.cc index d9e0edfb4da..99cf38fa016 100644 --- a/chromium/ui/platform_window/android/platform_ime_controller_android.cc +++ b/chromium/ui/platform_window/android/platform_ime_controller_android.cc @@ -13,11 +13,6 @@ using base::android::ScopedJavaLocalRef; namespace ui { -// static -bool PlatformImeControllerAndroid::Register(JNIEnv* env) { - return RegisterNativesImpl(env); -} - PlatformImeControllerAndroid::PlatformImeControllerAndroid() { } diff --git a/chromium/ui/platform_window/android/platform_ime_controller_android.h b/chromium/ui/platform_window/android/platform_ime_controller_android.h index 69d9682cc1b..ed47c6285e4 100644 --- a/chromium/ui/platform_window/android/platform_ime_controller_android.h +++ b/chromium/ui/platform_window/android/platform_ime_controller_android.h @@ -15,8 +15,6 @@ namespace ui { class ANDROID_WINDOW_EXPORT PlatformImeControllerAndroid : public PlatformImeController { public: - static bool Register(JNIEnv* env); - PlatformImeControllerAndroid(); ~PlatformImeControllerAndroid() override; diff --git a/chromium/ui/platform_window/android/platform_window_android.cc b/chromium/ui/platform_window/android/platform_window_android.cc index 97bb914598a..c32b24a3dac 100644 --- a/chromium/ui/platform_window/android/platform_window_android.cc +++ b/chromium/ui/platform_window/android/platform_window_android.cc @@ -49,11 +49,6 @@ ui::EventType MotionEventActionToEventType(jint action) { //////////////////////////////////////////////////////////////////////////////// // PlatformWindowAndroid, public: -// static -bool PlatformWindowAndroid::Register(JNIEnv* env) { - return RegisterNativesImpl(env); -} - PlatformWindowAndroid::PlatformWindowAndroid(PlatformWindowDelegate* delegate) : delegate_(delegate), window_(NULL), diff --git a/chromium/ui/platform_window/android/platform_window_android.h b/chromium/ui/platform_window/android/platform_window_android.h index 6ae06813727..599ff71883f 100644 --- a/chromium/ui/platform_window/android/platform_window_android.h +++ b/chromium/ui/platform_window/android/platform_window_android.h @@ -24,8 +24,6 @@ class PlatformWindowDelegate; class ANDROID_WINDOW_EXPORT PlatformWindowAndroid : public PlatformWindow { public: - static bool Register(JNIEnv* env); - explicit PlatformWindowAndroid(PlatformWindowDelegate* delegate); ~PlatformWindowAndroid() override; diff --git a/chromium/ui/platform_window/stub/stub_window.h b/chromium/ui/platform_window/stub/stub_window.h index 080e945cdd1..b171e0a8f0e 100644 --- a/chromium/ui/platform_window/stub/stub_window.h +++ b/chromium/ui/platform_window/stub/stub_window.h @@ -15,7 +15,7 @@ namespace ui { class PlatformWindowDelegate; -class STUB_WINDOW_EXPORT StubWindow : NON_EXPORTED_BASE(public PlatformWindow) { +class STUB_WINDOW_EXPORT StubWindow : public PlatformWindow { public: explicit StubWindow(PlatformWindowDelegate* delegate, bool use_default_accelerated_widget = true, diff --git a/chromium/ui/platform_window/win/win_window.h b/chromium/ui/platform_window/win/win_window.h index d91d8086e6a..5e4859ce247 100644 --- a/chromium/ui/platform_window/win/win_window.h +++ b/chromium/ui/platform_window/win/win_window.h @@ -15,7 +15,7 @@ namespace ui { class PlatformWindowDelegate; -class WIN_WINDOW_EXPORT WinWindow : public NON_EXPORTED_BASE(PlatformWindow), +class WIN_WINDOW_EXPORT WinWindow : public PlatformWindow, public gfx::WindowImpl { public: WinWindow(PlatformWindowDelegate* delegate, const gfx::Rect& bounds); diff --git a/chromium/ui/resources/BUILD.gn b/chromium/ui/resources/BUILD.gn index e81141e3203..cee10c4ff0f 100644 --- a/chromium/ui/resources/BUILD.gn +++ b/chromium/ui/resources/BUILD.gn @@ -238,7 +238,7 @@ repack("repack_ui_test_pak_200_percent") { } # Repack just the strings for the framework locales on Mac and iOS. This -# emulates repack_locales.py, but just for en-US. Note ui_test.pak is not simply +# emulates repack_locales(), but just for en-US. Note ui_test.pak is not simply # copied, because it causes leaks from allocations within system libraries when # trying to load non-string resources. http://crbug.com/413034. repack("repack_ui_test_mac_locale_pack") { diff --git a/chromium/ui/shell_dialogs/BUILD.gn b/chromium/ui/shell_dialogs/BUILD.gn index 505bf26edea..74080cd385d 100644 --- a/chromium/ui/shell_dialogs/BUILD.gn +++ b/chromium/ui/shell_dialogs/BUILD.gn @@ -51,8 +51,6 @@ component("shell_dialogs") { if (is_android && !use_aura) { sources += [ - "android/shell_dialogs_jni_registrar.cc", - "android/shell_dialogs_jni_registrar.h", "select_file_dialog_android.cc", "select_file_dialog_android.h", ] @@ -71,6 +69,10 @@ component("shell_dialogs") { "AppKit.framework", ] } + + if (is_fuchsia) { + sources += [ "select_file_dialog_fuchsia.cc" ] + } } test("shell_dialogs_unittests") { diff --git a/chromium/ui/shell_dialogs/select_file_dialog.cc b/chromium/ui/shell_dialogs/select_file_dialog.cc index e10afac1b6b..9b60383e547 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog.cc @@ -41,17 +41,17 @@ void SelectFileDialog::Listener::FileSelectedWithExtraInfo( // Most of the dialogs need actual local path, so default to it. // If local path is empty, use file_path instead. FileSelected(file.local_path.empty() ? file.file_path : file.local_path, - index, - params); + index, params); } void SelectFileDialog::Listener::MultiFilesSelectedWithExtraInfo( const std::vector<ui::SelectedFileInfo>& files, void* params) { std::vector<base::FilePath> file_paths; - for (size_t i = 0; i < files.size(); ++i) - file_paths.push_back(files[i].local_path.empty() ? files[i].file_path - : files[i].local_path); + for (const ui::SelectedFileInfo& file : files) { + file_paths.push_back(file.local_path.empty() ? file.file_path + : file.local_path); + } MultiFilesSelected(file_paths, params); } @@ -65,14 +65,10 @@ void SelectFileDialog::SetFactory(ui::SelectFileDialogFactory* factory) { // static scoped_refptr<SelectFileDialog> SelectFileDialog::Create( Listener* listener, - ui::SelectFilePolicy* policy) { - if (dialog_factory_) { - SelectFileDialog* dialog = dialog_factory_->Create(listener, policy); - if (dialog) - return dialog; - } - - return CreateSelectFileDialog(listener, policy); + std::unique_ptr<ui::SelectFilePolicy> policy) { + if (dialog_factory_) + return dialog_factory_->Create(listener, std::move(policy)); + return CreateSelectFileDialog(listener, std::move(policy)); } base::FilePath SelectFileDialog::GetShortenedFilePath( @@ -135,9 +131,8 @@ bool SelectFileDialog::HasMultipleFileTypeChoices() { } SelectFileDialog::SelectFileDialog(Listener* listener, - ui::SelectFilePolicy* policy) - : listener_(listener), - select_file_policy_(policy) { + std::unique_ptr<ui::SelectFilePolicy> policy) + : listener_(listener), select_file_policy_(std::move(policy)) { DCHECK(listener_); } diff --git a/chromium/ui/shell_dialogs/select_file_dialog.h b/chromium/ui/shell_dialogs/select_file_dialog.h index e5d288a07e9..c554ce2666a 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog.h +++ b/chromium/ui/shell_dialogs/select_file_dialog.h @@ -19,6 +19,7 @@ #include "ui/shell_dialogs/shell_dialogs_export.h" namespace ui { + class SelectFileDialogFactory; class SelectFilePolicy; struct SelectedFileInfo; @@ -102,8 +103,14 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog // Creates a dialog box helper. This is an inexpensive wrapper around the // platform-native file selection dialog. |policy| is an optional class that // can prevent showing a dialog. - static scoped_refptr<SelectFileDialog> Create(Listener* listener, - SelectFilePolicy* policy); + // + // The lifetime of the Listener is not managed by this class. The calling + // code should call always ListenerDestroyed() (on the base class + // BaseShellDialog) when the listener is destroyed since the SelectFileDialog + // is refcounted and uses a background thread. + static scoped_refptr<SelectFileDialog> Create( + Listener* listener, + std::unique_ptr<SelectFilePolicy> policy); // Holds information about allowed extensions on a file save dialog. struct SHELL_DIALOGS_EXPORT FileTypeInfo { @@ -117,7 +124,7 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog // // Only pass more than one extension in the inner vector if the extensions // are equivalent. Do NOT include leading periods. - std::vector<std::vector<base::FilePath::StringType> > extensions; + std::vector<std::vector<base::FilePath::StringType>> extensions; // Overrides the system descriptions of the specified extensions. Entries // correspond to |extensions|; if left blank the system descriptions will @@ -179,7 +186,9 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog protected: friend class base::RefCountedThreadSafe<SelectFileDialog>; - explicit SelectFileDialog(Listener* listener, SelectFilePolicy* policy); + + explicit SelectFileDialog(Listener* listener, + std::unique_ptr<SelectFilePolicy> policy); ~SelectFileDialog() override; // Displays the actual file-selection dialog. @@ -217,8 +226,10 @@ class SHELL_DIALOGS_EXPORT SelectFileDialog DISALLOW_COPY_AND_ASSIGN(SelectFileDialog); }; -SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, - SelectFilePolicy* policy); +SelectFileDialog* CreateSelectFileDialog( + SelectFileDialog::Listener* listener, + std::unique_ptr<SelectFilePolicy> policy); + } // namespace ui #endif // UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_H_ diff --git a/chromium/ui/shell_dialogs/select_file_dialog_android.cc b/chromium/ui/shell_dialogs/select_file_dialog_android.cc index 30c557d719a..954e19c34d9 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_android.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog_android.cc @@ -14,6 +14,7 @@ #include "base/strings/utf_string_conversions.h" #include "jni/SelectFileDialog_jni.h" #include "ui/android/window_android.h" +#include "ui/shell_dialogs/select_file_policy.h" #include "ui/shell_dialogs/selected_file_info.h" using base::android::ConvertJavaStringToUTF8; @@ -23,9 +24,10 @@ using base::android::ScopedJavaLocalRef; namespace ui { // static -SelectFileDialogImpl* SelectFileDialogImpl::Create(Listener* listener, - SelectFilePolicy* policy) { - return new SelectFileDialogImpl(listener, policy); +SelectFileDialogImpl* SelectFileDialogImpl::Create( + Listener* listener, + std::unique_ptr<SelectFilePolicy> policy) { + return new SelectFileDialogImpl(listener, std::move(policy)); } void SelectFileDialogImpl::OnFileSelected( @@ -125,16 +127,13 @@ void SelectFileDialogImpl::SelectFileImpl( owning_window->GetJavaObject()); } -bool SelectFileDialogImpl::RegisterSelectFileDialog(JNIEnv* env) { - return RegisterNativesImpl(env); -} - SelectFileDialogImpl::~SelectFileDialogImpl() { } -SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener, - SelectFilePolicy* policy) - : SelectFileDialog(listener, policy) { +SelectFileDialogImpl::SelectFileDialogImpl( + Listener* listener, + std::unique_ptr<SelectFilePolicy> policy) + : SelectFileDialog(listener, std::move(policy)) { JNIEnv* env = base::android::AttachCurrentThread(); java_object_.Reset( Java_SelectFileDialog_create(env, reinterpret_cast<intptr_t>(this))); @@ -145,9 +144,10 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { return false; } -SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { - return SelectFileDialogImpl::Create(listener, policy); +SelectFileDialog* CreateSelectFileDialog( + SelectFileDialog::Listener* listener, + std::unique_ptr<SelectFilePolicy> policy) { + return SelectFileDialogImpl::Create(listener, std::move(policy)); } } // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog_android.h b/chromium/ui/shell_dialogs/select_file_dialog_android.h index 5291cd3c92b..5ac283abf45 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_android.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_android.h @@ -17,7 +17,7 @@ namespace ui { class SelectFileDialogImpl : public SelectFileDialog { public: static SelectFileDialogImpl* Create(Listener* listener, - SelectFilePolicy* policy); + std::unique_ptr<SelectFilePolicy> policy); void OnFileSelected(JNIEnv* env, const base::android::JavaParamRef<jobject>& java_object, @@ -50,13 +50,12 @@ class SelectFileDialogImpl : public SelectFileDialog { gfx::NativeWindow owning_window, void* params) override; - static bool RegisterSelectFileDialog(JNIEnv* env); - protected: ~SelectFileDialogImpl() override; private: - SelectFileDialogImpl(Listener* listener, SelectFilePolicy* policy); + SelectFileDialogImpl(Listener* listener, + std::unique_ptr<SelectFilePolicy> policy); bool HasMultipleFileTypeChoicesImpl() override; diff --git a/chromium/ui/shell_dialogs/select_file_dialog_factory.h b/chromium/ui/shell_dialogs/select_file_dialog_factory.h index cb12e28be87..567f50de40b 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_factory.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_factory.h @@ -9,6 +9,7 @@ #include "ui/shell_dialogs/shell_dialogs_export.h" namespace ui { + class SelectFilePolicy; // Some chrome components want to create their own SelectFileDialog objects @@ -20,8 +21,9 @@ class SHELL_DIALOGS_EXPORT SelectFileDialogFactory { public: virtual ~SelectFileDialogFactory(); - virtual SelectFileDialog* Create(ui::SelectFileDialog::Listener* listener, - ui::SelectFilePolicy* policy) = 0; + virtual SelectFileDialog* Create( + ui::SelectFileDialog::Listener* listener, + std::unique_ptr<ui::SelectFilePolicy> policy) = 0; }; } // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog_fuchsia.cc b/chromium/ui/shell_dialogs/select_file_dialog_fuchsia.cc new file mode 100644 index 00000000000..ff3e29eeea3 --- /dev/null +++ b/chromium/ui/shell_dialogs/select_file_dialog_fuchsia.cc @@ -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. + +#include "ui/shell_dialogs/select_file_dialog.h" + +#include "base/logging.h" + +namespace ui { + +SelectFileDialog* CreateSelectFileDialog( + SelectFileDialog::Listener* listener, + std::unique_ptr<SelectFilePolicy> policy) { + // TODO(fuchsia): Port once we have UI, see https://crbug.com/746674. + NOTREACHED(); + return nullptr; +} + +} // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog_mac.h b/chromium/ui/shell_dialogs/select_file_dialog_mac.h index b3765ef4224..472e7e0392b 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_mac.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_mac.h @@ -30,7 +30,8 @@ class SelectFileDialogMacTest; // Exported for unit tests. class SHELL_DIALOGS_EXPORT SelectFileDialogImpl : public ui::SelectFileDialog { public: - SelectFileDialogImpl(Listener* listener, ui::SelectFilePolicy* policy); + SelectFileDialogImpl(Listener* listener, + std::unique_ptr<ui::SelectFilePolicy> policy); // BaseShellDialog implementation. bool IsRunning(gfx::NativeWindow parent_window) const override; diff --git a/chromium/ui/shell_dialogs/select_file_dialog_mac.mm b/chromium/ui/shell_dialogs/select_file_dialog_mac.mm index d944e479b15..e408e2158c9 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_mac.mm +++ b/chromium/ui/shell_dialogs/select_file_dialog_mac.mm @@ -20,6 +20,7 @@ #include "base/threading/thread_restrictions.h" #import "ui/base/cocoa/nib_loading.h" #include "ui/base/l10n/l10n_util_mac.h" +#include "ui/shell_dialogs/select_file_policy.h" #include "ui/strings/grit/ui_strings.h" namespace { @@ -87,12 +88,12 @@ NSString* GetDescriptionFromExtension(const base::FilePath::StringType& ext) { namespace ui { -SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener, - ui::SelectFilePolicy* policy) - : SelectFileDialog(listener, policy), - bridge_([[SelectFileDialogBridge alloc] - initWithSelectFileDialogImpl:this]) { -} +SelectFileDialogImpl::SelectFileDialogImpl( + Listener* listener, + std::unique_ptr<ui::SelectFilePolicy> policy) + : SelectFileDialog(listener, std::move(policy)), + bridge_( + [[SelectFileDialogBridge alloc] initWithSelectFileDialogImpl:this]) {} bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const { return parents_.find(parent_window) != parents_.end(); @@ -366,9 +367,10 @@ bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { return hasMultipleFileTypeChoices_; } -SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { - return new SelectFileDialogImpl(listener, policy); +SelectFileDialog* CreateSelectFileDialog( + SelectFileDialog::Listener* listener, + std::unique_ptr<SelectFilePolicy> policy) { + return new SelectFileDialogImpl(listener, std::move(policy)); } } // namespace ui diff --git a/chromium/ui/shell_dialogs/select_file_dialog_win.cc b/chromium/ui/shell_dialogs/select_file_dialog_win.cc index 411b8bd5b4e..a6f07f070e1 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_win.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog_win.cc @@ -31,6 +31,7 @@ #include "ui/base/win/open_file_name_win.h" #include "ui/gfx/native_widget_types.h" #include "ui/shell_dialogs/base_shell_dialog_win.h" +#include "ui/shell_dialogs/select_file_policy.h" #include "ui/strings/grit/ui_strings.h" namespace { @@ -163,7 +164,7 @@ class SelectFileDialogImpl : public ui::SelectFileDialog, public: SelectFileDialogImpl( Listener* listener, - ui::SelectFilePolicy* policy, + std::unique_ptr<ui::SelectFilePolicy> policy, const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl, const base::Callback<bool(OPENFILENAME*)>& get_save_file_name_impl); @@ -306,15 +307,14 @@ class SelectFileDialogImpl : public ui::SelectFileDialog, SelectFileDialogImpl::SelectFileDialogImpl( Listener* listener, - ui::SelectFilePolicy* policy, + std::unique_ptr<ui::SelectFilePolicy> policy, const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl, const base::Callback<bool(OPENFILENAME*)>& get_save_file_name_impl) - : SelectFileDialog(listener, policy), + : SelectFileDialog(listener, std::move(policy)), BaseShellDialogImpl(), has_multiple_file_type_choices_(false), get_open_file_name_impl_(get_open_file_name_impl), - get_save_file_name_impl_(get_save_file_name_impl) { -} + get_save_file_name_impl_(get_save_file_name_impl) {} SelectFileDialogImpl::~SelectFileDialogImpl() { } @@ -701,17 +701,18 @@ std::wstring AppendExtensionIfNeeded( SelectFileDialog* CreateWinSelectFileDialog( SelectFileDialog::Listener* listener, - SelectFilePolicy* policy, + std::unique_ptr<SelectFilePolicy> policy, const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl, const base::Callback<bool(OPENFILENAME* ofn)>& get_save_file_name_impl) { - return new SelectFileDialogImpl( - listener, policy, get_open_file_name_impl, get_save_file_name_impl); + return new SelectFileDialogImpl(listener, std::move(policy), + get_open_file_name_impl, + get_save_file_name_impl); } -SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { - return CreateWinSelectFileDialog(listener, - policy, +SelectFileDialog* CreateSelectFileDialog( + SelectFileDialog::Listener* listener, + std::unique_ptr<SelectFilePolicy> policy) { + return CreateWinSelectFileDialog(listener, std::move(policy), base::Bind(&CallBuiltinGetOpenFileName), base::Bind(&CallBuiltinGetSaveFileName)); } diff --git a/chromium/ui/shell_dialogs/select_file_dialog_win.h b/chromium/ui/shell_dialogs/select_file_dialog_win.h index e066c65f1d3..22ef919a919 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_win.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_win.h @@ -14,6 +14,7 @@ #include "ui/shell_dialogs/shell_dialogs_export.h" namespace ui { + class SelectFilePolicy; // Implementation detail exported for unit tests. @@ -24,7 +25,7 @@ SHELL_DIALOGS_EXPORT std::wstring AppendExtensionIfNeeded( SHELL_DIALOGS_EXPORT SelectFileDialog* CreateWinSelectFileDialog( SelectFileDialog::Listener* listener, - SelectFilePolicy* policy, + std::unique_ptr<SelectFilePolicy> policy, const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl, const base::Callback<bool(OPENFILENAME* ofn)>& get_save_file_name_impl); diff --git a/chromium/ui/shell_dialogs/select_file_policy.cc b/chromium/ui/shell_dialogs/select_file_policy.cc index 7e0f5861147..548e408e52f 100644 --- a/chromium/ui/shell_dialogs/select_file_policy.cc +++ b/chromium/ui/shell_dialogs/select_file_policy.cc @@ -6,6 +6,6 @@ namespace ui { -SelectFilePolicy::~SelectFilePolicy() {} +SelectFilePolicy::~SelectFilePolicy() = default; } // namespace ui diff --git a/chromium/ui/shell_dialogs/shell_dialog_linux.cc b/chromium/ui/shell_dialogs/shell_dialog_linux.cc index 685369c2baa..046c430dd46 100644 --- a/chromium/ui/shell_dialogs/shell_dialog_linux.cc +++ b/chromium/ui/shell_dialogs/shell_dialog_linux.cc @@ -4,6 +4,8 @@ #include "ui/shell_dialogs/shell_dialog_linux.h" +#include "ui/shell_dialogs/select_file_policy.h" + namespace { ui::ShellDialogLinux* g_shell_dialog_linux = nullptr; @@ -20,12 +22,13 @@ const ShellDialogLinux* ShellDialogLinux::instance() { return g_shell_dialog_linux; } -SelectFileDialog* CreateSelectFileDialog(SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) { +SelectFileDialog* CreateSelectFileDialog( + SelectFileDialog::Listener* listener, + std::unique_ptr<SelectFilePolicy> policy) { #if defined(USE_AURA) && !defined(OS_CHROMEOS) const ui::ShellDialogLinux* shell_dialogs = ui::ShellDialogLinux::instance(); if (shell_dialogs) - return shell_dialogs->CreateSelectFileDialog(listener, policy); + return shell_dialogs->CreateSelectFileDialog(listener, std::move(policy)); #endif NOTIMPLEMENTED(); return nullptr; diff --git a/chromium/ui/shell_dialogs/shell_dialog_linux.h b/chromium/ui/shell_dialogs/shell_dialog_linux.h index 1dad34d15b5..be0ffd0f69c 100644 --- a/chromium/ui/shell_dialogs/shell_dialog_linux.h +++ b/chromium/ui/shell_dialogs/shell_dialog_linux.h @@ -31,7 +31,7 @@ class SHELL_DIALOGS_EXPORT ShellDialogLinux { // Returns a native file selection dialog. virtual SelectFileDialog* CreateSelectFileDialog( SelectFileDialog::Listener* listener, - SelectFilePolicy* policy) const = 0; + std::unique_ptr<SelectFilePolicy> policy) const = 0; }; } // namespace ui diff --git a/chromium/ui/snapshot/DEPS b/chromium/ui/snapshot/DEPS index e7164444691..1ea69bfe1f9 100644 --- a/chromium/ui/snapshot/DEPS +++ b/chromium/ui/snapshot/DEPS @@ -1,7 +1,8 @@ include_rules = [ "+cc", "-cc/blink", - "-cc/surfaces", + "+components/viz/common/quads", + "-components/viz/service/surfaces", "+skia/ext", "+third_party/skia", "+ui/aura", diff --git a/chromium/ui/snapshot/screenshot_grabber.cc b/chromium/ui/snapshot/screenshot_grabber.cc index da79cb28737..bd563946dac 100644 --- a/chromium/ui/snapshot/screenshot_grabber.cc +++ b/chromium/ui/snapshot/screenshot_grabber.cc @@ -7,7 +7,6 @@ #include <stddef.h> #include <climits> -#include <string> #include "base/bind.h" #include "base/callback.h" @@ -18,6 +17,7 @@ #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/task_runner.h" +#include "base/task_scheduler/post_task.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -103,10 +103,10 @@ void EnsureLocalDirectoryExists( void ScreenshotGrabberDelegate::PrepareFileAndRunOnBlockingPool( const base::FilePath& path, - scoped_refptr<base::TaskRunner> blocking_task_runner, const FileCallback& callback_on_blocking_pool) { - blocking_task_runner->PostTask( + base::PostTaskWithTraits( FROM_HERE, + {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, base::Bind(EnsureLocalDirectoryExists, path, callback_on_blocking_pool)); } @@ -139,13 +139,8 @@ class ScreenshotGrabber::ScopedCursorHider { }; #endif -ScreenshotGrabber::ScreenshotGrabber( - ScreenshotGrabberDelegate* client, - scoped_refptr<base::TaskRunner> blocking_task_runner) - : client_(client), - blocking_task_runner_(blocking_task_runner), - factory_(this) { -} +ScreenshotGrabber::ScreenshotGrabber(ScreenshotGrabberDelegate* client) + : client_(client), factory_(this) {} ScreenshotGrabber::~ScreenshotGrabber() { } @@ -169,7 +164,7 @@ void ScreenshotGrabber::TakeScreenshot(gfx::NativeWindow window, cursor_hider_ = ScopedCursorHider::Create(aura_window->GetRootWindow()); #endif ui::GrabWindowSnapshotAsyncPNG( - window, rect, blocking_task_runner_, + window, rect, base::Bind(&ScreenshotGrabber::GrabWindowSnapshotAsyncCallback, factory_.GetWeakPtr(), window_identifier, screenshot_path, is_partial)); @@ -230,7 +225,7 @@ void ScreenshotGrabber::GrabWindowSnapshotAsyncCallback( ShowNotificationCallback notification_callback(base::Bind( &ScreenshotGrabber::NotifyScreenshotCompleted, factory_.GetWeakPtr())); client_->PrepareFileAndRunOnBlockingPool( - screenshot_path, blocking_task_runner_, + screenshot_path, base::Bind(&SaveScreenshot, base::ThreadTaskRunnerHandle::Get(), notification_callback, screenshot_path, png_data)); } diff --git a/chromium/ui/snapshot/screenshot_grabber.h b/chromium/ui/snapshot/screenshot_grabber.h index 7a0a9473f2e..8cf7723d47d 100644 --- a/chromium/ui/snapshot/screenshot_grabber.h +++ b/chromium/ui/snapshot/screenshot_grabber.h @@ -6,6 +6,7 @@ #define UI_SNAPSHOT_SCREENSHOT_GRABBER_H_ #include <memory> +#include <string> #include "base/callback.h" #include "base/compiler_specific.h" @@ -20,10 +21,6 @@ #include "ui/snapshot/screenshot_grabber_observer.h" #include "ui/snapshot/snapshot_export.h" -namespace base { -class TaskRunner; -} - namespace ui { // TODO(flackr): Componentize google drive so that we don't need the @@ -50,14 +47,12 @@ class SNAPSHOT_EXPORT ScreenshotGrabberDelegate { // the remote file and call the callback with the local path. virtual void PrepareFileAndRunOnBlockingPool( const base::FilePath& path, - scoped_refptr<base::TaskRunner> blocking_task_runner, const FileCallback& callback_on_blocking_pool); }; class SNAPSHOT_EXPORT ScreenshotGrabber { public: - ScreenshotGrabber(ScreenshotGrabberDelegate* client, - scoped_refptr<base::TaskRunner> blocking_task_runner); + explicit ScreenshotGrabber(ScreenshotGrabberDelegate* client); ~ScreenshotGrabber(); // Takes a screenshot of |rect| in |window| in that window's coordinate space @@ -92,9 +87,6 @@ class SNAPSHOT_EXPORT ScreenshotGrabber { // The timestamp when the screenshot task was issued last time. base::TimeTicks last_screenshot_timestamp_; - // Task runner for blocking tasks. - scoped_refptr<base::TaskRunner> blocking_task_runner_; - #if defined(USE_AURA) // The object to hide cursor when taking screenshot. std::unique_ptr<ScopedCursorHider> cursor_hider_; diff --git a/chromium/ui/snapshot/snapshot.cc b/chromium/ui/snapshot/snapshot.cc index 6e5188c2a6d..cc57fa93cf4 100644 --- a/chromium/ui/snapshot/snapshot.cc +++ b/chromium/ui/snapshot/snapshot.cc @@ -6,7 +6,7 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/task_runner_util.h" +#include "base/task_scheduler/post_task.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image.h" @@ -22,7 +22,7 @@ scoped_refptr<base::RefCountedMemory> EncodeImageAsPNG( std::vector<uint8_t> result; DCHECK(!image.AsImageSkia().GetRepresentation(1.0f).is_null()); gfx::PNGCodec::FastEncodeBGRASkBitmap(image.AsBitmap(), true, &result); - return new base::RefCountedBytes(result); + return base::RefCountedBytes::TakeVector(&result); } scoped_refptr<base::RefCountedMemory> EncodeImageAsJPEG( @@ -30,17 +30,17 @@ scoped_refptr<base::RefCountedMemory> EncodeImageAsJPEG( std::vector<uint8_t> result; DCHECK(!image.AsImageSkia().GetRepresentation(1.0f).is_null()); gfx::JPEG1xEncodedDataFromImage(image, 100, &result); - return new base::RefCountedBytes(result); + return base::RefCountedBytes::TakeVector(&result); } void EncodeImageAndScheduleCallback( scoped_refptr<base::RefCountedMemory> (*encode_func)(const gfx::Image&), - scoped_refptr<base::TaskRunner> background_task_runner, const base::Callback<void(scoped_refptr<base::RefCountedMemory> data)>& callback, const gfx::Image& image) { - base::PostTaskAndReplyWithResult(background_task_runner.get(), FROM_HERE, - base::Bind(encode_func, image), callback); + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, + base::Bind(encode_func, image), callback); } } // namespace @@ -48,23 +48,19 @@ void EncodeImageAndScheduleCallback( void GrabWindowSnapshotAsyncPNG( gfx::NativeWindow window, const gfx::Rect& source_rect, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncPNGCallback& callback) { GrabWindowSnapshotAsync( window, source_rect, - base::Bind(EncodeImageAndScheduleCallback, &EncodeImageAsPNG, - std::move(background_task_runner), callback)); + base::Bind(EncodeImageAndScheduleCallback, &EncodeImageAsPNG, callback)); } void GrabWindowSnapshotAsyncJPEG( gfx::NativeWindow window, const gfx::Rect& source_rect, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncJPEGCallback& callback) { GrabWindowSnapshotAsync( window, source_rect, - base::Bind(EncodeImageAndScheduleCallback, &EncodeImageAsJPEG, - std::move(background_task_runner), callback)); + base::Bind(EncodeImageAndScheduleCallback, &EncodeImageAsJPEG, callback)); } } // namespace ui diff --git a/chromium/ui/snapshot/snapshot.h b/chromium/ui/snapshot/snapshot.h index 31b9755a9b4..f5b5fa4f335 100644 --- a/chromium/ui/snapshot/snapshot.h +++ b/chromium/ui/snapshot/snapshot.h @@ -13,10 +13,6 @@ #include "ui/gfx/native_widget_types.h" #include "ui/snapshot/snapshot_export.h" -namespace base { -class TaskRunner; -} - namespace gfx { class Rect; class Image; @@ -48,7 +44,6 @@ SNAPSHOT_EXPORT void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncCallback& callback); SNAPSHOT_EXPORT void GrabWindowSnapshotAsync( @@ -66,7 +61,6 @@ using GrabWindowSnapshotAsyncPNGCallback = SNAPSHOT_EXPORT void GrabWindowSnapshotAsyncPNG( gfx::NativeWindow window, const gfx::Rect& source_rect, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncPNGCallback& callback); using GrabWindowSnapshotAsyncJPEGCallback = @@ -74,7 +68,6 @@ using GrabWindowSnapshotAsyncJPEGCallback = SNAPSHOT_EXPORT void GrabWindowSnapshotAsyncJPEG( gfx::NativeWindow window, const gfx::Rect& source_rect, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncJPEGCallback& callback); } // namespace ui diff --git a/chromium/ui/snapshot/snapshot_android.cc b/chromium/ui/snapshot/snapshot_android.cc index 0af3a337d84..c209fa5512a 100644 --- a/chromium/ui/snapshot/snapshot_android.cc +++ b/chromium/ui/snapshot/snapshot_android.cc @@ -8,8 +8,7 @@ #include <utility> #include "base/bind.h" -#include "base/task_runner.h" -#include "cc/output/copy_output_request.h" +#include "components/viz/common/quads/copy_output_request.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/android/view_android.h" #include "ui/android/window_android.h" @@ -38,9 +37,9 @@ bool GrabWindowSnapshot(gfx::NativeWindow window, static void MakeAsyncCopyRequest( gfx::NativeWindow window, const gfx::Rect& source_rect, - cc::CopyOutputRequest::CopyOutputRequestCallback callback) { - std::unique_ptr<cc::CopyOutputRequest> request = - cc::CopyOutputRequest::CreateBitmapRequest(std::move(callback)); + viz::CopyOutputRequest::CopyOutputRequestCallback callback) { + std::unique_ptr<viz::CopyOutputRequest> request = + viz::CopyOutputRequest::CreateBitmapRequest(std::move(callback)); float scale = ui::GetScaleFactorForNativeView(window); request->set_area(gfx::ScaleToEnclosingRect(source_rect, scale)); @@ -51,12 +50,10 @@ void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncCallback& callback) { - MakeAsyncCopyRequest( - window, source_rect, - base::BindOnce(&SnapshotAsync::ScaleCopyOutputResult, callback, - target_size, background_task_runner)); + MakeAsyncCopyRequest(window, source_rect, + base::BindOnce(&SnapshotAsync::ScaleCopyOutputResult, + callback, target_size)); } void GrabWindowSnapshotAsync(gfx::NativeWindow window, diff --git a/chromium/ui/snapshot/snapshot_async.cc b/chromium/ui/snapshot/snapshot_async.cc index 173f70a56ac..1eedc3c9a55 100644 --- a/chromium/ui/snapshot/snapshot_async.cc +++ b/chromium/ui/snapshot/snapshot_async.cc @@ -5,9 +5,8 @@ #include "ui/snapshot/snapshot_async.h" #include "base/location.h" -#include "base/memory/ref_counted.h" #include "base/numerics/safe_conversions.h" -#include "base/task_runner_util.h" +#include "base/task_scheduler/post_task.h" #include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/image/image.h" @@ -37,8 +36,7 @@ SkBitmap ScaleBitmap(const SkBitmap& input_bitmap, void SnapshotAsync::ScaleCopyOutputResult( const GrabWindowSnapshotAsyncCallback& callback, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, - std::unique_ptr<cc::CopyOutputResult> result) { + std::unique_ptr<viz::CopyOutputResult> result) { if (result->IsEmpty()) { callback.Run(gfx::Image()); return; @@ -48,16 +46,15 @@ void SnapshotAsync::ScaleCopyOutputResult( // from GPU. Image scaling is implemented in content::GlHelper, but it's can't // be used here because it's not in content/public. Move the scaling code // somewhere so that it can be reused here. - base::PostTaskAndReplyWithResult( - background_task_runner.get(), - FROM_HERE, + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, base::Bind(ScaleBitmap, *result->TakeBitmap(), target_size), base::Bind(&OnFrameScalingFinished, callback)); } void SnapshotAsync::RunCallbackWithCopyOutputResult( const GrabWindowSnapshotAsyncCallback& callback, - std::unique_ptr<cc::CopyOutputResult> result) { + std::unique_ptr<viz::CopyOutputResult> result) { if (result->IsEmpty()) { callback.Run(gfx::Image()); return; diff --git a/chromium/ui/snapshot/snapshot_async.h b/chromium/ui/snapshot/snapshot_async.h index 79928e0ac2e..4c66cf04f03 100644 --- a/chromium/ui/snapshot/snapshot_async.h +++ b/chromium/ui/snapshot/snapshot_async.h @@ -8,32 +8,27 @@ #include <memory> #include "base/macros.h" -#include "cc/output/copy_output_result.h" +#include "components/viz/common/quads/copy_output_result.h" #include "ui/snapshot/snapshot.h" -namespace base { -class TaskRunner; -} - namespace gfx { class Size; } namespace ui { -// Helper methods for async snapshots to convert a cc::CopyOutputResult into a +// Helper methods for async snapshots to convert a viz::CopyOutputResult into a // ui::GrabWindowSnapshot callback. class SnapshotAsync { public: static void ScaleCopyOutputResult( const GrabWindowSnapshotAsyncCallback& callback, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, - std::unique_ptr<cc::CopyOutputResult> result); + std::unique_ptr<viz::CopyOutputResult> result); static void RunCallbackWithCopyOutputResult( const GrabWindowSnapshotAsyncCallback& callback, - std::unique_ptr<cc::CopyOutputResult> result); + std::unique_ptr<viz::CopyOutputResult> result); private: DISALLOW_IMPLICIT_CONSTRUCTORS(SnapshotAsync); diff --git a/chromium/ui/snapshot/snapshot_aura.cc b/chromium/ui/snapshot/snapshot_aura.cc index 10328ee7fc3..40b59a110a0 100644 --- a/chromium/ui/snapshot/snapshot_aura.cc +++ b/chromium/ui/snapshot/snapshot_aura.cc @@ -4,13 +4,14 @@ #include "ui/snapshot/snapshot_aura.h" +#include <memory> #include <utility> #include "base/bind.h" #include "base/callback.h" #include "base/memory/ptr_util.h" #include "base/task_runner_util.h" -#include "cc/output/copy_output_request.h" +#include "components/viz/common/quads/copy_output_request.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/aura/window.h" #include "ui/aura/window_tracker.h" @@ -31,9 +32,9 @@ bool GrabWindowSnapshotAura(aura::Window* window, static void MakeAsyncCopyRequest( Layer* layer, const gfx::Rect& source_rect, - cc::CopyOutputRequest::CopyOutputRequestCallback callback) { - std::unique_ptr<cc::CopyOutputRequest> request = - cc::CopyOutputRequest::CreateBitmapRequest(std::move(callback)); + viz::CopyOutputRequest::CopyOutputRequestCallback callback) { + std::unique_ptr<viz::CopyOutputRequest> request = + viz::CopyOutputRequest::CreateBitmapRequest(std::move(callback)); request->set_area(source_rect); layer->RequestCopyOfOutput(std::move(request)); } @@ -41,9 +42,9 @@ static void MakeAsyncCopyRequest( static void FinishedAsyncCopyRequest( std::unique_ptr<aura::WindowTracker> tracker, const gfx::Rect& source_rect, - cc::CopyOutputRequest::CopyOutputRequestCallback callback, + viz::CopyOutputRequest::CopyOutputRequestCallback callback, int retry_count, - std::unique_ptr<cc::CopyOutputResult> result) { + std::unique_ptr<viz::CopyOutputResult> result) { static const int kMaxRetries = 5; // Retry the copy request if the previous one failed for some reason. if (!tracker->windows().empty() && (retry_count < kMaxRetries) && @@ -67,7 +68,7 @@ static void FinishedAsyncCopyRequest( static void MakeInitialAsyncCopyRequest( aura::Window* window, const gfx::Rect& source_rect, - cc::CopyOutputRequest::CopyOutputRequestCallback callback) { + viz::CopyOutputRequest::CopyOutputRequestCallback callback) { auto tracker = base::MakeUnique<aura::WindowTracker>(); tracker->Add(window); MakeAsyncCopyRequest( @@ -80,12 +81,11 @@ void GrabWindowSnapshotAndScaleAsyncAura( aura::Window* window, const gfx::Rect& source_rect, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncCallback& callback) { MakeInitialAsyncCopyRequest( window, source_rect, base::BindOnce(&SnapshotAsync::ScaleCopyOutputResult, callback, - target_size, background_task_runner)); + target_size)); } void GrabWindowSnapshotAsyncAura( @@ -116,10 +116,9 @@ void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncCallback& callback) { GrabWindowSnapshotAndScaleAsyncAura(window, source_rect, target_size, - background_task_runner, callback); + callback); } void GrabWindowSnapshotAsync(gfx::NativeWindow window, diff --git a/chromium/ui/snapshot/snapshot_aura.h b/chromium/ui/snapshot/snapshot_aura.h index 2c5429cef1d..7f693172cd6 100644 --- a/chromium/ui/snapshot/snapshot_aura.h +++ b/chromium/ui/snapshot/snapshot_aura.h @@ -19,7 +19,6 @@ SNAPSHOT_EXPORT void GrabWindowSnapshotAndScaleAsyncAura( aura::Window* window, const gfx::Rect& source_rect, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncCallback& callback); SNAPSHOT_EXPORT void GrabWindowSnapshotAsyncAura( diff --git a/chromium/ui/snapshot/snapshot_aura_unittest.cc b/chromium/ui/snapshot/snapshot_aura_unittest.cc index bee0b0ca79a..1f9a779b4fa 100644 --- a/chromium/ui/snapshot/snapshot_aura_unittest.cc +++ b/chromium/ui/snapshot/snapshot_aura_unittest.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/macros.h" +#include "base/run_loop.h" #include "base/test/test_simple_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkPixelRef.h" diff --git a/chromium/ui/snapshot/snapshot_ios.mm b/chromium/ui/snapshot/snapshot_ios.mm index d239b71871c..b0c57e8d3e4 100644 --- a/chromium/ui/snapshot/snapshot_ios.mm +++ b/chromium/ui/snapshot/snapshot_ios.mm @@ -28,7 +28,6 @@ void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& snapshot_bounds, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, GrabWindowSnapshotAsyncCallback callback) { callback.Run(gfx::Image()); } diff --git a/chromium/ui/snapshot/snapshot_mac.mm b/chromium/ui/snapshot/snapshot_mac.mm index 40cfaaf0e2f..fab9c68172e 100644 --- a/chromium/ui/snapshot/snapshot_mac.mm +++ b/chromium/ui/snapshot/snapshot_mac.mm @@ -10,7 +10,6 @@ #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/mac/sdk_forward_declarations.h" -#include "base/task_runner.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/image/image.h" @@ -69,7 +68,6 @@ void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& snapshot_bounds, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, GrabWindowSnapshotAsyncCallback callback) { callback.Run(gfx::Image()); } diff --git a/chromium/ui/snapshot/snapshot_win.cc b/chromium/ui/snapshot/snapshot_win.cc index cd0d8f15099..17f4278f9d8 100644 --- a/chromium/ui/snapshot/snapshot_win.cc +++ b/chromium/ui/snapshot/snapshot_win.cc @@ -4,8 +4,9 @@ #include "ui/snapshot/snapshot_win.h" +#include <memory> + #include "base/callback.h" -#include "base/task_runner.h" #include "base/win/windows_version.h" #include "skia/ext/platform_canvas.h" #include "skia/ext/skia_utils_win.h" @@ -149,11 +150,10 @@ void GrabWindowSnapshotAndScaleAsync( gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, - scoped_refptr<base::TaskRunner> background_task_runner, const GrabWindowSnapshotAsyncCallback& callback) { if (UseAuraSnapshot()) { GrabWindowSnapshotAndScaleAsyncAura(window, source_rect, target_size, - background_task_runner, callback); + callback); return; } NOTIMPLEMENTED(); diff --git a/chromium/ui/strings/translations/ui_strings_am.xtb b/chromium/ui/strings/translations/ui_strings_am.xtb index e1961fdafdc..7afaa65b656 100644 --- a/chromium/ui/strings/translations/ui_strings_am.xtb +++ b/chromium/ui/strings/translations/ui_strings_am.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(ባዶ)</translation> <translation id="2289052229480071835">በማያ ገጽዎ ላይ ያሉት የንክኪ ዒላማዎችን መታ ያድርጉ።</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> ቴባ/ሰ</translation> +<translation id="24452542372838207">ማሳወቂያን ዘርጋ</translation> <translation id="2482878487686419369">ማስታወቂያዎች</translation> <translation id="2497284189126895209">ሁሉም ፋይሎች</translation> <translation id="2522350507219695259">ልኬት ማስተካከል ተጠናቅቋል</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">ክፈት</translation> <translation id="9038489124413477075">ያልተሰየመ አቃፊ</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 ሰከን}one{# ሰከን}other{# ሰከን}}</translation> +<translation id="9059834730836941392">ማሳወቂያን ሰብስብ</translation> <translation id="9150735707954472829">ትር</translation> <translation id="9161053988251441839">በአስተያየት የተጠቆሙ መተግበሪያዎች</translation> <translation id="9170848237812810038">&ቀልብስ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ar.xtb b/chromium/ui/strings/translations/ui_strings_ar.xtb index f6d28aba77b..31d047a5d8e 100644 --- a/chromium/ui/strings/translations/ui_strings_ar.xtb +++ b/chromium/ui/strings/translations/ui_strings_ar.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(فارغ)</translation> <translation id="2289052229480071835">انقر على أهداف اللمس على شاشتك.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> تيرابايت/ثانية</translation> +<translation id="24452542372838207">توسيع الإشعار</translation> <translation id="2482878487686419369">الاشعارات</translation> <translation id="2497284189126895209">الملفّات كلّها</translation> <translation id="2522350507219695259">اكتملت المعايرة</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">الفتح</translation> <translation id="9038489124413477075">مجلد بدون اسم</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{ثانية واحدة}zero{# ثانية}two{ثانيتان (#)}few{# ثوانٍ}many{# ثانية}other{# من الثواني}}</translation> +<translation id="9059834730836941392">تصغير الإشعار</translation> <translation id="9150735707954472829">علامة تبويب</translation> <translation id="9161053988251441839">التطبيقات المُقترحَة</translation> <translation id="9170848237812810038">&إلغاء</translation> diff --git a/chromium/ui/strings/translations/ui_strings_bg.xtb b/chromium/ui/strings/translations/ui_strings_bg.xtb index cf67b155d8c..40b817247e0 100644 --- a/chromium/ui/strings/translations/ui_strings_bg.xtb +++ b/chromium/ui/strings/translations/ui_strings_bg.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(празно)</translation> <translation id="2289052229480071835">Докоснете съответните цели на екрана.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> ТБ/сек</translation> +<translation id="24452542372838207">Разгъване на известието</translation> <translation id="2482878487686419369">Известия</translation> <translation id="2497284189126895209">Всички файлове</translation> <translation id="2522350507219695259">Калибрирането завърши</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">отварям</translation> <translation id="9038489124413477075">Папка без име</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 сек}other{# сек}}</translation> +<translation id="9059834730836941392">Свиване на известието</translation> <translation id="9150735707954472829">Раздел</translation> <translation id="9161053988251441839">ПРЕДЛОЖЕНИ ПРИЛОЖЕНИЯ</translation> <translation id="9170848237812810038">&Отмяна</translation> diff --git a/chromium/ui/strings/translations/ui_strings_bn.xtb b/chromium/ui/strings/translations/ui_strings_bn.xtb index 689f44e7790..3f86cef1ae5 100644 --- a/chromium/ui/strings/translations/ui_strings_bn.xtb +++ b/chromium/ui/strings/translations/ui_strings_bn.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(খালি)</translation> <translation id="2289052229480071835">আপনার স্ক্রীনে স্পর্শ লক্ষ্যগুলি আলতা চাপুন।</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">বিজ্ঞপ্তি প্রসারিত করুন</translation> <translation id="2482878487686419369">বিজ্ঞপ্তিগুলি</translation> <translation id="2497284189126895209">সকল ফাইল</translation> <translation id="2522350507219695259">ক্রমাঙ্কন সম্পূর্ণ হয়েছে</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">খুলুন</translation> <translation id="9038489124413477075">নামবিহীন ফোল্ডার</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{১ সেকেন্ড}one{# সেকেন্ড}other{# সেকেন্ড}}</translation> +<translation id="9059834730836941392">বিজ্ঞপ্তি সঙ্কুচিত করুন</translation> <translation id="9150735707954472829">ট্যাব</translation> <translation id="9161053988251441839">প্রস্তাবিত অ্যাপ</translation> <translation id="9170848237812810038">&পূর্বাবস্থায় ফিরুন</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ca.xtb b/chromium/ui/strings/translations/ui_strings_ca.xtb index dadf2bb9000..426760e8af1 100644 --- a/chromium/ui/strings/translations/ui_strings_ca.xtb +++ b/chromium/ui/strings/translations/ui_strings_ca.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(buit)</translation> <translation id="2289052229480071835">Toca els elements tàctils a la pantalla.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Desplega la notificació</translation> <translation id="2482878487686419369">Notificacions</translation> <translation id="2497284189126895209">Tots els fitxers</translation> <translation id="2522350507219695259">S'ha acabat de calibrar</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">obrir</translation> <translation id="9038489124413477075">Carpeta sense nom</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}other{# s}}</translation> +<translation id="9059834730836941392">Replega la notificació</translation> <translation id="9150735707954472829">Pestanya</translation> <translation id="9161053988251441839">APLICACIONS SUGGERIDES</translation> <translation id="9170848237812810038">&Desfés</translation> diff --git a/chromium/ui/strings/translations/ui_strings_cs.xtb b/chromium/ui/strings/translations/ui_strings_cs.xtb index 5846316ddf6..05e09b4978c 100644 --- a/chromium/ui/strings/translations/ui_strings_cs.xtb +++ b/chromium/ui/strings/translations/ui_strings_cs.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(prázdné)</translation> <translation id="2289052229480071835">Klepněte na cíle klepnutí na obrazovce.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Rozbalit oznámení</translation> <translation id="2482878487686419369">Oznámení</translation> <translation id="2497284189126895209">Všechny soubory</translation> <translation id="2522350507219695259">Kalibrace je dokončena</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">otevřít</translation> <translation id="9038489124413477075">Nepojmenovaná složka</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}few{# s}many{# s}other{# s}}</translation> +<translation id="9059834730836941392">Sbalit oznámení</translation> <translation id="9150735707954472829">Karta</translation> <translation id="9161053988251441839">NAVRHOVANÉ APLIKACE</translation> <translation id="9170848237812810038">Z&pět</translation> diff --git a/chromium/ui/strings/translations/ui_strings_da.xtb b/chromium/ui/strings/translations/ui_strings_da.xtb index 2fb15912f01..6221426a06d 100644 --- a/chromium/ui/strings/translations/ui_strings_da.xtb +++ b/chromium/ui/strings/translations/ui_strings_da.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tom)</translation> <translation id="2289052229480071835">Tryk på de punkter på skærmen, der kan trykkes på.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/sek.</translation> +<translation id="24452542372838207">Udvid underretning</translation> <translation id="2482878487686419369">Underretninger</translation> <translation id="2497284189126895209">Alle filer</translation> <translation id="2522350507219695259">Kalibreringen er fuldført</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">åbn</translation> <translation id="9038489124413477075">Unavngiven mappe</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sek.}one{# sek.}other{# sek.}}</translation> +<translation id="9059834730836941392">Skjul underretning</translation> <translation id="9150735707954472829">Fane</translation> <translation id="9161053988251441839">FORESLÅEDE APPS</translation> <translation id="9170848237812810038">&Fortryd</translation> diff --git a/chromium/ui/strings/translations/ui_strings_de.xtb b/chromium/ui/strings/translations/ui_strings_de.xtb index 5b1adb5cd1f..50756460234 100644 --- a/chromium/ui/strings/translations/ui_strings_de.xtb +++ b/chromium/ui/strings/translations/ui_strings_de.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(leer)</translation> <translation id="2289052229480071835">Tippen Sie auf die Zielelemente auf dem Bildschirm.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Benachrichtigung erweitern</translation> <translation id="2482878487686419369">Benachrichtigungen</translation> <translation id="2497284189126895209">Alle Dateien</translation> <translation id="2522350507219695259">Die Kalibrierung ist abgeschlossen</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">Öffnen</translation> <translation id="9038489124413477075">Unbenannter Ordner</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 Sek.}other{# Sek.}}</translation> +<translation id="9059834730836941392">Benachrichtigung minimieren</translation> <translation id="9150735707954472829">Tab</translation> <translation id="9161053988251441839">VORGESCHLAGENE APPS</translation> <translation id="9170848237812810038">&Rückgängig</translation> diff --git a/chromium/ui/strings/translations/ui_strings_el.xtb b/chromium/ui/strings/translations/ui_strings_el.xtb index 195d2361e99..67898142fd9 100644 --- a/chromium/ui/strings/translations/ui_strings_el.xtb +++ b/chromium/ui/strings/translations/ui_strings_el.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(κενό)</translation> <translation id="2289052229480071835">Πατήστε τους στόχους αφής στην οθόνη σας.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Ανάπτυξη ειδοποίησης</translation> <translation id="2482878487686419369">Ειδοποιήσεις</translation> <translation id="2497284189126895209">Όλα τα αρχεία</translation> <translation id="2522350507219695259">Η βαθμονόμηση ολοκληρώθηκε</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">ανοίγω</translation> <translation id="9038489124413477075">Φάκελος χωρίς όνομα</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 δευτ.}other{# δευτ.}}</translation> +<translation id="9059834730836941392">Σύμπτυξη ειδοποίησης</translation> <translation id="9150735707954472829">Καρτέλα</translation> <translation id="9161053988251441839">ΠΡΟΤΕΙΝΟΜΕΝΕΣ ΕΦΑΡΜΟΓΕΣ</translation> <translation id="9170848237812810038">Αναί&ρεση</translation> diff --git a/chromium/ui/strings/translations/ui_strings_en-GB.xtb b/chromium/ui/strings/translations/ui_strings_en-GB.xtb index c878620dd04..fb094171541 100644 --- a/chromium/ui/strings/translations/ui_strings_en-GB.xtb +++ b/chromium/ui/strings/translations/ui_strings_en-GB.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(empty)</translation> <translation id="2289052229480071835">Tap the touch targets on your screen.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Expand notification</translation> <translation id="2482878487686419369">Notifications</translation> <translation id="2497284189126895209">All Files</translation> <translation id="2522350507219695259">Calibration is completed</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">open</translation> <translation id="9038489124413477075">Unnamed Folder</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sec}other{# secs}}</translation> +<translation id="9059834730836941392">Collapse notification</translation> <translation id="9150735707954472829">Tab</translation> <translation id="9161053988251441839">SUGGESTED APPS</translation> <translation id="9170848237812810038">&Undo</translation> diff --git a/chromium/ui/strings/translations/ui_strings_es-419.xtb b/chromium/ui/strings/translations/ui_strings_es-419.xtb index 9871a849aea..0e87a0fe1e2 100644 --- a/chromium/ui/strings/translations/ui_strings_es-419.xtb +++ b/chromium/ui/strings/translations/ui_strings_es-419.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(vacío)</translation> <translation id="2289052229480071835">Presiona los objetivos táctiles en tu pantalla.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Expandir notificación</translation> <translation id="2482878487686419369">Notificaciones</translation> <translation id="2497284189126895209">Todos los archivos</translation> <translation id="2522350507219695259">Se completó la calibración</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Carpeta sin nombre</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}other{# s}}</translation> +<translation id="9059834730836941392">Contraer notificación</translation> <translation id="9150735707954472829">Pestaña</translation> <translation id="9161053988251441839">APPS SUGERIDAS</translation> <translation id="9170848237812810038">&Deshacer</translation> diff --git a/chromium/ui/strings/translations/ui_strings_es.xtb b/chromium/ui/strings/translations/ui_strings_es.xtb index 2f13a0ddbe6..e318cf9ddfa 100644 --- a/chromium/ui/strings/translations/ui_strings_es.xtb +++ b/chromium/ui/strings/translations/ui_strings_es.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(vacío)</translation> <translation id="2289052229480071835">Toca los elementos táctiles en la pantalla.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Mostrar notificación</translation> <translation id="2482878487686419369">Notificaciones</translation> <translation id="2497284189126895209">Todos los archivos</translation> <translation id="2522350507219695259">Se ha completado la calibración</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Carpeta sin nombre</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}other{# s}}</translation> +<translation id="9059834730836941392">Ocultar notificación</translation> <translation id="9150735707954472829">Pestaña </translation> <translation id="9161053988251441839">APLICACIONES SUGERIDAS</translation> <translation id="9170848237812810038">&Deshacer</translation> diff --git a/chromium/ui/strings/translations/ui_strings_et.xtb b/chromium/ui/strings/translations/ui_strings_et.xtb index da3a070fb27..3d0fe97bad2 100644 --- a/chromium/ui/strings/translations/ui_strings_et.xtb +++ b/chromium/ui/strings/translations/ui_strings_et.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tühi)</translation> <translation id="2289052229480071835">Puudutage ekraanil puutesihtmärke.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Laienda märguannet</translation> <translation id="2482878487686419369">Märguanded</translation> <translation id="2497284189126895209">Kõik failid</translation> <translation id="2522350507219695259">Kalibreerimine on lõpetatud</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">avage</translation> <translation id="9038489124413477075">Nimeta kaust</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}other{# s}}</translation> +<translation id="9059834730836941392">Ahenda märguannet</translation> <translation id="9150735707954472829">Vahekaart</translation> <translation id="9161053988251441839">SOOVITATUD RAKENDUSED</translation> <translation id="9170848237812810038">&Võta tagasi</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fa.xtb b/chromium/ui/strings/translations/ui_strings_fa.xtb index e0465029c22..24f21bd3e16 100644 --- a/chromium/ui/strings/translations/ui_strings_fa.xtb +++ b/chromium/ui/strings/translations/ui_strings_fa.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(خالی)</translation> <translation id="2289052229480071835">روی اهداف لمسی در صفحهتان ضربه بزنید.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> ترابایت/ثانیه</translation> +<translation id="24452542372838207">بزرگ کردن اعلان</translation> <translation id="2482878487686419369">اعلانها</translation> <translation id="2497284189126895209">همه فایلها</translation> <translation id="2522350507219695259">کالیبراسیون تمام شد</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">باز کنید</translation> <translation id="9038489124413477075">پوشه بدون نام</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{۱ ثانیه}one{# ثانیه}other{# ثانیه}}</translation> +<translation id="9059834730836941392">کوچک کردن اعلان</translation> <translation id="9150735707954472829">Tab</translation> <translation id="9161053988251441839">برنامههای پیشنهادشده</translation> <translation id="9170848237812810038">&واگرد</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fi.xtb b/chromium/ui/strings/translations/ui_strings_fi.xtb index 6129d4f1a2f..80fc6acc914 100644 --- a/chromium/ui/strings/translations/ui_strings_fi.xtb +++ b/chromium/ui/strings/translations/ui_strings_fi.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tyhjä)</translation> <translation id="2289052229480071835">Napauta näytölläsi olevia kosketettavia kohteita.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> Tt/s</translation> +<translation id="24452542372838207">Laajenna ilmoitus</translation> <translation id="2482878487686419369">Ilmoitukset</translation> <translation id="2497284189126895209">Kaikki tiedostot</translation> <translation id="2522350507219695259">Kalibrointi on valmis.</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">avata</translation> <translation id="9038489124413477075">Nimetön kansio</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}other{# s}}</translation> +<translation id="9059834730836941392">Tiivistä ilmoitus</translation> <translation id="9150735707954472829">Välilehti</translation> <translation id="9161053988251441839">SOVELLUSEHDOTUKSET</translation> <translation id="9170848237812810038">K&umoa</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fil.xtb b/chromium/ui/strings/translations/ui_strings_fil.xtb index 5f7ee547b6d..50666148610 100644 --- a/chromium/ui/strings/translations/ui_strings_fil.xtb +++ b/chromium/ui/strings/translations/ui_strings_fil.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(walang laman)</translation> <translation id="2289052229480071835">I-tap ang mga target sa pagpindot sa iyong screen.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> (na) TB/s</translation> +<translation id="24452542372838207">Palawakin ang notification</translation> <translation id="2482878487686419369">Mga Abiso</translation> <translation id="2497284189126895209">Lahat ng Mga File</translation> <translation id="2522350507219695259">Nakumpleto na ang pag-calibrate</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">buksan</translation> <translation id="9038489124413477075">Walang Pangalan na Folder</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 segundo}one{# segundo}other{# na segundo}}</translation> +<translation id="9059834730836941392">I-collapse ang notification</translation> <translation id="9150735707954472829">Tab</translation> <translation id="9161053988251441839">MGA IMINUMUNGKAHING APP</translation> <translation id="9170848237812810038">&I-undo</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fr.xtb b/chromium/ui/strings/translations/ui_strings_fr.xtb index 67662ffc7cd..934de20b85f 100644 --- a/chromium/ui/strings/translations/ui_strings_fr.xtb +++ b/chromium/ui/strings/translations/ui_strings_fr.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(vide)</translation> <translation id="2289052229480071835">Appuyez sur les cibles tactiles affichées à l'écran.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> To/s</translation> +<translation id="24452542372838207">Développer la notification</translation> <translation id="2482878487686419369">Notifications</translation> <translation id="2497284189126895209">Tous les fichiers</translation> <translation id="2522350507219695259">Calibrage terminé</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">ouvrir</translation> <translation id="9038489124413477075">Dossier sans nom</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}one{# s}other{# s}}</translation> +<translation id="9059834730836941392">Réduire la notification</translation> <translation id="9150735707954472829">Onglet</translation> <translation id="9161053988251441839">APPLICATIONS SUGGÉRÉES</translation> <translation id="9170848237812810038">Ann&uler</translation> diff --git a/chromium/ui/strings/translations/ui_strings_gu.xtb b/chromium/ui/strings/translations/ui_strings_gu.xtb index 6d20644fa95..bd1ad668090 100644 --- a/chromium/ui/strings/translations/ui_strings_gu.xtb +++ b/chromium/ui/strings/translations/ui_strings_gu.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(ખાલી)</translation> <translation id="2289052229480071835">તમારી સ્ક્રીન પર ટચ કરવાના લક્ષ્યોને ટૅપ કરો.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">નોટિફિકેશન વિસ્તૃત કરો</translation> <translation id="2482878487686419369">સૂચનાઓ</translation> <translation id="2497284189126895209">બધી ફાઇલો</translation> <translation id="2522350507219695259">કૅલિબ્રેશન પૂર્ણ થયું</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">ખોલો</translation> <translation id="9038489124413477075">અનામાંકિત ફોલ્ડર</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 સે}one{# સે}other{# સે}}</translation> +<translation id="9059834730836941392">નોટિફિકેશન સંકુચિત કરો</translation> <translation id="9150735707954472829">ટૅબ</translation> <translation id="9161053988251441839">સૂચવેલ ઍપ્લિકેશનો</translation> <translation id="9170848237812810038">&પૂર્વવત્ કરો</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hi.xtb b/chromium/ui/strings/translations/ui_strings_hi.xtb index 8548fffe023..6da68dee230 100644 --- a/chromium/ui/strings/translations/ui_strings_hi.xtb +++ b/chromium/ui/strings/translations/ui_strings_hi.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(खाली)</translation> <translation id="2289052229480071835">अपनी स्क्रीन पर स्पर्श लक्ष्य पर टैप करें.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">सूचना को विस्तृत करें</translation> <translation id="2482878487686419369">अधिसूचनाएं</translation> <translation id="2497284189126895209">सभी फ़ाइलें</translation> <translation id="2522350507219695259">कैलिब्रेशन पूरा हो गया</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">खोलें</translation> <translation id="9038489124413477075">अनाम फ़ोल्डर</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 सेकंड}one{# सेकंड}other{# सेकंड}}</translation> +<translation id="9059834730836941392">सूचना को छोटा करें</translation> <translation id="9150735707954472829">टैब</translation> <translation id="9161053988251441839">सुझाए गए ऐप्लिकेशन</translation> <translation id="9170848237812810038">&पूर्ववत् करें</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hr.xtb b/chromium/ui/strings/translations/ui_strings_hr.xtb index 36d412b3f20..12223437030 100644 --- a/chromium/ui/strings/translations/ui_strings_hr.xtb +++ b/chromium/ui/strings/translations/ui_strings_hr.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(prazno)</translation> <translation id="2289052229480071835">Dodirnite ciljeve dodira na zaslonu.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Proširi obavijest</translation> <translation id="2482878487686419369">Obavijesti</translation> <translation id="2497284189126895209">Sve datoteke</translation> <translation id="2522350507219695259">Kalibracija je gotova</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">otvaranje</translation> <translation id="9038489124413477075">Neimenovana mapa</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}one{# s}few{# s}other{# s}}</translation> +<translation id="9059834730836941392">Sažmi obavijest</translation> <translation id="9150735707954472829">Kartica</translation> <translation id="9161053988251441839">PREDLOŽENE APLIKACIJE</translation> <translation id="9170848237812810038">&Poništi</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hu.xtb b/chromium/ui/strings/translations/ui_strings_hu.xtb index 6fe7fa69e42..54496d27f6a 100644 --- a/chromium/ui/strings/translations/ui_strings_hu.xtb +++ b/chromium/ui/strings/translations/ui_strings_hu.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(üres)</translation> <translation id="2289052229480071835">Koppintson a képernyőn az érintési célpontokra.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Értesítés kibontása</translation> <translation id="2482878487686419369">Értesítések</translation> <translation id="2497284189126895209">Minden fájl</translation> <translation id="2522350507219695259">A kalibrálás befejeződött</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">megnyitás</translation> <translation id="9038489124413477075">Név nélküli mappa</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 mp}other{# mp}}</translation> +<translation id="9059834730836941392">Értesítés összecsukása</translation> <translation id="9150735707954472829">Lap</translation> <translation id="9161053988251441839">JAVASOLT ALKALMAZÁSOK</translation> <translation id="9170848237812810038">&Visszavonás</translation> diff --git a/chromium/ui/strings/translations/ui_strings_id.xtb b/chromium/ui/strings/translations/ui_strings_id.xtb index 7d6a33a1f27..042b7d6f7df 100644 --- a/chromium/ui/strings/translations/ui_strings_id.xtb +++ b/chromium/ui/strings/translations/ui_strings_id.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(kosong)</translation> <translation id="2289052229480071835">Tap target sentuh di layar.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/dtk</translation> +<translation id="24452542372838207">Luaskan notifikasi</translation> <translation id="2482878487686419369">Notifikasi</translation> <translation id="2497284189126895209">Semua Jenis File</translation> <translation id="2522350507219695259">Kalibrasi selesai</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">buka</translation> <translation id="9038489124413477075">Folder Tanpa Nama</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 dtk}other{# dtk}}</translation> +<translation id="9059834730836941392">Ciutkan notifikasi</translation> <translation id="9150735707954472829">Tab</translation> <translation id="9161053988251441839">APLIKASI YANG DISARANKAN</translation> <translation id="9170848237812810038">&Urung</translation> diff --git a/chromium/ui/strings/translations/ui_strings_it.xtb b/chromium/ui/strings/translations/ui_strings_it.xtb index cca8990bf87..df43db55d5a 100644 --- a/chromium/ui/strings/translations/ui_strings_it.xtb +++ b/chromium/ui/strings/translations/ui_strings_it.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(vuoto)</translation> <translation id="2289052229480071835">Tocca i touch target sul tuo schermo.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Espandi la notifica</translation> <translation id="2482878487686419369">Notifiche</translation> <translation id="2497284189126895209">Tutti i file</translation> <translation id="2522350507219695259">Calibrazione completata</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">apri</translation> <translation id="9038489124413477075">Cartella senza nome</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sec}other{# sec}}</translation> +<translation id="9059834730836941392">Comprimi la notifica</translation> <translation id="9150735707954472829">Scheda</translation> <translation id="9161053988251441839">APP CONSIGLIATE</translation> <translation id="9170848237812810038">&Annulla</translation> diff --git a/chromium/ui/strings/translations/ui_strings_iw.xtb b/chromium/ui/strings/translations/ui_strings_iw.xtb index 693fdc92b3d..bcf0b8cd732 100644 --- a/chromium/ui/strings/translations/ui_strings_iw.xtb +++ b/chromium/ui/strings/translations/ui_strings_iw.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(ריק)</translation> <translation id="2289052229480071835">הקש על יעדי המגע במסך.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">הרחבת ההודעה</translation> <translation id="2482878487686419369">התראות</translation> <translation id="2497284189126895209">כל הקבצים</translation> <translation id="2522350507219695259">הכיול הושלם</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">פתח</translation> <translation id="9038489124413477075">תיקייה ללא שם</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{שנ‘ אחת}two{# שנ‘}many{# שנ‘}other{# שנ‘}}</translation> +<translation id="9059834730836941392">כיווץ ההודעה</translation> <translation id="9150735707954472829">כרטיסייה</translation> <translation id="9161053988251441839">אפליקציות מוצעות</translation> <translation id="9170848237812810038">&ביטול</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ja.xtb b/chromium/ui/strings/translations/ui_strings_ja.xtb index 49661fdb37b..cc70393aa70 100644 --- a/chromium/ui/strings/translations/ui_strings_ja.xtb +++ b/chromium/ui/strings/translations/ui_strings_ja.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(なし)</translation> <translation id="2289052229480071835">画面に表示されるタップ ターゲットをタップします。</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/秒</translation> +<translation id="24452542372838207">通知を展開</translation> <translation id="2482878487686419369">通知</translation> <translation id="2497284189126895209">すべてのファイル</translation> <translation id="2522350507219695259">キャリブレーションが完了しました</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">開く</translation> <translation id="9038489124413477075">名前のないフォルダ</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 秒}other{# 秒}}</translation> +<translation id="9059834730836941392">通知を折りたたむ</translation> <translation id="9150735707954472829">タブ</translation> <translation id="9161053988251441839">おすすめのアプリ</translation> <translation id="9170848237812810038">取消(&U)</translation> diff --git a/chromium/ui/strings/translations/ui_strings_kn.xtb b/chromium/ui/strings/translations/ui_strings_kn.xtb index 7ee5b07443b..c4a5a372e98 100644 --- a/chromium/ui/strings/translations/ui_strings_kn.xtb +++ b/chromium/ui/strings/translations/ui_strings_kn.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(ಖಾಲಿ)</translation> <translation id="2289052229480071835">ನಿಮ್ಮ ಪರದೆಯಲ್ಲಿ ಸ್ಪರ್ಶ ಟಾರ್ಗೆಟ್ಗಳನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">ವಿಸ್ತರಿಸುವ ಅಧಿಸೂಚನೆ</translation> <translation id="2482878487686419369">ಸೂಚನೆಗಳು</translation> <translation id="2497284189126895209">ಎಲ್ಲ ಫೈಲ್ಗಳು</translation> <translation id="2522350507219695259">ಮಾಪನಾಂಕವು ಪೂರ್ಣಗೊಂಡಿದೆ</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">ತೆರೆ</translation> <translation id="9038489124413477075">ಹೆಸರಿಸದ ಫೋಲ್ಡರ್</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 ಸೆಕೆಂಡ್}one{# ಸೆಕೆಂಡುಗಳು}other{# ಸೆಕೆಂಡುಗಳು}}</translation> +<translation id="9059834730836941392">ಕುಗ್ಗಿಸುವ ಅಧಿಸೂಚನೆ</translation> <translation id="9150735707954472829">ಟ್ಯಾಬ್</translation> <translation id="9161053988251441839">ಸಲಹೆ ಮಾಡಿರುವ ಅಪ್ಲಿಕೇಶನ್ಗಳು</translation> <translation id="9170848237812810038">&ರದ್ದುಮಾಡು</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ko.xtb b/chromium/ui/strings/translations/ui_strings_ko.xtb index 6a5cd3edbbd..424831f7169 100644 --- a/chromium/ui/strings/translations/ui_strings_ko.xtb +++ b/chromium/ui/strings/translations/ui_strings_ko.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(비어있음)</translation> <translation id="2289052229480071835">화면에서 터치 대상을 탭하세요.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" />TB/s</translation> +<translation id="24452542372838207">알림 펼치기</translation> <translation id="2482878487686419369">알림</translation> <translation id="2497284189126895209">모든 파일</translation> <translation id="2522350507219695259">보정이 완료되었습니다.</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">열기</translation> <translation id="9038489124413477075">이름이 없는 폴더</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1초}other{#초}}</translation> +<translation id="9059834730836941392">알림 접기</translation> <translation id="9150735707954472829">탭</translation> <translation id="9161053988251441839">추천 앱</translation> <translation id="9170848237812810038">실행 취소(&U)</translation> diff --git a/chromium/ui/strings/translations/ui_strings_lt.xtb b/chromium/ui/strings/translations/ui_strings_lt.xtb index 0b9d3867840..85ccb38b9e5 100644 --- a/chromium/ui/strings/translations/ui_strings_lt.xtb +++ b/chromium/ui/strings/translations/ui_strings_lt.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tuščias)</translation> <translation id="2289052229480071835">Palieskite jutiklines sritis ekrane.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Išskleisti pranešimą</translation> <translation id="2482878487686419369">Pranešimai</translation> <translation id="2497284189126895209">Visi failai</translation> <translation id="2522350507219695259">Kalibravimas baigtas</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">atidaryti</translation> <translation id="9038489124413477075">Aplankas be pavadinimo</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sek.}one{# sek.}few{# sek.}many{# sek.}other{# sek.}}</translation> +<translation id="9059834730836941392">Sutraukti pranešimą</translation> <translation id="9150735707954472829">Skirtukas</translation> <translation id="9161053988251441839">SIŪLOMOS PROGRAMOS</translation> <translation id="9170848237812810038">&Atšaukti</translation> diff --git a/chromium/ui/strings/translations/ui_strings_lv.xtb b/chromium/ui/strings/translations/ui_strings_lv.xtb index 6cd54539ce9..b444f2bff3e 100644 --- a/chromium/ui/strings/translations/ui_strings_lv.xtb +++ b/chromium/ui/strings/translations/ui_strings_lv.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tukšs)</translation> <translation id="2289052229480071835">Pieskarieties ekrānā redzamajiem pieskāriena mērķiem.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Izvērst paziņojumu</translation> <translation id="2482878487686419369">Paziņojumi</translation> <translation id="2497284189126895209">Visi faili</translation> <translation id="2522350507219695259">Kalibrēšana ir pabeigta.</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">atvērt</translation> <translation id="9038489124413477075">Mape bez nosaukuma</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}zero{# s}one{# s}other{# s}}</translation> +<translation id="9059834730836941392">Sakļaut paziņojumu</translation> <translation id="9150735707954472829">Cilne</translation> <translation id="9161053988251441839">IETEIKTĀS LIETOTNES</translation> <translation id="9170848237812810038">&Atsaukt</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ml.xtb b/chromium/ui/strings/translations/ui_strings_ml.xtb index 99051d762b7..667fbed983f 100644 --- a/chromium/ui/strings/translations/ui_strings_ml.xtb +++ b/chromium/ui/strings/translations/ui_strings_ml.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(ശൂന്യം)</translation> <translation id="2289052229480071835">സ്ക്രീനിലുള്ള 'ടാർഗെറ്റുകൾ സ്പർശിക്കുക' ടാപ്പുചെയ്യുക.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">അറിയിപ്പ് വികസിപ്പിക്കുക</translation> <translation id="2482878487686419369">വിജ്ഞാപനങ്ങള്</translation> <translation id="2497284189126895209">എല്ലാ ഫയലുകളും</translation> <translation id="2522350507219695259">കാലിബറേഷൻ പൂർത്തിയായി</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">തുറക്കുക</translation> <translation id="9038489124413477075">പേരിടാത്ത ഫോൾഡർ</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{ഒരു സെക്കൻഡ്}other{# സെക്കൻഡ്}}</translation> +<translation id="9059834730836941392">അറിയിപ്പ് ചുരുക്കുക</translation> <translation id="9150735707954472829">ടാബ്</translation> <translation id="9161053988251441839">നിർദ്ദേശിച്ചിരിക്കുന്ന ആപ്പുകൾ</translation> <translation id="9170848237812810038">&പൂര്വാവസ്ഥയിലാക്കുക</translation> diff --git a/chromium/ui/strings/translations/ui_strings_mr.xtb b/chromium/ui/strings/translations/ui_strings_mr.xtb index 15799be80c4..3e3a09a3d20 100644 --- a/chromium/ui/strings/translations/ui_strings_mr.xtb +++ b/chromium/ui/strings/translations/ui_strings_mr.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(रिक्त)</translation> <translation id="2289052229480071835">आपल्या स्क्रीनवरील लक्ष्यांना स्पर्श करा टॅप करा.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">सूचना विस्तृत करा</translation> <translation id="2482878487686419369">सूचना</translation> <translation id="2497284189126895209">सर्व फाइल</translation> <translation id="2522350507219695259">कॅलिब्रेशन पूर्ण झाले</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">उघडा</translation> <translation id="9038489124413477075">अनामित फोल्डर</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 से}one{# से}other{# से}}</translation> +<translation id="9059834730836941392">सूचना संकुचित करा</translation> <translation id="9150735707954472829">टॅब</translation> <translation id="9161053988251441839">सुचवलेले अॅप्स</translation> <translation id="9170848237812810038">&पूर्ववत करा</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ms.xtb b/chromium/ui/strings/translations/ui_strings_ms.xtb index 21b6a456df6..a14ce96f241 100644 --- a/chromium/ui/strings/translations/ui_strings_ms.xtb +++ b/chromium/ui/strings/translations/ui_strings_ms.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(kosong)</translation> <translation id="2289052229480071835">Ketik sasaran sentuhan pada skrin anda.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Kembangkan pemberitahuan</translation> <translation id="2482878487686419369">Pemberitahuan</translation> <translation id="2497284189126895209">Semua Fail</translation> <translation id="2522350507219695259">Penentukuran telah selesai</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">buka</translation> <translation id="9038489124413477075">Folder Tanpa Nama</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 saat}other{# saat}}</translation> +<translation id="9059834730836941392">Runtuhkan pemberitahuan</translation> <translation id="9150735707954472829">Tab</translation> <translation id="9161053988251441839">APL DISYORKAN</translation> <translation id="9170848237812810038">&Buat asal</translation> diff --git a/chromium/ui/strings/translations/ui_strings_nl.xtb b/chromium/ui/strings/translations/ui_strings_nl.xtb index 5b25b062e4b..5c19318ec2b 100644 --- a/chromium/ui/strings/translations/ui_strings_nl.xtb +++ b/chromium/ui/strings/translations/ui_strings_nl.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(leeg)</translation> <translation id="2289052229480071835">Tik op de tikdoelen op je scherm.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Melding uitvouwen</translation> <translation id="2482878487686419369">Meldingen</translation> <translation id="2497284189126895209">Alle bestanden</translation> <translation id="2522350507219695259">Kalibratie is voltooid</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">openen</translation> <translation id="9038489124413477075">Naamloze map</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sec.}other{# sec.}}</translation> +<translation id="9059834730836941392">Melding samenvouwen</translation> <translation id="9150735707954472829">Tabblad</translation> <translation id="9161053988251441839">VOORGESTELDE APPS</translation> <translation id="9170848237812810038">&Ongedaan maken</translation> diff --git a/chromium/ui/strings/translations/ui_strings_no.xtb b/chromium/ui/strings/translations/ui_strings_no.xtb index d4aca96af68..60d8d3653a8 100644 --- a/chromium/ui/strings/translations/ui_strings_no.xtb +++ b/chromium/ui/strings/translations/ui_strings_no.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tom)</translation> <translation id="2289052229480071835">Trykk på berøringsmålene på skjermen.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB per sek</translation> +<translation id="24452542372838207">Vis varselet</translation> <translation id="2482878487686419369">Varsler</translation> <translation id="2497284189126895209">Alle filer</translation> <translation id="2522350507219695259">Kalibreringen er ferdig</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">åpne</translation> <translation id="9038489124413477075">Mappe uten navn</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sek}other{# sek}}</translation> +<translation id="9059834730836941392">Skjul varselet</translation> <translation id="9150735707954472829">Fane</translation> <translation id="9161053988251441839">FORESLÅTTE APPER</translation> <translation id="9170848237812810038">&Angre</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pl.xtb b/chromium/ui/strings/translations/ui_strings_pl.xtb index a0aa3514f97..3c71b17d470 100644 --- a/chromium/ui/strings/translations/ui_strings_pl.xtb +++ b/chromium/ui/strings/translations/ui_strings_pl.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(puste)</translation> <translation id="2289052229480071835">Kliknij docelowe obszary kliknięcia na ekranie.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Rozwiń powiadomienie</translation> <translation id="2482878487686419369">Powiadomienia</translation> <translation id="2497284189126895209">Wszystkie pliki</translation> <translation id="2522350507219695259">Kalibracja została ukończona</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">otwórz</translation> <translation id="9038489124413477075">Folder bez nazwy</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}few{# s}many{# s}other{# s}}</translation> +<translation id="9059834730836941392">Zwiń powiadomienie</translation> <translation id="9150735707954472829">Karta</translation> <translation id="9161053988251441839">SUGEROWANE APLIKACJE</translation> <translation id="9170848237812810038">&Cofnij</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb index c3e19d18cd8..f1610776240 100644 --- a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb +++ b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(vazio)</translation> <translation id="2289052229480071835">Toque nas áreas de toque da tela.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Expandir notificação</translation> <translation id="2482878487686419369">Notificações</translation> <translation id="2497284189126895209">Todos os arquivos</translation> <translation id="2522350507219695259">A calibração está concluída</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Pasta sem nome</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}one{# s}other{# s}}</translation> +<translation id="9059834730836941392">Recolher notificação</translation> <translation id="9150735707954472829">Guia</translation> <translation id="9161053988251441839">APPS SUGERIDOS</translation> <translation id="9170848237812810038">&Desfazer</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb index 7660857fc1a..10a29e211a6 100644 --- a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb +++ b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(vazio)</translation> <translation id="2289052229480071835">Toque nos alvos de toque no ecrã.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Expandir notificação</translation> <translation id="2482878487686419369">Notificações</translation> <translation id="2497284189126895209">Todos os ficheiros</translation> <translation id="2522350507219695259">A calibração foi concluída</translation> @@ -110,14 +111,14 @@ <translation id="6699343763173986273">Faixa seguinte de multimédia</translation> <translation id="6786750046913594791">Fechar pasta</translation> <translation id="6808150112686056157">Parar multimédia</translation> -<translation id="6845383723252244143">Seleccionar pasta</translation> +<translation id="6845383723252244143">Selecionar pasta</translation> <translation id="6845533974506654842">premir</translation> <translation id="6863590663815976734">{HOURS,plural, =1{Falta 1 hora}one{# hours left}other{Faltam # horas}}</translation> <translation id="688711909580084195">Página Web Sem Nome</translation> <translation id="6907759265145635167"><ph name="QUANTITY" /> PB/s</translation> <translation id="6917971086528278418">{YEARS,plural, =1{Falta 1 ano}one{# years left}other{Faltam # anos}}</translation> <translation id="6918245111648057970">Permitir as seguintes notificações para cada utilizador:</translation> -<translation id="6945221475159498467">Seleccionar</translation> +<translation id="6945221475159498467">Selecionar</translation> <translation id="6965382102122355670">OK</translation> <translation id="7052633198403197513">F1</translation> <translation id="7130207228079676353">MUITO PROVAVELMENTE</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Pasta sem nome</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 seg}one{# secs}other{# seg}}</translation> +<translation id="9059834730836941392">Reduzir notificação</translation> <translation id="9150735707954472829">Tabulação</translation> <translation id="9161053988251441839">APLICAÇÕES SUGERIDAS</translation> <translation id="9170848237812810038">An&ular</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ro.xtb b/chromium/ui/strings/translations/ui_strings_ro.xtb index 24dd4de1ad1..f53c0b1896a 100644 --- a/chromium/ui/strings/translations/ui_strings_ro.xtb +++ b/chromium/ui/strings/translations/ui_strings_ro.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(gol)</translation> <translation id="2289052229480071835">Atinge țintele de atingere de pe ecran.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Extinde notificarea</translation> <translation id="2482878487686419369">Notificări</translation> <translation id="2497284189126895209">Toate fișierele</translation> <translation id="2522350507219695259">Calibrarea este finalizată</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">deschide</translation> <translation id="9038489124413477075">Dosar fără nume</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{O sec.}few{# sec.}other{# sec.}}</translation> +<translation id="9059834730836941392">Restrânge notificarea</translation> <translation id="9150735707954472829">Filă</translation> <translation id="9161053988251441839">APLICAȚII SUGERATE</translation> <translation id="9170848237812810038">&Anulează</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ru.xtb b/chromium/ui/strings/translations/ui_strings_ru.xtb index 4169660ebbc..dfc0806c04d 100644 --- a/chromium/ui/strings/translations/ui_strings_ru.xtb +++ b/chromium/ui/strings/translations/ui_strings_ru.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(пусто)</translation> <translation id="2289052229480071835">Коснитесь интерактивных элементов на экране.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> ТБ/с</translation> +<translation id="24452542372838207">Развернуть уведомление</translation> <translation id="2482878487686419369">Оповещения</translation> <translation id="2497284189126895209">Все файлы</translation> <translation id="2522350507219695259">Калибровка завершена</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">открыть</translation> <translation id="9038489124413477075">Без названия</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 сек.}one{# сек.}few{# сек.}many{# сек.}other{# сек.}}</translation> +<translation id="9059834730836941392">Свернуть уведомление</translation> <translation id="9150735707954472829">Вкладка</translation> <translation id="9161053988251441839">РЕКОМЕНДУЕМЫЕ ПРИЛОЖЕНИЯ</translation> <translation id="9170848237812810038">&Отменить</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sk.xtb b/chromium/ui/strings/translations/ui_strings_sk.xtb index c73ccddb1a3..01db9a46bb6 100644 --- a/chromium/ui/strings/translations/ui_strings_sk.xtb +++ b/chromium/ui/strings/translations/ui_strings_sk.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(prázdne)</translation> <translation id="2289052229480071835">Klepnite na dotykové ciele na obrazovke.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Rozbaliť upozornenie</translation> <translation id="2482878487686419369">Upozornenia</translation> <translation id="2497284189126895209">Všetky súbory</translation> <translation id="2522350507219695259">Kalibrácia je dokončená</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">otvorenie</translation> <translation id="9038489124413477075">Priečinok bez názvu</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}few{# s}many{# s}other{# s}}</translation> +<translation id="9059834730836941392">Zbaliť upozornenie</translation> <translation id="9150735707954472829">Karta</translation> <translation id="9161053988251441839">ODPORÚČANÉ APLIKÁCIE</translation> <translation id="9170848237812810038">&Naspäť</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sl.xtb b/chromium/ui/strings/translations/ui_strings_sl.xtb index c6862817420..ea8fa4ac1f5 100644 --- a/chromium/ui/strings/translations/ui_strings_sl.xtb +++ b/chromium/ui/strings/translations/ui_strings_sl.xtb @@ -28,6 +28,7 @@ <translation id="2190355936436201913">(prazno)</translation> <translation id="2289052229480071835">Dotaknite se ciljev za dotik na zaslonu.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Razširi obvestilo</translation> <translation id="2482878487686419369">Obvestila</translation> <translation id="2497284189126895209">Vse datoteke</translation> <translation id="2522350507219695259">Umerjanje je končano</translation> @@ -152,6 +153,7 @@ <translation id="9002566407876343676">odpreti</translation> <translation id="9038489124413477075">Neimenovana mapa</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 s}one{# s}two{# s}few{# s}other{# s}}</translation> +<translation id="9059834730836941392">Strni obvestilo</translation> <translation id="9150735707954472829">Zavihek</translation> <translation id="9161053988251441839">PREDLAGANE APLIKACIJE</translation> <translation id="9170848237812810038">&Razveljavi</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sr.xtb b/chromium/ui/strings/translations/ui_strings_sr.xtb index abc0228cae5..5240068d11d 100644 --- a/chromium/ui/strings/translations/ui_strings_sr.xtb +++ b/chromium/ui/strings/translations/ui_strings_sr.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(празно)</translation> <translation id="2289052229480071835">Додирните циљна поља за додир на екрану.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Прошири обавештење</translation> <translation id="2482878487686419369">Обавештења</translation> <translation id="2497284189126895209">Све датотеке</translation> <translation id="2522350507219695259">Калибрација је довршена</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">отварање</translation> <translation id="9038489124413477075">Неименовани директоријум</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 сек}one{# сек}few{# сек}other{# сек}}</translation> +<translation id="9059834730836941392">Скупи обавештење</translation> <translation id="9150735707954472829">Картица</translation> <translation id="9161053988251441839">ПРЕДЛОЖЕНЕ АПЛИКАЦИЈЕ</translation> <translation id="9170848237812810038">&Опозови</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sv.xtb b/chromium/ui/strings/translations/ui_strings_sv.xtb index c9de27633e8..c64dcfb5fb7 100644 --- a/chromium/ui/strings/translations/ui_strings_sv.xtb +++ b/chromium/ui/strings/translations/ui_strings_sv.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tom)</translation> <translation id="2289052229480071835">Tryck på tryckområdena på skärmen.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/sek</translation> +<translation id="24452542372838207">Utöka avisering</translation> <translation id="2482878487686419369">Aviseringar</translation> <translation id="2497284189126895209">Alla filer</translation> <translation id="2522350507219695259">Kalibreringen är slutförd</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">öppna</translation> <translation id="9038489124413477075">Namnlös mapp</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sek}other{# sek}}</translation> +<translation id="9059834730836941392">Komprimera avisering</translation> <translation id="9150735707954472829">Flik</translation> <translation id="9161053988251441839">FÖRESLAGNA APPAR</translation> <translation id="9170848237812810038">&Ångra</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sw.xtb b/chromium/ui/strings/translations/ui_strings_sw.xtb index e2c318c23e0..c285e3059f5 100644 --- a/chromium/ui/strings/translations/ui_strings_sw.xtb +++ b/chromium/ui/strings/translations/ui_strings_sw.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(tupu)</translation> <translation id="2289052229480071835">Gonga viashirio vya kugusa kwenye skrini yako.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">Panua arifa</translation> <translation id="2482878487686419369">Arifa</translation> <translation id="2497284189126895209">Faili zote</translation> <translation id="2522350507219695259">Imemaliza kurekebisha usahihi</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">fungua</translation> <translation id="9038489124413477075">Folda isiyo na jina</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{Sekunde 1}other{Sekunde #}}</translation> +<translation id="9059834730836941392">Kunja arifa</translation> <translation id="9150735707954472829">Kichupo</translation> <translation id="9161053988251441839">PROGRAMU ZINAZOPENDEKEZWA</translation> <translation id="9170848237812810038">&Tendua</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ta.xtb b/chromium/ui/strings/translations/ui_strings_ta.xtb index 0ddb7cde10c..19457f4c5af 100644 --- a/chromium/ui/strings/translations/ui_strings_ta.xtb +++ b/chromium/ui/strings/translations/ui_strings_ta.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(காலி)</translation> <translation id="2289052229480071835">உங்கள் திரையில் இருக்கும் தொடுவதற்கான இலக்கிடங்களைத் தட்டவும்.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> டெ.பை/வி</translation> +<translation id="24452542372838207">அறிவிப்பை விரிவாக்கு</translation> <translation id="2482878487686419369">அறிவிப்புகள்</translation> <translation id="2497284189126895209">அனைத்து கோப்புகளும்</translation> <translation id="2522350507219695259">அளவுத்திருத்தம் முடிந்தது</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">திற</translation> <translation id="9038489124413477075">பெயரிடப்படாதக் கோப்புறை</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 நொடி}other{# நொடிகள்}}</translation> +<translation id="9059834730836941392">அறிவிப்பைச் சுருக்கு</translation> <translation id="9150735707954472829">தாவல்</translation> <translation id="9161053988251441839">பரிந்துரைக்கும் பயன்பாடுகள்</translation> <translation id="9170848237812810038">&செயல்தவிர்</translation> diff --git a/chromium/ui/strings/translations/ui_strings_te.xtb b/chromium/ui/strings/translations/ui_strings_te.xtb index 554cddcc884..6c8646b9b20 100644 --- a/chromium/ui/strings/translations/ui_strings_te.xtb +++ b/chromium/ui/strings/translations/ui_strings_te.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(ఖాళీ)</translation> <translation id="2289052229480071835">మీ స్క్రీన్పై ఉన్న స్పర్శ లక్ష్యాలను నొక్కండి.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">నోటిఫికేషన్ని విస్తరించు</translation> <translation id="2482878487686419369">ప్రకటనలు</translation> <translation id="2497284189126895209">మొత్తం ఫైళ్లు</translation> <translation id="2522350507219695259">క్రమాంకనం పూర్తయింది</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">తెరువు</translation> <translation id="9038489124413477075">పేరులేని ఫోల్డర్</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 సెక.}other{# సెక.}}</translation> +<translation id="9059834730836941392">నోటిఫికేషన్ని కుదించు</translation> <translation id="9150735707954472829">ట్యాబ్</translation> <translation id="9161053988251441839">సూచించబడిన అనువర్తనాలు</translation> <translation id="9170848237812810038">&అన్డు</translation> diff --git a/chromium/ui/strings/translations/ui_strings_th.xtb b/chromium/ui/strings/translations/ui_strings_th.xtb index 5fb90c761d3..ae7940bcf1c 100644 --- a/chromium/ui/strings/translations/ui_strings_th.xtb +++ b/chromium/ui/strings/translations/ui_strings_th.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(ว่างเปล่า)</translation> <translation id="2289052229480071835">แตะเป้าหมายการสัมผัสในหน้าจอ</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/วินาที</translation> +<translation id="24452542372838207">ขยายการแจ้งเตือน</translation> <translation id="2482878487686419369">การแจ้งเตือน</translation> <translation id="2497284189126895209">ไฟล์ทั้งหมด</translation> <translation id="2522350507219695259">การปรับเทียบเสร็จสมบูรณ์</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">เปิด</translation> <translation id="9038489124413477075">โฟลเดอร์ที่ไม่มีชื่อ</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 วินาที}other{# วินาที}}</translation> +<translation id="9059834730836941392">ยุบการแจ้งเตือน</translation> <translation id="9150735707954472829">แท็บ</translation> <translation id="9161053988251441839">แอปที่แนะนำ</translation> <translation id="9170848237812810038">เ&ลิกทำ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_tr.xtb b/chromium/ui/strings/translations/ui_strings_tr.xtb index dc33caa13a5..9992af5c08a 100644 --- a/chromium/ui/strings/translations/ui_strings_tr.xtb +++ b/chromium/ui/strings/translations/ui_strings_tr.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(boş)</translation> <translation id="2289052229480071835">Ekranınızda dokunma hedeflerine dokunun.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/sn</translation> +<translation id="24452542372838207">Bildirimi genişlet</translation> <translation id="2482878487686419369">Bildirimler</translation> <translation id="2497284189126895209">Tüm Dosyalar</translation> <translation id="2522350507219695259">Kalibrasyon tamamlandı</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">açma</translation> <translation id="9038489124413477075">Adsız Klasör</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 sn.}other{# sn.}}</translation> +<translation id="9059834730836941392">Bildirimi daralt</translation> <translation id="9150735707954472829">Sekme</translation> <translation id="9161053988251441839">ÖNERİLEN UYGULAMALAR</translation> <translation id="9170848237812810038">&Geri al</translation> diff --git a/chromium/ui/strings/translations/ui_strings_uk.xtb b/chromium/ui/strings/translations/ui_strings_uk.xtb index 93f0c155963..d1ffc268540 100644 --- a/chromium/ui/strings/translations/ui_strings_uk.xtb +++ b/chromium/ui/strings/translations/ui_strings_uk.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(пусто)</translation> <translation id="2289052229480071835">Торкніться елементів для дотику на екрані.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> ТБ/сек.</translation> +<translation id="24452542372838207">Розгорнути сповіщення</translation> <translation id="2482878487686419369">Сповіщення</translation> <translation id="2497284189126895209">Усі файли</translation> <translation id="2522350507219695259">Калібрування завершено</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">відкрити</translation> <translation id="9038489124413477075">Папка без назви</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 с}one{# с}few{# с}many{# с}other{# с}}</translation> +<translation id="9059834730836941392">Згорнути сповіщення</translation> <translation id="9150735707954472829">Вкладка</translation> <translation id="9161053988251441839">ПРОПОНОВАНІ ДОДАТКИ</translation> <translation id="9170848237812810038">&Скасувати</translation> diff --git a/chromium/ui/strings/translations/ui_strings_vi.xtb b/chromium/ui/strings/translations/ui_strings_vi.xtb index 58c7ae0da5e..cb24dbeed75 100644 --- a/chromium/ui/strings/translations/ui_strings_vi.xtb +++ b/chromium/ui/strings/translations/ui_strings_vi.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(trống)</translation> <translation id="2289052229480071835">Nhấn vào các mục tiêu cảm ứng trên màn hình của bạn.</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/giây</translation> +<translation id="24452542372838207">Mở rộng thông báo</translation> <translation id="2482878487686419369">Thông báo</translation> <translation id="2497284189126895209">Tất cả Tệp tin</translation> <translation id="2522350507219695259">Hiệu chỉnh xong</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">mở</translation> <translation id="9038489124413477075">Thư mục không có tên</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 giây}other{# giây}}</translation> +<translation id="9059834730836941392">Thu gọn thông báo</translation> <translation id="9150735707954472829">Tab</translation> <translation id="9161053988251441839">ỨNG DỤNG ĐƯỢC ĐỀ XUẤT</translation> <translation id="9170848237812810038">H&oàn tác</translation> diff --git a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb index e71d3406e52..11056d3a7ec 100644 --- a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb +++ b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(空)</translation> <translation id="2289052229480071835">点按您屏幕上的触摸目标。</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> +<translation id="24452542372838207">展开通知</translation> <translation id="2482878487686419369">通知</translation> <translation id="2497284189126895209">所有文件</translation> <translation id="2522350507219695259">已完成校准</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">打开</translation> <translation id="9038489124413477075">未命名的文件夹</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 秒}other{# 秒}}</translation> +<translation id="9059834730836941392">收起通知</translation> <translation id="9150735707954472829">标签</translation> <translation id="9161053988251441839">推荐的应用</translation> <translation id="9170848237812810038">撤消(&U)</translation> diff --git a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb index 4392f8050f9..0a48672dc18 100644 --- a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb +++ b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb @@ -32,6 +32,7 @@ <translation id="2190355936436201913">(空白)</translation> <translation id="2289052229480071835">輕觸畫面上的觸控目標。</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/秒</translation> +<translation id="24452542372838207">展開通知</translation> <translation id="2482878487686419369">通知</translation> <translation id="2497284189126895209">所有檔案</translation> <translation id="2522350507219695259">校正完成</translation> @@ -162,6 +163,7 @@ <translation id="9002566407876343676">開啟</translation> <translation id="9038489124413477075">未命名的資料夾</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 秒}other{# 秒}}</translation> +<translation id="9059834730836941392">收合通知</translation> <translation id="9150735707954472829">分頁</translation> <translation id="9161053988251441839">建議使用的應用程式</translation> <translation id="9170848237812810038">取消(&U)</translation> diff --git a/chromium/ui/strings/ui_strings.grd b/chromium/ui/strings/ui_strings.grd index 7ece91dd9d8..310e90a4ffc 100644 --- a/chromium/ui/strings/ui_strings.grd +++ b/chromium/ui/strings/ui_strings.grd @@ -634,6 +634,12 @@ need to be translated for each locale.--> <message name="IDS_MESSAGE_CENTER_CLEAR_ALL" desc="The button for clearing all notifications."> Clear All </message> + <message name="IDS_MESSAGE_CENTER_EXPAND_NOTIFICATION" desc="The button for expanding the notification. By clicking this, the long message and the buttons of the notification are shown."> + Expand notification + </message> + <message name="IDS_MESSAGE_CENTER_COLLAPSE_NOTIFICATION" desc="The button for collapsing the notification. By clicking this, the long message and the buttons of the notification are hidden."> + Collapse notification + </message> <message name="IDS_MESSAGE_CENTER_LIST_NOTIFICATION_MESSAGE_WITH_DIVIDER" desc="The divider symbol of the title and the message of a sub item in list notification. In English, the title is on the left of this text. In RTL languages, the title is on the right of this message."> ''' - <ph name="message">$1<ex>This is the message!</ex></ph>''' </message> diff --git a/chromium/ui/touch_selection/BUILD.gn b/chromium/ui/touch_selection/BUILD.gn index 110a4e0626e..233dffad714 100644 --- a/chromium/ui/touch_selection/BUILD.gn +++ b/chromium/ui/touch_selection/BUILD.gn @@ -38,9 +38,10 @@ component("touch_selection") { if (use_aura) { deps += [ + "//services/ui/public/interfaces", "//skia:skia", - "//ui/aura:aura", - "//ui/aura_extra:aura_extra", + "//ui/aura", + "//ui/aura_extra", "//ui/compositor:compositor", "//ui/gfx:gfx", "//ui/resources", diff --git a/chromium/ui/touch_selection/DEPS b/chromium/ui/touch_selection/DEPS index 5b830344f32..e81e69afb83 100644 --- a/chromium/ui/touch_selection/DEPS +++ b/chromium/ui/touch_selection/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+services/ui/public/interfaces", "+ui/aura", "+ui/aura_extra", "+ui/base", diff --git a/chromium/ui/touch_selection/touch_handle.cc b/chromium/ui/touch_selection/touch_handle.cc index ad81711ac6b..ee1c765d17e 100644 --- a/chromium/ui/touch_selection/touch_handle.cc +++ b/chromium/ui/touch_selection/touch_handle.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ui/touch_selection/touch_handle.h" +#include "base/metrics/histogram_macros.h" #include <algorithm> #include <cmath> @@ -95,6 +96,7 @@ void TouchHandle::SetEnabled(bool enabled) { if (enabled_ == enabled) return; if (!enabled) { + SetVisible(false, ANIMATION_NONE); EndDrag(); EndFade(); } @@ -262,7 +264,7 @@ void TouchHandle::UpdateHandleLayout() { // Update mirror values only when dragging has stopped to prevent unwanted // inversion while dragging of handles. - if (client_->IsAdaptiveHandleOrientationEnabled() && !is_dragging_) { + if (!is_dragging_) { gfx::RectF handle_bounds = drawable_->GetVisibleBounds(); bool mirror_horizontal = false; bool mirror_vertical = false; @@ -276,22 +278,52 @@ void TouchHandle::UpdateHandleLayout() { const float top_y_mirrored = focus_top_.y() - handle_height + viewport_rect_.y(); - // In case the viewport height is small, like webview, avoid inversion. - if (bottom_y_unmirrored > viewport_rect_.bottom() && - top_y_mirrored > viewport_rect_.y()) { - mirror_vertical = true; + const float bottom_y_clipped = + std::max(bottom_y_unmirrored - viewport_rect_.bottom(), 0.f); + const float top_y_clipped = + std::max(viewport_rect_.y() - top_y_mirrored, 0.f); + + mirror_vertical = top_y_clipped < bottom_y_clipped; + + const float best_y_clipped = + mirror_vertical ? top_y_clipped : bottom_y_clipped; + + UMA_HISTOGRAM_PERCENTAGE( + "Event.TouchSelectionHandle.BottomHandleClippingPercentage", + static_cast<int>((bottom_y_clipped / handle_height) * 100)); + UMA_HISTOGRAM_PERCENTAGE( + "Event.TouchSelectionHandle.BestVerticalClippingPercentage", + static_cast<int>((best_y_clipped / handle_height) * 100)); + UMA_HISTOGRAM_BOOLEAN( + "Event.TouchSelectionHandle.ShouldFlipHandleVertically", + mirror_vertical); + UMA_HISTOGRAM_PERCENTAGE( + "Event.TouchSelectionHandle.FlippingImprovementPercentage", + static_cast<int>(((bottom_y_clipped - best_y_clipped) / handle_height) * + 100)); + + if (orientation_ == TouchHandleOrientation::LEFT) { + const float left_x_clipped = std::max( + viewport_rect_.x() - (focus_bottom_.x() - handle_width), 0.f); + UMA_HISTOGRAM_PERCENTAGE( + "Event.TouchSelectionHandle.LeftHandleClippingPercentage", + static_cast<int>((left_x_clipped / handle_height) * 100)); + if (left_x_clipped > 0) + mirror_horizontal = true; + } else if (orientation_ == TouchHandleOrientation::RIGHT) { + const float right_x_clipped = std::max( + (focus_bottom_.x() + handle_width) - viewport_rect_.right(), 0.f); + UMA_HISTOGRAM_PERCENTAGE( + "Event.TouchSelectionHandle.RightHandleClippingPercentage", + static_cast<int>((right_x_clipped / handle_height) * 100)); + if (right_x_clipped > 0) + mirror_horizontal = true; } - if (orientation_ == TouchHandleOrientation::LEFT && - focus_bottom_.x() - handle_width < viewport_rect_.x()) { - mirror_horizontal = true; - } else if (orientation_ == TouchHandleOrientation::RIGHT && - focus_bottom_.x() + handle_width > viewport_rect_.right()) { - mirror_horizontal = true; + if (client_->IsAdaptiveHandleOrientationEnabled()) { + mirror_horizontal_ = mirror_horizontal; + mirror_vertical_ = mirror_vertical; } - - mirror_horizontal_ = mirror_horizontal; - mirror_vertical_ = mirror_vertical; } drawable_->SetOrientation(orientation_, mirror_vertical_, mirror_horizontal_); diff --git a/chromium/ui/touch_selection/touch_handle.h b/chromium/ui/touch_selection/touch_handle.h index 5d581e50034..3815fea5237 100644 --- a/chromium/ui/touch_selection/touch_handle.h +++ b/chromium/ui/touch_selection/touch_handle.h @@ -122,6 +122,7 @@ class UI_TOUCH_SELECTION_EXPORT TouchHandle : public TouchSelectionDraggable { const gfx::PointF& focus_bottom() const { return focus_bottom_; } TouchHandleOrientation orientation() const { return orientation_; } + float alpha() const { return alpha_; } private: gfx::PointF ComputeHandleOrigin() const; diff --git a/chromium/ui/touch_selection/touch_handle_drawable_aura.cc b/chromium/ui/touch_selection/touch_handle_drawable_aura.cc index d61ff09ac71..ed2914fef14 100644 --- a/chromium/ui/touch_selection/touch_handle_drawable_aura.cc +++ b/chromium/ui/touch_selection/touch_handle_drawable_aura.cc @@ -4,6 +4,7 @@ #include "ui/touch_selection/touch_handle_drawable_aura.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "ui/aura/window.h" #include "ui/aura/window_targeter.h" #include "ui/aura_extra/image_window_delegate.h" @@ -68,7 +69,7 @@ TouchHandleDrawableAura::TouchHandleDrawableAura(aura::Window* parent) window_->SetTransparent(true); window_->Init(LAYER_TEXTURED); window_->set_owned_by_parent(false); - window_->set_ignore_events(true); + window_->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); parent->AddChild(window_.get()); } diff --git a/chromium/ui/touch_selection/touch_handle_unittest.cc b/chromium/ui/touch_selection/touch_handle_unittest.cc index 7c07f4462bd..890e86d52e2 100644 --- a/chromium/ui/touch_selection/touch_handle_unittest.cc +++ b/chromium/ui/touch_selection/touch_handle_unittest.cc @@ -337,8 +337,11 @@ TEST_F(TouchHandleTest, Enabled) { // Disabling mid-animation should cancel the animation. handle.SetEnabled(true); - UpdateHandleVisibility(handle, false, TouchHandle::ANIMATION_SMOOTH); - EXPECT_TRUE(drawable().visible); + UpdateHandleVisibility(handle, true, TouchHandle::ANIMATION_SMOOTH); + EXPECT_TRUE(drawable().enabled); + EXPECT_EQ(0.f, drawable().alpha); + // Since alpha value is 0, visibility of drawable will be false. + EXPECT_FALSE(drawable().visible); EXPECT_TRUE(GetAndResetNeedsAnimate()); handle.SetEnabled(false); EXPECT_FALSE(drawable().enabled); 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 17bdfa6691c..fe5a14306f1 100644 --- a/chromium/ui/touch_selection/touch_selection_controller_test_api.cc +++ b/chromium/ui/touch_selection/touch_selection_controller_test_api.cc @@ -20,6 +20,20 @@ bool TouchSelectionControllerTestApi::GetEndVisible() const { return controller_->GetEndVisible(); } +float TouchSelectionControllerTestApi::GetStartAlpha() const { + if (controller_->active_status_ == TouchSelectionController::SELECTION_ACTIVE) + return controller_->start_selection_handle_->alpha(); + + return 0.f; +} + +float TouchSelectionControllerTestApi::GetEndAlpha() const { + if (controller_->active_status_ == TouchSelectionController::SELECTION_ACTIVE) + return controller_->end_selection_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 66ce6db027d..6454b9e8999 100644 --- a/chromium/ui/touch_selection/touch_selection_controller_test_api.h +++ b/chromium/ui/touch_selection/touch_selection_controller_test_api.h @@ -20,6 +20,8 @@ class TouchSelectionControllerTestApi { bool GetStartVisible() const; bool GetEndVisible() const; + float GetStartAlpha() const; + float GetEndAlpha() 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 9d59383729c..a9089df1fd6 100644 --- a/chromium/ui/touch_selection/touch_selection_controller_unittest.cc +++ b/chromium/ui/touch_selection/touch_selection_controller_unittest.cc @@ -876,15 +876,20 @@ TEST_F(TouchSelectionControllerTest, TemporarilyHidden) { EXPECT_FALSE(test_controller.GetStartVisible()); EXPECT_FALSE(test_controller.GetEndVisible()); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); + EXPECT_EQ(0.f, test_controller.GetEndAlpha()); + visible = false; ChangeInsertion(insertion_rect, visible); EXPECT_FALSE(GetAndResetNeedsAnimate()); EXPECT_FALSE(test_controller.GetStartVisible()); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); visible = true; ChangeInsertion(insertion_rect, visible); EXPECT_FALSE(GetAndResetNeedsAnimate()); EXPECT_FALSE(test_controller.GetStartVisible()); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); controller().SetTemporarilyHidden(false); EXPECT_TRUE(GetAndResetNeedsAnimate()); @@ -935,6 +940,8 @@ TEST_F(TouchSelectionControllerTest, LongPressDrag) { // drag gesture are pending. EXPECT_FALSE(test_controller.GetStartVisible()); EXPECT_FALSE(test_controller.GetEndVisible()); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); + EXPECT_EQ(0.f, test_controller.GetEndAlpha()); // The selection coordinates should reflect the drag movement. gfx::PointF fixed_offset = start_rect.CenterPoint(); @@ -970,6 +977,8 @@ TEST_F(TouchSelectionControllerTest, LongPressDrag) { // The handles should still be hidden. EXPECT_FALSE(test_controller.GetStartVisible()); EXPECT_FALSE(test_controller.GetEndVisible()); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); + EXPECT_EQ(0.f, test_controller.GetEndAlpha()); // Releasing the touch sequence should end the drag and show the handles. EXPECT_FALSE(controller().WillHandleTouchEvent(event.ReleasePoint())); @@ -1002,6 +1011,9 @@ TEST_F(TouchSelectionControllerTest, LongPressNoDrag) { EXPECT_FALSE(test_controller.GetStartVisible()); EXPECT_FALSE(test_controller.GetEndVisible()); + EXPECT_EQ(0.f, test_controller.GetStartAlpha()); + EXPECT_EQ(0.f, test_controller.GetEndAlpha()); + // If no drag movement occurs, the handles should reappear after the touch // is released. EXPECT_FALSE(controller().WillHandleTouchEvent(event.ReleasePoint())); @@ -1031,6 +1043,9 @@ TEST_F(TouchSelectionControllerTest, NoLongPressDragIfDisabled) { EXPECT_TRUE(test_controller.GetStartVisible()); EXPECT_TRUE(test_controller.GetEndVisible()); + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); + // Subsequent motion of the same touch sequence after longpress shouldn't // trigger drag selection. EXPECT_FALSE(controller().WillHandleTouchEvent(event.MovePoint(0, 0, 0))); @@ -1045,6 +1060,9 @@ TEST_F(TouchSelectionControllerTest, NoLongPressDragIfDisabled) { EXPECT_THAT(GetAndResetEvents(), IsEmpty()); EXPECT_TRUE(test_controller.GetStartVisible()); EXPECT_TRUE(test_controller.GetEndVisible()); + + EXPECT_EQ(1.f, test_controller.GetStartAlpha()); + EXPECT_EQ(1.f, test_controller.GetEndAlpha()); } TEST_F(TouchSelectionControllerTest, RectBetweenBounds) { diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn index f08e2b91ef5..6a7ad0283d3 100644 --- a/chromium/ui/views/BUILD.gn +++ b/chromium/ui/views/BUILD.gn @@ -53,6 +53,7 @@ component("views") { "animation/ink_drop_ripple_observer.h", "animation/ink_drop_state.h", "animation/ink_drop_stub.h", + "animation/ink_drop_util.h", "animation/scroll_animator.h", "animation/square_ink_drop_ripple.h", "background.h", @@ -82,7 +83,6 @@ component("views") { "controls/button/blue_button.h", "controls/button/button.h", "controls/button/checkbox.h", - "controls/button/custom_button.h", "controls/button/image_button.h", "controls/button/image_button_factory.h", "controls/button/label_button.h", @@ -185,6 +185,7 @@ component("views") { "native_cursor.h", "native_theme_delegate.h", "painter.h", + "paint_info.h", "pointer_watcher.h", "rect_based_targeting_utils.h", "repeat_controller.h", @@ -229,6 +230,7 @@ component("views") { "window/frame_background.h", "window/frame_buttons.h", "window/native_frame_view.h", + "window/nav_button_provider.h", "window/non_client_view.h", "window/window_button_order_provider.h", "window/window_resources.h", @@ -250,6 +252,7 @@ component("views") { "animation/ink_drop_ripple.cc", "animation/ink_drop_state.cc", "animation/ink_drop_stub.cc", + "animation/ink_drop_util.cc", "animation/scroll_animator.cc", "animation/square_ink_drop_ripple.cc", "background.cc", @@ -276,7 +279,6 @@ component("views") { "controls/button/blue_button.cc", "controls/button/button.cc", "controls/button/checkbox.cc", - "controls/button/custom_button.cc", "controls/button/image_button.cc", "controls/button/image_button_factory.cc", "controls/button/label_button.cc", @@ -364,6 +366,7 @@ component("views") { "mouse_watcher.cc", "mouse_watcher_view_host.cc", "native_cursor_mac.mm", + "paint_info.cc", "painter.cc", "rect_based_targeting_utils.cc", "repeat_controller.cc", @@ -462,7 +465,7 @@ component("views") { "//ui/views/resources", ] - if (use_x11 && !is_chromeos) { + if (use_x11) { deps += [ "//ui/display/util" ] } if (is_linux && !is_chromeos) { @@ -495,6 +498,7 @@ component("views") { "win/hwnd_message_handler.h", "win/hwnd_message_handler_delegate.h", "win/hwnd_util.h", + "win/pen_event_processor.h", "win/scoped_fullscreen_visibility.h", "win/windows_session_change_observer.h", ] @@ -503,6 +507,7 @@ component("views") { "win/fullscreen_handler.cc", "win/hwnd_message_handler.cc", "win/hwnd_util_aurawin.cc", + "win/pen_event_processor.cc", "win/scoped_fullscreen_visibility.cc", "win/windows_session_change_observer.cc", ] @@ -542,7 +547,6 @@ component("views") { "accessibility/ax_view_obj_wrapper.h", "accessibility/ax_widget_obj_wrapper.h", "accessibility/ax_window_obj_wrapper.h", - "bubble/bubble_window_targeter.h", "bubble/tray_bubble_view.h", "controls/menu/menu_pre_target_handler.h", "controls/native/native_view_host_aura.h", @@ -575,7 +579,6 @@ component("views") { "accessibility/ax_view_obj_wrapper.cc", "accessibility/ax_widget_obj_wrapper.cc", "accessibility/ax_window_obj_wrapper.cc", - "bubble/bubble_window_targeter.cc", "bubble/tray_bubble_view.cc", "controls/menu/display_change_listener_aura.cc", "controls/menu/menu_pre_target_handler.cc", @@ -824,7 +827,7 @@ static_library("test_support_internal") { "//ui/wm", "//ui/wm/public", ] - if (use_x11 && !is_chromeos) { + if (use_x11) { sources += [ "test/desktop_screen_x11_test_api.cc", "test/desktop_screen_x11_test_api.h", @@ -889,17 +892,17 @@ source_set("views_unittests_sources") { "bubble/bubble_border_unittest.cc", "bubble/bubble_dialog_delegate_unittest.cc", "bubble/bubble_frame_view_unittest.cc", - "bubble/bubble_window_targeter_unittest.cc", "cocoa/bridged_native_widget_unittest.mm", "cocoa/cocoa_mouse_capture_unittest.mm", "cocoa/drag_drop_client_mac_unittest.mm", "controls/button/blue_button_unittest.cc", - "controls/button/custom_button_unittest.cc", + "controls/button/button_unittest.cc", "controls/button/image_button_factory_unittest.cc", "controls/button/image_button_unittest.cc", "controls/button/label_button_label_unittest.cc", "controls/button/label_button_unittest.cc", "controls/button/menu_button_unittest.cc", + "controls/button/radio_button_unittest.cc", "controls/button/toggle_button_unittest.cc", "controls/combobox/combobox_unittest.cc", "controls/label_unittest.cc", @@ -933,6 +936,7 @@ source_set("views_unittests_sources") { "layout/box_layout_unittest.cc", "layout/fill_layout_unittest.cc", "layout/grid_layout_unittest.cc", + "paint_info_unittest.cc", "rect_based_targeting_utils_unittest.cc", "test/widget_test_unittest.cc", "view_model_unittest.cc", @@ -1002,6 +1006,7 @@ source_set("views_unittests_sources") { "oleacc.lib", "comctl32.lib", ] + sources += [ "win/pen_event_processor_unittest.cc" ] } if (has_native_accessibility) { @@ -1038,10 +1043,7 @@ source_set("views_unittests_sources") { if (is_mac) { # views_unittests not yet compiling on Mac. http://crbug.com/378134 - sources -= [ - "bubble/bubble_window_targeter_unittest.cc", - "controls/native/native_view_host_unittest.cc", - ] + sources -= [ "controls/native/native_view_host_unittest.cc" ] public_deps += [ "//ui/accelerated_widget_mac" ] } } diff --git a/chromium/ui/views/DEPS b/chromium/ui/views/DEPS index ee243276fc5..edc6d257814 100644 --- a/chromium/ui/views/DEPS +++ b/chromium/ui/views/DEPS @@ -36,5 +36,8 @@ specific_include_rules = { ], "views_test_suite\.cc": [ "+gpu/ipc/service", + ], + "paint_info_unittest\.cc": [ + "+cc/base", ] } diff --git a/chromium/ui/views/accessibility/OWNERS b/chromium/ui/views/accessibility/OWNERS index bdb9178c0a6..2c85844e32c 100644 --- a/chromium/ui/views/accessibility/OWNERS +++ b/chromium/ui/views/accessibility/OWNERS @@ -1,5 +1,4 @@ -dmazzoni@chromium.org -dtseng@chromium.org +file://ui/accessibility/OWNERS # TEAM: chromium-accessibility@chromium.org # COMPONENT: UI>Accessibility diff --git a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc index b0898a68810..bad990722ed 100644 --- a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc +++ b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc @@ -88,7 +88,7 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestOnCaretBoundsChangeInTextField) { textfield_test_api.ExecuteTextEditCommand( ui::TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT); - gfx::Point caret_position = textfield_test_api.GetCursorViewOrigin(); + gfx::Point caret_position = textfield_test_api.GetCursorViewRect().origin(); LONG x, y, width, height; EXPECT_EQ(S_OK, caret_accessible->accLocation(&x, &y, &width, &height, self_)); @@ -98,7 +98,7 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestOnCaretBoundsChangeInTextField) { textfield_test_api.ExecuteTextEditCommand( ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT); - gfx::Point caret_position2 = textfield_test_api.GetCursorViewOrigin(); + gfx::Point caret_position2 = textfield_test_api.GetCursorViewRect().origin(); EXPECT_NE(caret_position, caret_position2); EXPECT_EQ(S_OK, caret_accessible->accLocation(&x, &y, &width, &height, self_)); diff --git a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc index e16ba4a95de..4c1afebdd43 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_auralinux.cc @@ -131,12 +131,7 @@ class AuraLinuxApplication } ui::AXPlatformNodeAuraLinux::SetApplication(platform_node_); if (ViewsDelegate::GetInstance()) { - // This should be on the a blocking pool thread so that we can open - // libatk-bridge.so without blocking this thread. - scoped_refptr<base::TaskRunner> init_task_runner = - ViewsDelegate::GetInstance()->GetBlockingPoolTaskRunner(); - if (init_task_runner) - ui::AXPlatformNodeAuraLinux::StaticInitialize(init_task_runner); + ui::AXPlatformNodeAuraLinux::StaticInitialize(); } } diff --git a/chromium/ui/views/accessibility/native_view_accessibility_win.cc b/chromium/ui/views/accessibility/native_view_accessibility_win.cc index 153e9281f9e..af44ab0c4c7 100644 --- a/chromium/ui/views/accessibility/native_view_accessibility_win.cc +++ b/chromium/ui/views/accessibility/native_view_accessibility_win.cc @@ -22,7 +22,7 @@ #include "ui/base/layout.h" #include "ui/base/win/accessibility_misc_utils.h" #include "ui/base/win/atl_module.h" -#include "ui/views/controls/button/custom_button.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" diff --git a/chromium/ui/views/animation/OWNERS b/chromium/ui/views/animation/OWNERS index 9710b4e0f93..be29560c22a 100644 --- a/chromium/ui/views/animation/OWNERS +++ b/chromium/ui/views/animation/OWNERS @@ -1 +1 @@ -per-file *ink*=bruthig@chromium.org +per-file *ink*=mohsen@chromium.org diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc index 33b5c69b35c..849ee7dc85c 100644 --- a/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc +++ b/chromium/ui/views/animation/flood_fill_ink_drop_ripple.cc @@ -14,6 +14,7 @@ #include "ui/gfx/animation/animation.h" #include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/vector2d_f.h" +#include "ui/views/animation/ink_drop_util.h" namespace { @@ -213,8 +214,10 @@ void FloodFillInkDropRipple::AnimateStateChange( } break; case InkDropState::ACTION_PENDING: { - DCHECK_EQ(InkDropState::HIDDEN, old_ink_drop_state) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + DLOG_IF(WARNING, InkDropState::HIDDEN != old_ink_drop_state) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); AnimateToOpacity(visible_opacity_, GetAnimationDuration(ACTION_PENDING_FADE_IN), @@ -232,9 +235,12 @@ void FloodFillInkDropRipple::AnimateStateChange( break; } case InkDropState::ACTION_TRIGGERED: { - DCHECK(old_ink_drop_state == InkDropState::HIDDEN || - old_ink_drop_state == InkDropState::ACTION_PENDING) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + DLOG_IF(WARNING, old_ink_drop_state != InkDropState::HIDDEN && + old_ink_drop_state != InkDropState::ACTION_PENDING) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); + if (old_ink_drop_state == InkDropState::HIDDEN) { AnimateStateChange(old_ink_drop_state, InkDropState::ACTION_PENDING, animation_observer); @@ -246,8 +252,11 @@ void FloodFillInkDropRipple::AnimateStateChange( break; } case InkDropState::ALTERNATE_ACTION_PENDING: { - DCHECK_EQ(InkDropState::ACTION_PENDING, old_ink_drop_state) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + DLOG_IF(WARNING, InkDropState::ACTION_PENDING != old_ink_drop_state) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); + AnimateToOpacity(visible_opacity_, GetAnimationDuration(ALTERNATE_ACTION_PENDING), ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, @@ -259,8 +268,12 @@ void FloodFillInkDropRipple::AnimateStateChange( break; } case InkDropState::ALTERNATE_ACTION_TRIGGERED: - DCHECK_EQ(InkDropState::ALTERNATE_ACTION_PENDING, old_ink_drop_state) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + DLOG_IF(WARNING, + InkDropState::ALTERNATE_ACTION_PENDING != old_ink_drop_state) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); + AnimateToOpacity(kHiddenOpacity, GetAnimationDuration( ALTERNATE_ACTION_TRIGGERED_FADE_OUT), ui::LayerAnimator::ENQUEUE_NEW_ANIMATION, @@ -414,6 +427,10 @@ gfx::Transform FloodFillInkDropRipple::CalculateTransform( circle_layer_delegate_.GetCenteringOffset(); transform.Translate(-drawn_center_offset.x(), -drawn_center_offset.y()); + // Add subpixel correction to the transform. + transform.ConcatTransform(GetTransformSubpixelCorrection( + transform, painted_layer_.device_scale_factor())); + return transform; } diff --git a/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc b/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc index 44ea59f9523..f61dae86e7d 100644 --- a/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc +++ b/chromium/ui/views/animation/flood_fill_ink_drop_ripple_unittest.cc @@ -7,6 +7,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_conversions.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/views/animation/test/flood_fill_ink_drop_ripple_test_api.h" @@ -29,10 +30,11 @@ TEST(FloodFillInkDropRippleTest, TransformedCenterPointForIrregularClipBounds) { SK_ColorWHITE, 0.175f); FloodFillInkDropRippleTestApi test_api(&ripple); - gfx::Point actual_center = test_api.GetDrawnCenterPoint(); + gfx::Point3F actual_center(gfx::PointF(test_api.GetDrawnCenterPoint())); test_api.TransformPoint(10, &actual_center); - EXPECT_EQ(expected_center_point, actual_center); + EXPECT_EQ(expected_center_point, + gfx::ToRoundedPoint(actual_center.AsPointF())); } TEST(FloodFillInkDropRippleTest, MaxDistanceToCorners) { @@ -107,5 +109,40 @@ TEST(FloodFillInkDropRippleTest, ActivatedFinalState) { activated_transform.ApproximatelyEqual(pending_activated_transform)); } +TEST(FloodFillInkDropRippleTest, TransformIsPixelAligned) { + const float kEpsilon = 0.001f; + + const gfx::Size host_size(11, 11); + // Keep the draw center different from the the host center to have a non zero + // offset in the transformation. + const gfx::Point center_point(host_size.width() / 3, host_size.height() / 3); + const SkColor color = SK_ColorYELLOW; + const float visible_opacity = 0.3f; + + FloodFillInkDropRipple ripple(host_size, center_point, color, + visible_opacity); + FloodFillInkDropRippleTestApi test_api(&ripple); + + for (auto dsf : {1.25, 1.33, 1.5, 1.6, 1.75, 1.8, 2.25}) { + SCOPED_TRACE(testing::Message() + << std::endl + << "Device Scale Factor: " << dsf << std::endl); + ripple.GetRootLayer()->OnDeviceScaleFactorChanged(dsf); + gfx::Point3F ripple_origin; + + test_api.TransformPoint(host_size.width() / 2, &ripple_origin); + + // Apply device scale factor to get the final offset. + gfx::Transform dsf_transform; + dsf_transform.Scale(dsf, dsf); + dsf_transform.TransformPoint(&ripple_origin); + + EXPECT_NEAR(ripple_origin.x(), gfx::ToRoundedInt(ripple_origin.x()), + kEpsilon); + EXPECT_NEAR(ripple_origin.y(), gfx::ToRoundedInt(ripple_origin.y()), + kEpsilon); + } +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_highlight.cc b/chromium/ui/views/animation/ink_drop_highlight.cc index effde4973db..98c9030845e 100644 --- a/chromium/ui/views/animation/ink_drop_highlight.cc +++ b/chromium/ui/views/animation/ink_drop_highlight.cc @@ -17,6 +17,7 @@ #include "ui/gfx/geometry/insets.h" #include "ui/views/animation/ink_drop_highlight_observer.h" #include "ui/views/animation/ink_drop_painted_layer_delegates.h" +#include "ui/views/animation/ink_drop_util.h" namespace views { @@ -161,6 +162,11 @@ gfx::Transform InkDropHighlight::CalculateTransform( size_.height() == 0 ? 0 : size.height() / size_.height()); gfx::Vector2dF layer_offset = layer_delegate_->GetCenteringOffset(); transform.Translate(-layer_offset.x(), -layer_offset.y()); + + // Add subpixel correction to the transform. + transform.ConcatTransform( + GetTransformSubpixelCorrection(transform, layer_->device_scale_factor())); + return transform; } diff --git a/chromium/ui/views/animation/ink_drop_highlight_unittest.cc b/chromium/ui/views/animation/ink_drop_highlight_unittest.cc index 94dc954bfb9..95380dba477 100644 --- a/chromium/ui/views/animation/ink_drop_highlight_unittest.cc +++ b/chromium/ui/views/animation/ink_drop_highlight_unittest.cc @@ -10,10 +10,12 @@ #include "base/memory/ptr_util.h" #include "base/time/time.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/animation/animation.h" #include "ui/gfx/animation/animation_test_api.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/transform.h" #include "ui/views/animation/test/ink_drop_highlight_test_api.h" #include "ui/views/animation/test/test_ink_drop_highlight_observer.h" @@ -206,5 +208,35 @@ TEST_F(InkDropHighlightTest, AnimatingAZeroSizeHighlight) { false /* explode */); } +TEST_F(InkDropHighlightTest, TransformIsPixelAligned) { + const float kEpsilon = 0.001f; + gfx::Size highlight_size(10, 10); + InitHighlight(base::MakeUnique<InkDropHighlight>( + highlight_size, 3, gfx::PointF(3.5f, 3.5f), SK_ColorYELLOW)); + const gfx::PointF layer_origin( + ink_drop_highlight()->layer()->bounds().origin()); + for (auto dsf : {1.25, 1.33, 1.5, 1.6, 1.75, 1.8, 2.25}) { + SCOPED_TRACE(testing::Message() + << std::endl + << "Device Scale Factor: " << dsf << std::endl); + ink_drop_highlight()->layer()->OnDeviceScaleFactorChanged(dsf); + + const gfx::SizeF size(highlight_size); + gfx::Transform transform = test_api()->CalculateTransform(size); + gfx::Point3F transformed_layer_origin(layer_origin.x(), layer_origin.y(), + 0); + transform.TransformPoint(&transformed_layer_origin); + + // Apply device scale factor to get the final offset. + gfx::Transform dsf_transform; + dsf_transform.Scale(dsf, dsf); + dsf_transform.TransformPoint(&transformed_layer_origin); + EXPECT_NEAR(transformed_layer_origin.x(), + gfx::ToRoundedInt(transformed_layer_origin.x()), kEpsilon); + EXPECT_NEAR(transformed_layer_origin.y(), + gfx::ToRoundedInt(transformed_layer_origin.y()), kEpsilon); + } +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc index 1856e89293e..3124f8266fc 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.cc +++ b/chromium/ui/views/animation/ink_drop_host_view.cc @@ -118,7 +118,8 @@ gfx::Size InkDropHostView::CalculateLargeInkDropSize( InkDropHostView::InkDropHostView() : ink_drop_mode_(InkDropMode::OFF), ink_drop_(nullptr), - ink_drop_visible_opacity_(kInkDropVisibleOpacity), + ink_drop_visible_opacity_( + PlatformStyle::kUseRipples ? kInkDropVisibleOpacity : 0), old_paint_to_layer_(false), destroying_(false) {} @@ -277,7 +278,7 @@ bool InkDropHostView::HasInkDrop() const { InkDrop* InkDropHostView::GetInkDrop() { if (!ink_drop_) { - if (ink_drop_mode_ == InkDropMode::OFF || !PlatformStyle::kUseRipples) + if (ink_drop_mode_ == InkDropMode::OFF) ink_drop_ = base::MakeUnique<InkDropStub>(); else ink_drop_ = CreateInkDrop(); diff --git a/chromium/ui/views/animation/ink_drop_mask.cc b/chromium/ui/views/animation/ink_drop_mask.cc index d95286d91c9..104c4b3389e 100644 --- a/chromium/ui/views/animation/ink_drop_mask.cc +++ b/chromium/ui/views/animation/ink_drop_mask.cc @@ -48,9 +48,15 @@ void RoundRectInkDropMask::OnPaintLayer(const ui::PaintContext& context) { flags.setAntiAlias(true); ui::PaintRecorder recorder(context, layer()->size()); - gfx::RectF bounds(layer()->bounds()); - bounds.Inset(mask_insets_); - recorder.canvas()->DrawRoundRect(bounds, corner_radius_, flags); + const float dsf = recorder.canvas()->UndoDeviceScaleFactor(); + + gfx::RectF masking_bound(layer()->bounds()); + masking_bound.Inset(mask_insets_); + + const gfx::Rect masking_bound_scaled = + gfx::ScaleToRoundedRect(gfx::ToNearestRect(masking_bound), dsf); + recorder.canvas()->DrawRoundRect(masking_bound_scaled, corner_radius_ * dsf, + flags); } // CircleInkDropMask diff --git a/chromium/ui/views/animation/ink_drop_ripple.cc b/chromium/ui/views/animation/ink_drop_ripple.cc index 22306a933a9..f6360a80947 100644 --- a/chromium/ui/views/animation/ink_drop_ripple.cc +++ b/chromium/ui/views/animation/ink_drop_ripple.cc @@ -38,7 +38,8 @@ void InkDropRipple::AnimateToState(InkDropState ink_drop_state) { // 1. The attached observers must be notified of all animations started and // ended. // 2. Not all state transitions is are valid, especially no-op transitions, - // and these should be detected by DCHECKs in AnimateStateChange(). + // and these invalid transitions will be logged as warnings in + // AnimateStateChange(). // |animation_observer| will be deleted when AnimationEndedCallback() returns // true. diff --git a/chromium/ui/views/animation/ink_drop_util.cc b/chromium/ui/views/animation/ink_drop_util.cc new file mode 100644 index 00000000000..4344d479756 --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_util.cc @@ -0,0 +1,54 @@ +// 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/animation/ink_drop_util.h" + +#include <math.h> + +#include "base/logging.h" +#include "ui/gfx/geometry/point3_f.h" +#include "ui/gfx/geometry/safe_integer_conversions.h" +#include "ui/gfx/geometry/vector2d_f.h" +#include "ui/gfx/transform.h" + +namespace views { + +gfx::Transform GetTransformSubpixelCorrection(const gfx::Transform& transform, + float device_scale_factor) { + gfx::Point3F origin; + transform.TransformPoint(&origin); + + const gfx::Vector2dF offset_in_dip = origin.AsPointF().OffsetFromOrigin(); + + // Scale the origin to screen space + origin.Scale(device_scale_factor); + + // Compute the rounded offset in screen space and finally unscale it back to + // DIP space. + gfx::Vector2dF aligned_offset_in_dip = origin.AsPointF().OffsetFromOrigin(); + aligned_offset_in_dip.set_x(gfx::ToRoundedInt(aligned_offset_in_dip.x())); + aligned_offset_in_dip.set_y(gfx::ToRoundedInt(aligned_offset_in_dip.y())); + aligned_offset_in_dip.Scale(1.f / device_scale_factor); + + // Compute the subpixel offset correction and apply it to the transform. + gfx::Transform subpixel_correction; + subpixel_correction.Translate(aligned_offset_in_dip - offset_in_dip); +#if DCHECK_IS_ON() + const float kEpsilon = 0.0001f; + gfx::Point3F offset; + + gfx::Transform transform_corrected(transform); + transform_corrected.ConcatTransform(subpixel_correction); + transform_corrected.TransformPoint(&offset); + offset.Scale(device_scale_factor); + + if (!std::isnan(offset.x())) + DCHECK_LT(std::abs(gfx::ToRoundedInt(offset.x()) - offset.x()), kEpsilon); + if (!std::isnan(offset.y())) + DCHECK_LT(std::abs(gfx::ToRoundedInt(offset.y()) - offset.y()), kEpsilon); +#endif + return subpixel_correction; +} + +} // namespace views diff --git a/chromium/ui/views/animation/ink_drop_util.h b/chromium/ui/views/animation/ink_drop_util.h new file mode 100644 index 00000000000..883b78974ef --- /dev/null +++ b/chromium/ui/views/animation/ink_drop_util.h @@ -0,0 +1,27 @@ +// 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_VIEWS_ANIMATION_INK_DROP_UTIL_H_ +#define UI_VIEWS_ANIMATION_INK_DROP_UTIL_H_ + +#include "ui/views/views_export.h" + +namespace gfx { +class Transform; +} // namespace gfx + +namespace views { + +// A layer |transform| may add an offset to its layer relative to the parent +// layer. This offset does not take into consideration the subpixel positioning. +// A subpixel correction needs to be applied to make sure the layers are pixel +// aligned after the transform is applied. Use this function to compute the +// subpixel correction transform. +VIEWS_EXPORT gfx::Transform GetTransformSubpixelCorrection( + const gfx::Transform& transform, + float device_scale_factor); + +} // namespace views + +#endif // UI_VIEWS_ANIMATION_INK_DROP_UTIL_H_ diff --git a/chromium/ui/views/animation/square_ink_drop_ripple.cc b/chromium/ui/views/animation/square_ink_drop_ripple.cc index 7dd72be2dd4..276b5d5f732 100644 --- a/chromium/ui/views/animation/square_ink_drop_ripple.cc +++ b/chromium/ui/views/animation/square_ink_drop_ripple.cc @@ -238,8 +238,13 @@ void SquareInkDropRipple::AnimateStateChange( } break; case InkDropState::ACTION_PENDING: - DCHECK_EQ(InkDropState::HIDDEN, old_ink_drop_state) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + if (old_ink_drop_state == new_ink_drop_state) + return; + DLOG_IF(WARNING, InkDropState::HIDDEN != old_ink_drop_state) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); + AnimateToOpacity(visible_opacity_, GetAnimationDuration(ACTION_PENDING_FADE_IN), ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, @@ -255,9 +260,12 @@ void SquareInkDropRipple::AnimateStateChange( gfx::Tween::EASE_IN_OUT, animation_observer); break; case InkDropState::ACTION_TRIGGERED: { - DCHECK(old_ink_drop_state == InkDropState::HIDDEN || - old_ink_drop_state == InkDropState::ACTION_PENDING) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + DLOG_IF(WARNING, old_ink_drop_state != InkDropState::HIDDEN && + old_ink_drop_state != InkDropState::ACTION_PENDING) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); + if (old_ink_drop_state == InkDropState::HIDDEN) { AnimateStateChange(old_ink_drop_state, InkDropState::ACTION_PENDING, animation_observer); @@ -275,8 +283,11 @@ void SquareInkDropRipple::AnimateStateChange( break; } case InkDropState::ALTERNATE_ACTION_PENDING: - DCHECK_EQ(InkDropState::ACTION_PENDING, old_ink_drop_state) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + DLOG_IF(WARNING, InkDropState::ACTION_PENDING != old_ink_drop_state) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); + AnimateToOpacity(visible_opacity_, GetAnimationDuration(ALTERNATE_ACTION_PENDING), ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET, @@ -288,8 +299,12 @@ void SquareInkDropRipple::AnimateStateChange( gfx::Tween::EASE_IN_OUT, animation_observer); break; case InkDropState::ALTERNATE_ACTION_TRIGGERED: { - DCHECK_EQ(InkDropState::ALTERNATE_ACTION_PENDING, old_ink_drop_state) - << " old_ink_drop_state=" << ToString(old_ink_drop_state); + DLOG_IF(WARNING, + InkDropState::ALTERNATE_ACTION_PENDING != old_ink_drop_state) + << "Invalid InkDropState transition. old_ink_drop_state=" + << ToString(old_ink_drop_state) + << " new_ink_drop_state=" << ToString(new_ink_drop_state); + base::TimeDelta visible_duration = GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_TRANSFORM) - GetAnimationDuration(ALTERNATE_ACTION_TRIGGERED_FADE_OUT); diff --git a/chromium/ui/views/bubble/bubble_border_unittest.cc b/chromium/ui/views/bubble/bubble_border_unittest.cc index 3c6245e14b0..0d2a89cc8fe 100644 --- a/chromium/ui/views/bubble/bubble_border_unittest.cc +++ b/chromium/ui/views/bubble/bubble_border_unittest.cc @@ -10,11 +10,19 @@ #include "base/macros.h" #include "base/strings/stringprintf.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/rect.h" #include "ui/views/test/views_test_base.h" namespace views { +namespace { + +bool UseMd() { + return ui::MaterialDesignController::IsSecondaryUiMaterial(); +} + +} // namespace typedef views::ViewsTestBase BubbleBorderTest; @@ -216,6 +224,7 @@ TEST_F(BubbleBorderTest, GetSizeForContentsSizeTest) { SK_ColorWHITE); const views::internal::BorderImages* kImages = border.GetImagesForTest(); + const gfx::Insets kInsets = border.GetInsets(); // kSmallSize is smaller than the minimum allowable size and does not // contribute to the resulting size. @@ -225,29 +234,39 @@ TEST_F(BubbleBorderTest, GetSizeForContentsSizeTest) { const gfx::Size kMediumSize = gfx::Size(50, 60); const gfx::Size kSmallHorizArrow( - 2 * kImages->border_thickness + kImages->arrow_width, - kImages->border_thickness + kImages->arrow_thickness + - kImages->border_interior_thickness); + UseMd() ? kSmallSize.width() + kInsets.width() + : 2 * kImages->border_thickness + kImages->arrow_width, + UseMd() ? kSmallSize.height() + kInsets.height() + : kImages->border_thickness + kImages->arrow_thickness + + kImages->border_interior_thickness); - const gfx::Size kSmallVertArrow(kSmallHorizArrow.height(), - kSmallHorizArrow.width()); + const gfx::Size kSmallVertArrow( + UseMd() ? kSmallHorizArrow.width() : kSmallHorizArrow.height(), + UseMd() ? kSmallHorizArrow.height() : kSmallHorizArrow.width()); - const gfx::Size kSmallNoArrow(2 * kImages->border_thickness, - 2 * kImages->border_thickness); + const gfx::Size kSmallNoArrow( + UseMd() ? kSmallHorizArrow.width() : 2 * kImages->border_thickness, + UseMd() ? kSmallHorizArrow.height() : 2 * kImages->border_thickness); const gfx::Size kMediumHorizArrow( - kMediumSize.width() + 2 * border.GetBorderThickness(), - kMediumSize.height() + border.GetBorderThickness() + - kImages->arrow_thickness); + UseMd() ? kMediumSize.width() + kInsets.width() + : kMediumSize.width() + 2 * border.GetBorderThickness(), + UseMd() ? kMediumSize.height() + kInsets.height() + : kMediumSize.height() + border.GetBorderThickness() + + kImages->arrow_thickness); const gfx::Size kMediumVertArrow( - kMediumSize.width() + border.GetBorderThickness() + - kImages->arrow_thickness, - kMediumSize.height() + 2 * border.GetBorderThickness()); + UseMd() ? kMediumHorizArrow.width() + : kMediumSize.width() + border.GetBorderThickness() + + kImages->arrow_thickness, + UseMd() ? kMediumHorizArrow.height() + : kMediumSize.height() + 2 * border.GetBorderThickness()); const gfx::Size kMediumNoArrow( - kMediumSize.width() + 2 * border.GetBorderThickness(), - kMediumSize.height() + 2 * border.GetBorderThickness()); + UseMd() ? kMediumHorizArrow.width() + : kMediumSize.width() + 2 * border.GetBorderThickness(), + UseMd() ? kMediumHorizArrow.height() + : kMediumSize.height() + 2 * border.GetBorderThickness()); struct TestCase { BubbleBorder::Arrow arrow; @@ -334,6 +353,7 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { const gfx::Rect kAnchor(100, 100, 20, 30); const gfx::Size kContentSize(500, 600); + const gfx::Insets kInsets = border.GetInsets(); const views::internal::BorderImages* kImages = border.GetImagesForTest(); @@ -354,14 +374,14 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { const int kArrowOffsetForHorizCenter = kTotalSizeWithHorizArrow.width() / 2; const int kArrowOffsetForVertCenter = kTotalSizeWithVertArrow.height() / 2; const int kArrowOffsetForNotCenter = - kImages->border_thickness + (kImages->arrow_width / 2); + UseMd() ? 0 : kImages->border_thickness + (kImages->arrow_width / 2); const int kStrokeWidth = shadow == BubbleBorder::NO_ASSETS ? 0 : BubbleBorder::kStroke; - const int kArrowThickness = kImages->arrow_interior_thickness; + const int kArrowThickness = UseMd() ? 0 : kImages->arrow_interior_thickness; const int kArrowShift = - kArrowThickness + kStrokeWidth - kImages->arrow_thickness; + UseMd() ? 0 : kArrowThickness + kStrokeWidth - kImages->arrow_thickness; const int kHeightDifference = kTotalSizeWithHorizArrow.height() - kTotalSizeWithNoArrow.height(); const int kWidthDifference = @@ -371,19 +391,26 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { // The arrow only makes a difference in height if it is longer than the // shadow. const int kExpectedHeightDifference = - std::max(kImages->arrow_thickness + kImages->border_interior_thickness, - kImages->border_thickness) - - std::max(kImages->border_interior_thickness, kImages->border_thickness); + UseMd() ? 0 + : std::max(kImages->arrow_thickness + + kImages->border_interior_thickness, + kImages->border_thickness) - + std::max(kImages->border_interior_thickness, + kImages->border_thickness); EXPECT_EQ(kExpectedHeightDifference, kHeightDifference) << "Size with arrow: " << kTotalSizeWithHorizArrow.ToString() << " vs. size without arrow: " << kTotalSizeWithNoArrow.ToString(); - const int kTopHorizArrowY = kAnchor.y() + kAnchor.height() + kArrowShift; + const int kTopHorizArrowY = + UseMd() ? kAnchor.bottom() + kStrokeWidth - kInsets.top() + : kAnchor.bottom() + kArrowShift; const int kBottomHorizArrowY = - kAnchor.y() - kArrowShift - kTotalSizeWithHorizArrow.height(); + UseMd() ? kAnchor.y() - kTotalSizeWithHorizArrow.height() + : kAnchor.y() - kArrowShift - kTotalSizeWithHorizArrow.height(); const int kLeftVertArrowX = kAnchor.x() + kAnchor.width() + kArrowShift; const int kRightVertArrowX = - kAnchor.x() - kArrowShift - kTotalSizeWithVertArrow.width(); + UseMd() ? kAnchor.x() - kTotalSizeWithHorizArrow.width() + : kAnchor.x() - kArrowShift - kTotalSizeWithVertArrow.width(); struct TestCase { BubbleBorder::Arrow arrow; @@ -395,15 +422,21 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { TestCase cases[] = { // Horizontal arrow tests. {BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, - kAnchor.CenterPoint().x() - kArrowOffsetForNotCenter, kTopHorizArrowY}, + UseMd() ? kAnchor.x() + kStrokeWidth - kInsets.left() + : kAnchor.CenterPoint().x() - kArrowOffsetForNotCenter, + kTopHorizArrowY}, {BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE, - kAnchor.x() + kStrokeWidth - kBorderThickness, kTopHorizArrowY}, - {BubbleBorder::TOP_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, - kAnchor.CenterPoint().x() - kArrowOffsetForHorizCenter, + UseMd() ? kAnchor.x() + kStrokeWidth - kInsets.left() + : 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)}, {BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, - kAnchor.CenterPoint().x() + kArrowOffsetForNotCenter - - kTotalSizeWithHorizArrow.width(), + UseMd() ? kAnchor.CenterPoint().x() - kTotalSizeWithHorizArrow.width() + : kAnchor.CenterPoint().x() + kArrowOffsetForNotCenter - + kTotalSizeWithHorizArrow.width(), kBottomHorizArrowY}, {BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE, kAnchor.x() + kAnchor.width() - kTotalSizeWithHorizArrow.width() + @@ -416,8 +449,9 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { {BubbleBorder::LEFT_TOP, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE, kLeftVertArrowX, kAnchor.y() + kStrokeWidth - kBorderThickness}, {BubbleBorder::LEFT_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, - kLeftVertArrowX, - kAnchor.CenterPoint().y() - kArrowOffsetForVertCenter}, + kLeftVertArrowX - (UseMd() ? kInsets.right() - kStrokeWidth : 0), + kAnchor.CenterPoint().y() - kArrowOffsetForVertCenter + + (UseMd() ? 2 * kStrokeWidth : 0)}, {BubbleBorder::RIGHT_BOTTOM, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR, kRightVertArrowX, kAnchor.CenterPoint().y() + kArrowOffsetForNotCenter - @@ -485,6 +519,8 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { // Ensure all the shadow types pass some size validation and paint sanely. TEST_F(BubbleBorderTest, ShadowTypes) { + if (UseMd()) + return; // This test doesn't mean anything in MD mode. const gfx::Rect rect(0, 0, 320, 200); View paint_view; paint_view.SetBoundsRect(rect); diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.cc b/chromium/ui/views/bubble/bubble_dialog_delegate.cc index b335b5bfc5d..405ee89345e 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.cc @@ -98,18 +98,14 @@ bool BubbleDialogDelegateView::ShouldShowCloseButton() const { ClientView* BubbleDialogDelegateView::CreateClientView(Widget* widget) { DialogClientView* client = new DialogClientView(widget, GetContentsView()); - LayoutProvider* provider = LayoutProvider::Get(); - // The other three sides are taken care of by the |margins_| given to - // BubbleFrameView in CreateNonClientFrameView(). - client->SetButtonRowInsets(gfx::Insets( - provider->GetDistanceMetric(DISTANCE_BUBBLE_BUTTON_TOP_MARGIN), 0, 0, 0)); widget->non_client_view()->set_mirror_client_in_rtl(mirror_arrow_in_rtl_); return client; } NonClientFrameView* BubbleDialogDelegateView::CreateNonClientFrameView( Widget* widget) { - BubbleFrameView* frame = new BubbleFrameView(title_margins_, margins_); + BubbleFrameView* frame = new BubbleFrameView(title_margins_, gfx::Insets()); + frame->set_footnote_margins(margins()); frame->SetFootnoteView(CreateFootnoteView()); BubbleBorder::Arrow adjusted_arrow = arrow(); @@ -178,7 +174,7 @@ void BubbleDialogDelegateView::OnBeforeBubbleWidgetInit( void BubbleDialogDelegateView::UseCompactMargins() { const int kCompactMargin = 6; - margins_.Set(kCompactMargin, kCompactMargin, kCompactMargin, kCompactMargin); + set_margins(gfx::Insets(kCompactMargin)); } void BubbleDialogDelegateView::SetAlignment( @@ -218,8 +214,8 @@ BubbleDialogDelegateView::BubbleDialogDelegateView(View* anchor_view, adjust_if_offscreen_(true), parent_window_(nullptr) { LayoutProvider* provider = LayoutProvider::Get(); - margins_ = provider->GetInsetsMetric(INSETS_BUBBLE_CONTENTS); - title_margins_ = provider->GetInsetsMetric(INSETS_BUBBLE_TITLE); + set_margins(provider->GetInsetsMetric(INSETS_DIALOG_CONTENTS)); + title_margins_ = provider->GetInsetsMetric(INSETS_DIALOG_TITLE); if (anchor_view) SetAnchorView(anchor_view); UpdateColorsFromTheme(GetNativeTheme()); diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate.h b/chromium/ui/views/bubble/bubble_dialog_delegate.h index c3fc547307f..07172f1c020 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate.h +++ b/chromium/ui/views/bubble/bubble_dialog_delegate.h @@ -41,14 +41,14 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, // Create and initialize the bubble Widget(s) with proper bounds. static Widget* CreateBubble(BubbleDialogDelegateView* bubble_delegate); - // WidgetDelegateView overrides: + // WidgetDelegateView: BubbleDialogDelegateView* AsBubbleDialogDelegate() override; bool ShouldShowCloseButton() const override; ClientView* CreateClientView(Widget* widget) override; NonClientFrameView* CreateNonClientFrameView(Widget* widget) override; const char* GetClassName() const override; - // WidgetObserver overrides: + // WidgetObserver: void OnWidgetDestroying(Widget* widget) override; void OnWidgetVisibilityChanging(Widget* widget, bool visible) override; void OnWidgetVisibilityChanged(Widget* widget, bool visible) override; @@ -79,8 +79,6 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, color_explicitly_set_ = true; } - const gfx::Insets& margins() const { return margins_; } - void set_margins(const gfx::Insets& margins) { margins_ = margins; } void set_title_margins(const gfx::Insets& title_margins) { title_margins_ = title_margins; } @@ -104,7 +102,7 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, virtual void OnBeforeBubbleWidgetInit(Widget::InitParams* params, Widget* widget) const; - // Sets |margins_| to a default picked for smaller bubbles. + // Sets the content margins to a default picked for smaller bubbles. void UseCompactMargins(); // Sets the bubble alignment relative to the anchor. This may only be called @@ -185,9 +183,6 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, SkColor color_; bool color_explicitly_set_; - // The margins between the content and the inside of the border. - gfx::Insets margins_; - // The margins around the title. // TODO(tapted): Investigate deleting this when MD is default. gfx::Insets title_margins_; diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc index a6c2b175532..f556302ca17 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_unittest.cc @@ -19,18 +19,19 @@ #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" +#include "ui/views/window/dialog_client_view.h" namespace views { namespace { +constexpr int kContentHeight = 200; +constexpr int kContentWidth = 200; + class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { public: TestBubbleDialogDelegateView(View* anchor_view) - : BubbleDialogDelegateView(anchor_view, BubbleBorder::TOP_LEFT), - view_(new View()), - title_view_(nullptr), - should_show_close_button_(false) { + : BubbleDialogDelegateView(anchor_view, BubbleBorder::TOP_LEFT) { view_->SetFocusBehavior(FocusBehavior::ALWAYS); AddChildView(view_); } @@ -39,7 +40,7 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { // BubbleDialogDelegateView overrides: View* GetInitiallyFocusedView() override { return view_; } gfx::Size CalculatePreferredSize() const override { - return gfx::Size(200, 200); + return gfx::Size(kContentWidth, kContentHeight); } void AddedToWidget() override { if (title_view_) @@ -50,21 +51,36 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { return base::ASCIIToUTF16("TITLE TITLE TITLE"); } + bool ShouldShowWindowTitle() const override { + return should_show_window_title_; + } + bool ShouldShowCloseButton() const override { return should_show_close_button_; } + int GetDialogButtons() const override { return buttons_; } + void set_title_view(View* title_view) { title_view_.reset(title_view); } void show_close_button() { should_show_close_button_ = true; } + void hide_buttons() { + should_show_close_button_ = false; + buttons_ = ui::DIALOG_BUTTON_NONE; + } + void set_should_show_window_title(bool should_show_window_title) { + should_show_window_title_ = should_show_window_title; + } using BubbleDialogDelegateView::SetAnchorRect; using BubbleDialogDelegateView::GetBubbleFrameView; using BubbleDialogDelegateView::SizeToContents; private: - View* view_; + View* view_ = new View; std::unique_ptr<View> title_view_; - bool should_show_close_button_; + int buttons_ = ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL; + bool should_show_close_button_ = false; + bool should_show_window_title_ = true; DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView); }; @@ -360,8 +376,8 @@ TEST_F(BubbleDialogDelegateTest, CustomTitle) { std::unique_ptr<Widget> anchor_widget(CreateTestWidget()); TestBubbleDialogDelegateView* bubble_delegate = new TestBubbleDialogDelegateView(anchor_widget->GetContentsView()); - constexpr int kTitlePreferredHeight = 20; - View* title_view = new StaticSizedView(gfx::Size(10, kTitlePreferredHeight)); + constexpr int kTitleHeight = 20; + View* title_view = new StaticSizedView(gfx::Size(10, kTitleHeight)); bubble_delegate->set_title_view(title_view); Widget* bubble_widget = BubbleDialogDelegateView::CreateBubble(bubble_delegate); @@ -373,7 +389,7 @@ TEST_F(BubbleDialogDelegateTest, CustomTitle) { EXPECT_EQ(bubble_frame, title_view->parent()); // Title takes up the whole bubble width when there's no icon or close button. EXPECT_EQ(bubble_delegate->width(), title_view->size().width()); - EXPECT_EQ(kTitlePreferredHeight, title_view->size().height()); + EXPECT_EQ(kTitleHeight, title_view->size().height()); bubble_delegate->show_close_button(); bubble_frame->ResetWindowControls(); @@ -384,6 +400,50 @@ TEST_F(BubbleDialogDelegateTest, CustomTitle) { EXPECT_EQ(close_button->x() - LayoutProvider::Get()->GetDistanceMetric( DISTANCE_CLOSE_BUTTON_MARGIN), title_view->bounds().right()); + + LayoutProvider* provider = LayoutProvider::Get(); + const gfx::Insets content_margins = + provider->GetInsetsMetric(INSETS_DIALOG_CONTENTS); + const gfx::Insets title_margins = + provider->GetInsetsMetric(INSETS_DIALOG_TITLE); + EXPECT_EQ(content_margins, bubble_delegate->margins()); + // Note there is no title_margins() accessor (it should not be customizable). + + // To perform checks on the precise size, first hide the dialog buttons so the + // calculations are simpler (e.g. platform font discrepancies can be ignored). + DialogClientView* client_view = + static_cast<DialogClientView*>(bubble_widget->client_view()); + bubble_delegate->hide_buttons(); + bubble_frame->ResetWindowControls(); + client_view->UpdateDialogButtons(); + bubble_delegate->SizeToContents(); + + // Use GetContentsBounds() to exclude the bubble border, which can change per + // platform. + gfx::Rect frame_size = bubble_frame->GetContentsBounds(); + EXPECT_EQ(content_margins.height() + kContentHeight + title_margins.height() + + kTitleHeight, + frame_size.height()); + EXPECT_EQ(content_margins.width() + kContentWidth, frame_size.width()); + + // Set the title preferred size to 0. The bubble frame makes fewer assumptions + // about custom title views, so there should still be margins for it while the + // WidgetDelegate says it should be shown, even if its preferred size is zero. + title_view->SetPreferredSize(gfx::Size()); + bubble_widget->UpdateWindowTitle(); + bubble_delegate->SizeToContents(); + frame_size = bubble_frame->GetContentsBounds(); + EXPECT_EQ(content_margins.height() + kContentHeight + title_margins.height(), + frame_size.height()); + EXPECT_EQ(content_margins.width() + kContentWidth, frame_size.width()); + + // Now hide the title properly. The margins should also disappear. + bubble_delegate->set_should_show_window_title(false); + bubble_widget->UpdateWindowTitle(); + bubble_delegate->SizeToContents(); + frame_size = bubble_frame->GetContentsBounds(); + EXPECT_EQ(content_margins.height() + kContentHeight, frame_size.height()); + EXPECT_EQ(content_margins.width() + kContentWidth, frame_size.width()); } // Ensure the BubbleFrameView correctly resizes when the title is provided by a diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc index a04b0982ebc..302bce50f87 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.cc +++ b/chromium/ui/views/bubble/bubble_frame_view.cc @@ -14,7 +14,6 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/compositor/paint_context.h" #include "ui/compositor/paint_recorder.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -31,6 +30,7 @@ #include "ui/views/controls/image_view.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_provider.h" +#include "ui/views/paint_info.h" #include "ui/views/resources/grit/views_resources.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -72,6 +72,21 @@ int GetOffScreenLength(const gfx::Rect& available_bounds, } // namespace +// A container that changes visibility with its contents. +class FootnoteContainerView : public View { + public: + FootnoteContainerView() {} + + // View: + void ChildVisibilityChanged(View* child) override { + DCHECK_EQ(child_count(), 1); + SetVisible(child->visible()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FootnoteContainerView); +}; + // static const char BubbleFrameView::kViewClassName[] = "BubbleFrameView"; @@ -80,6 +95,7 @@ BubbleFrameView::BubbleFrameView(const gfx::Insets& title_margins, : bubble_border_(nullptr), title_margins_(title_margins), content_margins_(content_margins), + footnote_margins_(content_margins_), title_icon_(new views::ImageView()), default_title_(CreateDefaultTitleLabel(base::string16()).release()), custom_title_(nullptr), @@ -122,13 +138,13 @@ Button* BubbleFrameView::CreateCloseButton(ButtonListener* listener) { } else { ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); close_button = new ImageButton(listener); - close_button->SetImage(CustomButton::STATE_NORMAL, + close_button->SetImage(Button::STATE_NORMAL, *rb->GetImageNamed(IDR_CLOSE_DIALOG).ToImageSkia()); close_button->SetImage( - CustomButton::STATE_HOVERED, + Button::STATE_HOVERED, *rb->GetImageNamed(IDR_CLOSE_DIALOG_H).ToImageSkia()); close_button->SetImage( - CustomButton::STATE_PRESSED, + Button::STATE_PRESSED, *rb->GetImageNamed(IDR_CLOSE_DIALOG_P).ToImageSkia()); } close_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE)); @@ -147,7 +163,9 @@ gfx::Rect BubbleFrameView::GetBoundsForClientView() const { // (i.e. |this|), but not the client view bounds. gfx::Rect client_bounds = GetContentsBounds(); client_bounds.Inset(GetClientInsetsForFrameWidth(client_bounds.width())); - if (footnote_container_) { + // Only account for footnote_container_'s height if it's visible, because + // content_margins_ adds extra padding even if all child views are invisible. + if (footnote_container_ && footnote_container_->visible()) { client_bounds.set_height(client_bounds.height() - footnote_container_->height()); } @@ -364,12 +382,14 @@ void BubbleFrameView::Layout() { std::max(title_icon_pref_size.height(), title_preferred_height); title()->SetBounds(title_label_x, bounds.y() + (title_height - title_preferred_height) / 2, - title_available_width, title_height); + title_available_width, title_preferred_height); title_icon_->SetBounds(bounds.x(), bounds.y(), title_icon_pref_size.width(), title_height); - if (footnote_container_) { + // Only account for footnote_container_'s height if it's visible, because + // content_margins_ adds extra padding even if all child views are invisible. + if (footnote_container_ && footnote_container_->visible()) { const int width = contents_bounds.width(); const int height = footnote_container_->GetHeightForWidth(width); footnote_container_->SetBounds( @@ -409,11 +429,14 @@ void BubbleFrameView::OnPaint(gfx::Canvas* canvas) { // Border comes after children. } -void BubbleFrameView::PaintChildren(const ui::PaintContext& context) { - NonClientFrameView::PaintChildren(context); +void BubbleFrameView::PaintChildren(const PaintInfo& paint_info) { + NonClientFrameView::PaintChildren(paint_info); ui::PaintCache paint_cache; - ui::PaintRecorder recorder(context, size(), &paint_cache); + ui::PaintRecorder recorder( + paint_info.context(), paint_info.paint_recording_size(), + paint_info.paint_recording_scale_x(), + paint_info.paint_recording_scale_y(), &paint_cache); OnPaintBorder(recorder.canvas()); } @@ -437,14 +460,15 @@ void BubbleFrameView::SetFootnoteView(View* view) { return; DCHECK(!footnote_container_); - footnote_container_ = new views::View(); + footnote_container_ = new FootnoteContainerView(); footnote_container_->SetLayoutManager( - new BoxLayout(BoxLayout::kVertical, content_margins_, 0)); + new BoxLayout(BoxLayout::kVertical, footnote_margins_, 0)); footnote_container_->SetBackground( CreateSolidBackground(kFootnoteBackgroundColor)); footnote_container_->SetBorder( CreateSolidSidedBorder(1, 0, 0, 0, kFootnoteBorderColor)); footnote_container_->AddChildView(view); + footnote_container_->SetVisible(view->visible()); AddChildView(footnote_container_); } @@ -573,15 +597,19 @@ gfx::Size BubbleFrameView::GetFrameSizeForClientSize( DCHECK_GE(frame_width, client_size.width()); gfx::Size size(frame_width, client_size.height() + client_insets.height()); - if (footnote_container_) + // Only account for footnote_container_'s height if it's visible, because + // content_margins_ adds extra padding even if all child views are invisible. + if (footnote_container_ && footnote_container_->visible()) size.Enlarge(0, footnote_container_->GetHeightForWidth(size.width())); return size; } bool BubbleFrameView::HasTitle() const { - return custom_title_ != nullptr || - default_title_->GetPreferredSize().height() > 0 || + return (custom_title_ != nullptr && + GetWidget()->widget_delegate()->ShouldShowWindowTitle()) || + (default_title_ != nullptr && + default_title_->GetPreferredSize().height() > 0) || title_icon_->GetPreferredSize().height() > 0; } diff --git a/chromium/ui/views/bubble/bubble_frame_view.h b/chromium/ui/views/bubble/bubble_frame_view.h index 1b72b8d80e4..1369874c91c 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.h +++ b/chromium/ui/views/bubble/bubble_frame_view.h @@ -60,7 +60,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, gfx::Size GetMaximumSize() const override; void Layout() override; void OnPaint(gfx::Canvas* canvas) override; - void PaintChildren(const ui::PaintContext& context) override; + void PaintChildren(const PaintInfo& paint_info) override; void OnThemeChanged() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; void ViewHierarchyChanged( @@ -84,6 +84,9 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, gfx::Insets content_margins() const { return content_margins_; } void SetFootnoteView(View* view); + void set_footnote_margins(const gfx::Insets& footnote_margins) { + footnote_margins_ = footnote_margins; + } // Given the size of the contents and the rect to point at, returns the bounds // of the bubble window. The bubble's arrow location may change if the bubble @@ -106,6 +109,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, private: FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, GetBoundsForClientView); FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, RemoveFootnoteView); + FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithIcon); FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CloseReasons); FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateTest, CloseMethods); @@ -150,6 +154,9 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, // Margins between the content and the inside of the border, in pixels. gfx::Insets content_margins_; + // Margins between the footnote view and the footnote container. + gfx::Insets footnote_margins_; + // The optional title icon. views::ImageView* title_icon_; diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc index c2ebde257a0..583c4a8082f 100644 --- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc +++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc @@ -7,7 +7,9 @@ #include <memory> #include "base/macros.h" +#include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" @@ -26,6 +28,10 @@ typedef ViewsTestBase BubbleFrameViewTest; namespace { +bool UseMd() { + return ui::MaterialDesignController::IsSecondaryUiMaterial(); +} + const BubbleBorder::Arrow kArrow = BubbleBorder::TOP_LEFT; const SkColor kColor = SK_ColorRED; const int kMargin = 6; @@ -168,6 +174,20 @@ TEST_F(BubbleFrameViewTest, GetBoundsForClientViewWithClose) { frame.GetBoundsForClientView().y()); } +TEST_F(BubbleFrameViewTest, + FootnoteContainerViewShouldMatchVisibilityOfFirstChild) { + TestBubbleFrameView frame(this); + View* footnote_dummy_view = new StaticSizedView(gfx::Size(200, 200)); + footnote_dummy_view->SetVisible(false); + frame.SetFootnoteView(footnote_dummy_view); + View* footnote_container_view = footnote_dummy_view->parent(); + EXPECT_FALSE(footnote_container_view->visible()); + footnote_dummy_view->SetVisible(true); + EXPECT_TRUE(footnote_container_view->visible()); + footnote_dummy_view->SetVisible(false); + EXPECT_FALSE(footnote_container_view->visible()); +} + // Tests that the arrow is mirrored as needed to better fit the screen. TEST_F(BubbleFrameViewTest, GetUpdatedWindowBounds) { TestBubbleFrameView frame(this); @@ -345,15 +365,25 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsDontTryMirror) { TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) { TestBubbleFrameView frame(this); gfx::Rect window_bounds; + // Bubbles have a thicker shadow on the bottom in MD. + // Match definition of kLargeShadowVerticalOffset in bubble_border.cc. + const int kLargeShadowVerticalOffset = UseMd() ? 2 : 0; + + // Some of these tests may go away once --secondary-ui-md becomes the + // default. Under Material Design mode, the BubbleBorder doesn't support all + // "arrow" positions. If this changes, then the tests should be updated or + // added for MD mode. // Test that the bubble displays normally when it fits. - frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); - window_bounds = frame.GetUpdatedWindowBounds( - gfx::Rect(500, 100, 50, 50), // |anchor_rect| - gfx::Size(500, 500), // |client_size| - true); // |adjust_if_offscreen| - EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525); + if (!UseMd()) { // TOP_CENTER isn't supported by the bubble_border() in MD. + frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); + window_bounds = frame.GetUpdatedWindowBounds( + gfx::Rect(500, 100, 50, 50), // |anchor_rect| + gfx::Size(500, 500), // |client_size| + true); // |adjust_if_offscreen| + EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); + EXPECT_EQ(window_bounds.x() + window_bounds.width() / 2, 525); + } frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER); window_bounds = frame.GetUpdatedWindowBounds( @@ -369,7 +399,8 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) { gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425); + EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, + 425 + kLargeShadowVerticalOffset); frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); window_bounds = frame.GetUpdatedWindowBounds( @@ -377,18 +408,22 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) { gfx::Size(500, 500), // |client_size| true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, 425); + EXPECT_EQ(window_bounds.y() + window_bounds.height() / 2, + 425 + kLargeShadowVerticalOffset); // Test bubble not fitting left screen edge. - frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); - window_bounds = frame.GetUpdatedWindowBounds( - gfx::Rect(100, 100, 50, 50), // |anchor_rect| - gfx::Size(500, 500), // |client_size| - true); // |adjust_if_offscreen| - EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.x(), 0); - EXPECT_EQ(window_bounds.x() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); + if (!UseMd()) { // TOP_CENTER isn't supported by the bubble_border() in MD. + frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); + window_bounds = frame.GetUpdatedWindowBounds( + gfx::Rect(100, 100, 50, 50), // |anchor_rect| + gfx::Size(500, 500), // |client_size| + true); // |adjust_if_offscreen| + EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); + EXPECT_EQ(window_bounds.x(), 0); + EXPECT_EQ(window_bounds.x() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 125); + } frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER); window_bounds = frame.GetUpdatedWindowBounds( @@ -397,19 +432,25 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) { true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.x(), 0); - EXPECT_EQ(window_bounds.x() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); + if (!UseMd()) { // There is no arrow offset in MD mode. + EXPECT_EQ(window_bounds.x() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 125); + } // Test bubble not fitting right screen edge. - frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); - window_bounds = frame.GetUpdatedWindowBounds( - gfx::Rect(900, 100, 50, 50), // |anchor_rect| - gfx::Size(500, 500), // |client_size| - true); // |adjust_if_offscreen| - EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.right(), 1000); - EXPECT_EQ(window_bounds.x() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); + if (!UseMd()) { // TOP_CENTER isn't supported by the bubble_border() in MD. + frame.bubble_border()->set_arrow(BubbleBorder::TOP_CENTER); + window_bounds = frame.GetUpdatedWindowBounds( + gfx::Rect(900, 100, 50, 50), // |anchor_rect| + gfx::Size(500, 500), // |client_size| + true); // |adjust_if_offscreen| + EXPECT_EQ(BubbleBorder::TOP_CENTER, frame.bubble_border()->arrow()); + EXPECT_EQ(window_bounds.right(), 1000); + EXPECT_EQ(window_bounds.x() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 925); + } frame.bubble_border()->set_arrow(BubbleBorder::BOTTOM_CENTER); window_bounds = frame.GetUpdatedWindowBounds( @@ -418,53 +459,64 @@ TEST_F(BubbleFrameViewTest, GetUpdatedWindowBoundsCenterArrows) { true); // |adjust_if_offscreen| EXPECT_EQ(BubbleBorder::BOTTOM_CENTER, frame.bubble_border()->arrow()); EXPECT_EQ(window_bounds.right(), 1000); - EXPECT_EQ(window_bounds.x() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); + if (!UseMd()) { // There is no arrow offset in MD mode. + EXPECT_EQ(window_bounds.x() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 925); + } // Test bubble not fitting top screen edge. - frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); - window_bounds = frame.GetUpdatedWindowBounds( - gfx::Rect(100, 100, 50, 50), // |anchor_rect| - gfx::Size(500, 500), // |client_size| - true); // |adjust_if_offscreen| - EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.y(), 0); - EXPECT_EQ(window_bounds.y() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); - - frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); - window_bounds = frame.GetUpdatedWindowBounds( - gfx::Rect(900, 100, 50, 50), // |anchor_rect| - gfx::Size(500, 500), // |client_size| - true); // |adjust_if_offscreen| - EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.y(), 0); - EXPECT_EQ(window_bounds.y() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 125); - - // Test bubble not fitting bottom screen edge. - frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); - window_bounds = frame.GetUpdatedWindowBounds( - gfx::Rect(100, 900, 50, 50), // |anchor_rect| - gfx::Size(500, 500), // |client_size| - true); // |adjust_if_offscreen| - EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.bottom(), 1000); - EXPECT_EQ(window_bounds.y() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); - - frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); - window_bounds = frame.GetUpdatedWindowBounds( - gfx::Rect(900, 900, 50, 50), // |anchor_rect| - gfx::Size(500, 500), // |client_size| - true); // |adjust_if_offscreen| - EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); - EXPECT_EQ(window_bounds.bottom(), 1000); - EXPECT_EQ(window_bounds.y() + - frame.bubble_border()->GetArrowOffset(window_bounds.size()), 925); + if (!UseMd()) { // Moving the bubble by setting the arrow offset doesn't work + // in MD mode since there is no arrow displayed. + frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); + window_bounds = frame.GetUpdatedWindowBounds( + gfx::Rect(100, 100, 50, 50), // |anchor_rect| + gfx::Size(500, 500), // |client_size| + true); // |adjust_if_offscreen| + EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); + EXPECT_EQ(window_bounds.y(), 0); + EXPECT_EQ(window_bounds.y() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 125); + + frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); + window_bounds = frame.GetUpdatedWindowBounds( + gfx::Rect(900, 100, 50, 50), // |anchor_rect| + gfx::Size(500, 500), // |client_size| + true); // |adjust_if_offscreen| + EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); + EXPECT_EQ(window_bounds.y(), 0); + EXPECT_EQ(window_bounds.y() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 125); + + // Test bubble not fitting bottom screen edge. + frame.bubble_border()->set_arrow(BubbleBorder::LEFT_CENTER); + window_bounds = frame.GetUpdatedWindowBounds( + gfx::Rect(100, 900, 50, 50), // |anchor_rect| + gfx::Size(500, 500), // |client_size| + true); // |adjust_if_offscreen| + EXPECT_EQ(BubbleBorder::LEFT_CENTER, frame.bubble_border()->arrow()); + EXPECT_EQ(window_bounds.bottom(), 1000); + EXPECT_EQ(window_bounds.y() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 925); + + frame.bubble_border()->set_arrow(BubbleBorder::RIGHT_CENTER); + window_bounds = frame.GetUpdatedWindowBounds( + gfx::Rect(900, 900, 50, 50), // |anchor_rect| + gfx::Size(500, 500), // |client_size| + true); // |adjust_if_offscreen| + EXPECT_EQ(BubbleBorder::RIGHT_CENTER, frame.bubble_border()->arrow()); + EXPECT_EQ(window_bounds.bottom(), 1000); + EXPECT_EQ(window_bounds.y() + + frame.bubble_border()->GetArrowOffset(window_bounds.size()), + 925); + } } TEST_F(BubbleFrameViewTest, GetPreferredSize) { + // Test border/insets. TestBubbleFrameView frame(this); gfx::Rect preferred_rect(frame.GetPreferredSize()); // Expect that a border has been added to the preferred size. @@ -475,6 +527,29 @@ TEST_F(BubbleFrameViewTest, GetPreferredSize) { EXPECT_EQ(expected_size, preferred_rect.size()); } +TEST_F(BubbleFrameViewTest, GetPreferredSizeWithFootnote) { + // Test footnote view: adding a footnote should increase the preferred size, + // but only when the footnote is visible. + TestBubbleFrameView frame(this); + + constexpr int kFootnoteHeight = 20; + const gfx::Size no_footnote_size = frame.GetPreferredSize(); + View* footnote = new StaticSizedView(gfx::Size(10, kFootnoteHeight)); + footnote->SetVisible(false); + frame.SetFootnoteView(footnote); + EXPECT_EQ(no_footnote_size, frame.GetPreferredSize()); // No change. + + footnote->SetVisible(true); + gfx::Size with_footnote_size = no_footnote_size; + constexpr int kFootnoteTopBorderThickness = 1; + with_footnote_size.Enlarge(0, kFootnoteHeight + kFootnoteTopBorderThickness + + frame.content_margins().height()); + EXPECT_EQ(with_footnote_size, frame.GetPreferredSize()); + + footnote->SetVisible(false); + EXPECT_EQ(no_footnote_size, frame.GetPreferredSize()); +} + TEST_F(BubbleFrameViewTest, GetMinimumSize) { TestBubbleFrameView frame(this); gfx::Rect minimum_rect(frame.GetMinimumSize()); @@ -524,10 +599,13 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { } void set_override_snap(bool value) { override_snap_ = value; } + void set_icon(const gfx::ImageSkia& icon) { icon_ = icon; } // BubbleDialogDelegateView: using BubbleDialogDelegateView::SetAnchorView; using BubbleDialogDelegateView::SizeToContents; + gfx::ImageSkia GetWindowIcon() override { return icon_; } + bool ShouldShowWindowIcon() const override { return !icon_.isNull(); } base::string16 GetWindowTitle() const override { return title_; } bool ShouldShowWindowTitle() const override { return !title_.empty(); } @@ -545,7 +623,13 @@ class TestBubbleDialogDelegateView : public BubbleDialogDelegateView { return gfx::Size(200, 200); } + BubbleFrameView* GetBubbleFrameView() const { + return static_cast<BubbleFrameView*>( + GetWidget()->non_client_view()->frame_view()); + } + private: + gfx::ImageSkia icon_; base::string16 title_; base::Optional<bool> override_snap_; @@ -684,4 +768,29 @@ TEST_F(BubbleFrameViewTest, LayoutEdgeCases) { // When |anchor| goes out of scope it should take |bubble| with it. } +TEST_F(BubbleFrameViewTest, LayoutWithIcon) { + TestBubbleDialogDelegateView delegate; + TestAnchor anchor(CreateParams(Widget::InitParams::TYPE_WINDOW)); + delegate.SetAnchorView(anchor.widget().GetContentsView()); + SkBitmap bitmap; + bitmap.allocN32Pixels(20, 80); + delegate.set_icon(gfx::ImageSkia::CreateFrom1xBitmap(bitmap)); + + Widget* widget = BubbleDialogDelegateView::CreateBubble(&delegate); + widget->Show(); + + delegate.ChangeTitle(base::ASCIIToUTF16("test title")); + BubbleFrameView* frame = delegate.GetBubbleFrameView(); + View* icon = frame->title_icon_; + View* title = frame->title(); + + // There should be equal amounts of space on the left and right of the icon. + EXPECT_EQ(icon->x() * 2 + icon->width(), title->x()); + + // The title should be vertically centered relative to the icon. + EXPECT_LT(title->height(), icon->height()); + const int title_offset_y = (icon->height() - title->height()) / 2; + EXPECT_EQ(icon->y() + title_offset_y, title->y()); +} + } // namespace views diff --git a/chromium/ui/views/bubble/bubble_window_targeter.cc b/chromium/ui/views/bubble/bubble_window_targeter.cc deleted file mode 100644 index 8fa0da81c94..00000000000 --- a/chromium/ui/views/bubble/bubble_window_targeter.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/bubble/bubble_window_targeter.h" - -#include "ui/aura/window.h" -#include "ui/gfx/path.h" -#include "ui/gfx/skia_util.h" -#include "ui/views/bubble/bubble_dialog_delegate.h" -#include "ui/views/bubble/bubble_frame_view.h" - -namespace views { - -BubbleWindowTargeter::BubbleWindowTargeter(BubbleDialogDelegateView* bubble) - : wm::MaskedWindowTargeter(bubble->GetWidget()->GetNativeView()), - bubble_(bubble) {} - -BubbleWindowTargeter::~BubbleWindowTargeter() { -} - -bool BubbleWindowTargeter::GetHitTestMask(aura::Window* window, - gfx::Path* mask) const { - mask->addRect( - gfx::RectToSkRect(bubble_->GetBubbleFrameView()->GetContentsBounds())); - return true; -} - -} // namespace views diff --git a/chromium/ui/views/bubble/bubble_window_targeter.h b/chromium/ui/views/bubble/bubble_window_targeter.h deleted file mode 100644 index 7b7f87b5f76..00000000000 --- a/chromium/ui/views/bubble/bubble_window_targeter.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/macros.h" -#include "ui/views/views_export.h" -#include "ui/wm/core/masked_window_targeter.h" - -namespace aura { -class Window; -} - -namespace views { - -class BubbleDialogDelegateView; - -// A convenient window-targeter that uses a mask based on the content-bounds of -// the bubble-frame. -class VIEWS_EXPORT BubbleWindowTargeter - : public NON_EXPORTED_BASE(wm::MaskedWindowTargeter) { - public: - explicit BubbleWindowTargeter(BubbleDialogDelegateView* bubble); - ~BubbleWindowTargeter() override; - - private: - // wm::MaskedWindowTargeter: - bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const override; - - views::BubbleDialogDelegateView* bubble_; - - DISALLOW_COPY_AND_ASSIGN(BubbleWindowTargeter); -}; - -} // namespace views diff --git a/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc b/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc deleted file mode 100644 index 975d68e82db..00000000000 --- a/chromium/ui/views/bubble/bubble_window_targeter_unittest.cc +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/bubble/bubble_window_targeter.h" - -#include "base/macros.h" -#include "ui/aura/window.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/aura/window_tree_host.h" -#include "ui/events/event_utils.h" -#include "ui/views/bubble/bubble_border.h" -#include "ui/views/bubble/bubble_dialog_delegate.h" -#include "ui/views/test/views_test_base.h" -#include "ui/views/widget/widget.h" - -namespace views { - -namespace { - -class WidgetOwnsNativeBubble : public BubbleDialogDelegateView { - public: - WidgetOwnsNativeBubble(View* content, BubbleBorder::Arrow arrow) - : BubbleDialogDelegateView(content, arrow) {} - - ~WidgetOwnsNativeBubble() override {} - - private: - // BubbleDialogDelegateView: - void OnBeforeBubbleWidgetInit(Widget::InitParams* params, - Widget* widget) const override { - params->ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - } - - DISALLOW_COPY_AND_ASSIGN(WidgetOwnsNativeBubble); -}; - -} // namespace - -class BubbleWindowTargeterTest : public ViewsTestBase { - public: - BubbleWindowTargeterTest() - : bubble_delegate_(NULL) { - } - ~BubbleWindowTargeterTest() override {} - - void SetUp() override { - ViewsTestBase::SetUp(); - CreateAnchorWidget(); - CreateBubbleWidget(); - - anchor_widget()->Show(); - bubble_widget()->Show(); - } - - void TearDown() override { - bubble_delegate_ = NULL; - bubble_widget_.reset(); - anchor_.reset(); - ViewsTestBase::TearDown(); - } - - Widget* anchor_widget() { return anchor_.get(); } - Widget* bubble_widget() { return bubble_widget_.get(); } - BubbleDialogDelegateView* bubble_delegate() { return bubble_delegate_; } - - private: - void CreateAnchorWidget() { - anchor_.reset(new Widget()); - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - anchor_->Init(params); - } - - void CreateBubbleWidget() { - bubble_delegate_ = new WidgetOwnsNativeBubble( - anchor_->GetContentsView(), BubbleBorder::NONE); - bubble_delegate_->set_color(SK_ColorGREEN); - bubble_widget_.reset( - BubbleDialogDelegateView::CreateBubble(bubble_delegate_)); - } - - std::unique_ptr<Widget> anchor_; - std::unique_ptr<Widget> bubble_widget_; - BubbleDialogDelegateView* bubble_delegate_; - - DISALLOW_COPY_AND_ASSIGN(BubbleWindowTargeterTest); -}; - -TEST_F(BubbleWindowTargeterTest, HitTest) { - aura::Window* root = bubble_widget()->GetNativeWindow()->GetRootWindow(); - ui::EventTargeter* targeter = - root->GetHost()->dispatcher()->GetDefaultEventTargeter(); - aura::Window* bubble_window = bubble_widget()->GetNativeWindow(); - gfx::Rect bubble_bounds = bubble_window->GetBoundsInRootWindow(); - - { - bubble_delegate()->set_margins(gfx::Insets()); - ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), - bubble_bounds.origin(), ui::EventTimeForNow(), - ui::EF_NONE, ui::EF_NONE); - EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1)); - } - { - bubble_delegate()->set_margins(gfx::Insets(20)); - ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), - bubble_bounds.origin(), ui::EventTimeForNow(), - ui::EF_NONE, ui::EF_NONE); - EXPECT_EQ(bubble_window, targeter->FindTargetForEvent(root, &move1)); - } - - bubble_window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>( - new BubbleWindowTargeter(bubble_delegate()))); - { - bubble_delegate()->set_margins(gfx::Insets(20)); - ui::MouseEvent move1(ui::ET_MOUSE_MOVED, bubble_bounds.origin(), - bubble_bounds.origin(), ui::EventTimeForNow(), - ui::EF_NONE, ui::EF_NONE); - EXPECT_NE(bubble_window, targeter->FindTargetForEvent(root, &move1)); - } -} - -} // namespace views diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc index 165a07eb45d..305aaebb2a5 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.cc +++ b/chromium/ui/views/bubble/tray_bubble_view.cc @@ -16,8 +16,7 @@ #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/compositor/layer.h" -#include "ui/compositor/layer_delegate.h" -#include "ui/compositor/paint_recorder.h" +#include "ui/compositor/layer_owner.h" #include "ui/events/event.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" @@ -26,8 +25,8 @@ #include "ui/gfx/path.h" #include "ui/gfx/skia_util.h" #include "ui/views/bubble/bubble_frame_view.h" -#include "ui/views/bubble/bubble_window_targeter.h" #include "ui/views/layout/box_layout.h" +#include "ui/views/painter.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/widget.h" #include "ui/wm/core/shadow_types.h" @@ -83,54 +82,6 @@ bool MouseMoveDetectorHost::Contains(const gfx::Point& screen_point, return false; } -// This mask layer clips the bubble's content so that it does not overwrite the -// rounded bubble corners. -// TODO(miket): This does not work on Windows. Implement layer masking or -// alternate solutions if the TrayBubbleView is needed there in the future. -class TrayBubbleContentMask : public ui::LayerDelegate { - public: - explicit TrayBubbleContentMask(int corner_radius); - ~TrayBubbleContentMask() override; - - ui::Layer* layer() { return &layer_; } - - // Overridden from LayerDelegate. - void OnPaintLayer(const ui::PaintContext& context) override; - void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {} - void OnDeviceScaleFactorChanged(float device_scale_factor) override; - - private: - ui::Layer layer_; - int corner_radius_; - - DISALLOW_COPY_AND_ASSIGN(TrayBubbleContentMask); -}; - -TrayBubbleContentMask::TrayBubbleContentMask(int corner_radius) - : layer_(ui::LAYER_TEXTURED), - corner_radius_(corner_radius) { - layer_.set_delegate(this); - layer_.SetFillsBoundsOpaquely(false); -} - -TrayBubbleContentMask::~TrayBubbleContentMask() { - layer_.set_delegate(NULL); -} - -void TrayBubbleContentMask::OnPaintLayer(const ui::PaintContext& context) { - ui::PaintRecorder recorder(context, layer()->size()); - cc::PaintFlags flags; - flags.setAlpha(255); - flags.setStyle(cc::PaintFlags::kFill_Style); - gfx::Rect rect(layer()->bounds().size()); - recorder.canvas()->DrawRoundRect(rect, corner_radius_, flags); -} - -void TrayBubbleContentMask::OnDeviceScaleFactorChanged( - float device_scale_factor) { - // Redrawing will take care of scale factor change. -} - // Custom layout for the bubble-view. Does the default box-layout if there is // enough height. Otherwise, makes sure the bottom rows are visible. class BottomAlignedBoxLayout : public BoxLayout { @@ -168,7 +119,6 @@ class BottomAlignedBoxLayout : public BoxLayout { } // namespace internal -using internal::TrayBubbleContentMask; using internal::BottomAlignedBoxLayout; TrayBubbleView::Delegate::~Delegate() {} @@ -266,8 +216,9 @@ TrayBubbleView::TrayBubbleView(const InitParams& init_params) set_margins(gfx::Insets()); SetPaintToLayer(); - bubble_content_mask_.reset( - new TrayBubbleContentMask(bubble_border_->GetBorderCornerRadius())); + bubble_content_mask_ = views::Painter::CreatePaintedLayer( + views::Painter::CreateSolidRoundRectPainter( + SK_ColorBLACK, bubble_border_->GetBorderCornerRadius())); layout_->SetDefaultFlex(1); SetLayoutManager(layout_); @@ -291,8 +242,6 @@ void TrayBubbleView::InitializeAndShowBubble() { layer()->parent()->SetMaskLayer(bubble_content_mask_->layer()); GetWidget()->Show(); - GetWidget()->GetNativeWindow()->SetEventTargeter( - std::unique_ptr<ui::EventTargeter>(new BubbleWindowTargeter(this))); UpdateBubble(); ++g_current_tray_bubble_showing_count_; diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h index 51928ad75ce..aeff9a874a0 100644 --- a/chromium/ui/views/bubble/tray_bubble_view.h +++ b/chromium/ui/views/bubble/tray_bubble_view.h @@ -15,6 +15,10 @@ #include "ui/views/mouse_watcher.h" #include "ui/views/views_export.h" +namespace ui { +class LayerOwner; +} + namespace views { class BoxLayout; class View; @@ -23,10 +27,6 @@ class Widget; namespace views { -namespace internal { -class TrayBubbleContentMask; -} - // Specialized bubble view for bubbles associated with a tray icon (e.g. the // Ash status area). Mostly this handles custom anchor location and arrow and // border rendering. This also has its own delegate for handling mouse events @@ -202,7 +202,7 @@ class VIEWS_EXPORT TrayBubbleView : public BubbleDialogDelegateView, // the latter ensures we don't leak it before passing off ownership. BubbleBorder* bubble_border_; std::unique_ptr<views::BubbleBorder> owned_bubble_border_; - std::unique_ptr<internal::TrayBubbleContentMask> bubble_content_mask_; + std::unique_ptr<ui::LayerOwner> bubble_content_mask_; bool is_gesture_dragging_; // True once the mouse cursor was actively moved by the user over the bubble. diff --git a/chromium/ui/views/button_drag_utils.cc b/chromium/ui/views/button_drag_utils.cc index 064a133d269..c898239ead0 100644 --- a/chromium/ui/views/button_drag_utils.cc +++ b/chromium/ui/views/button_drag_utils.cc @@ -18,6 +18,7 @@ #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/label_button_border.h" #include "ui/views/drag_utils.h" +#include "ui/views/paint_info.h" #include "ui/views/widget/widget.h" #include "url/gurl.h" @@ -83,7 +84,11 @@ void SetDragImage(const GURL& url, SkBitmap bitmap; float raster_scale = ScaleFactorForDragFromWidget(&widget); SkColor color = SK_ColorTRANSPARENT; - button.Paint(ui::CanvasPainter(&bitmap, size, raster_scale, color).context()); + button.Paint(views::PaintInfo::CreateRootPaintInfo( + ui::CanvasPainter(&bitmap, size, raster_scale, color, + widget.GetCompositor()->is_pixel_canvas()) + .context(), + size)); gfx::ImageSkia image(gfx::ImageSkiaRep(bitmap, raster_scale)); data->provider().SetDragImage(image, press_point); } diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views/cocoa/bridged_content_view.mm index e300b7e245c..a06eed11e58 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.mm +++ b/chromium/ui/views/cocoa/bridged_content_view.mm @@ -466,6 +466,12 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { if (!hostedView_) return; + // Always propagate the shift modifier if present. Shift doesn't always alter + // the command selector, but should always be passed along. Control and Alt + // have different meanings on Mac, so they do not propagate automatically. + if ([keyDownEvent_ modifierFlags] & NSShiftKeyMask) + eventFlags |= ui::EF_SHIFT_DOWN; + // Generate a synthetic event with the keycode toolkit-views expects. ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); @@ -622,6 +628,13 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { ui::MouseEvent event(theEvent); + // ui::EventLocationFromNative() assumes the event hit the contentView. + // Adjust if that's not the case (e.g. for reparented views). + if ([theEvent window] && [[self window] contentView] != self) { + NSPoint p = [self convertPoint:[theEvent locationInWindow] fromView:nil]; + event.set_location(gfx::Point(p.x, NSHeight([self frame]) - p.y)); + } + // Aura updates tooltips with the help of aura::Window::AddPreTargetHandler(). // Mac hooks in here. [self updateTooltipIfRequiredAt:event.location()]; @@ -669,7 +682,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // window containing it, since AppKit requires a titlebar to give frameless // windows correct shadows and rounded corners. NSWindow* window = [self window]; - if (window) + if (window && [window contentView] == self) newSize = [window contentRectForFrameRect:[window frame]].size; [super setFrameSize:newSize]; @@ -1449,8 +1462,9 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // 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.underlines.push_back(ui::CompositionUnderline( - 0, [text length], SK_ColorBLACK, false, SK_ColorTRANSPARENT)); + composition.ime_text_spans.push_back( + ui::ImeTextSpan(ui::ImeTextSpan::Type::kComposition, 0, [text length], + SK_ColorBLACK, false, 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 ac529290b78..bf322525d2e 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.h +++ b/chromium/ui/views/cocoa/bridged_native_widget.h @@ -201,6 +201,13 @@ class VIEWS_EXPORT BridgedNativeWidget return child_windows_; } + // Re-parent a |native_view| in this Widget to be a child of |new_parent|. + // |native_view| must either be |ns_view()| or a descendant of |ns_view()|. + // |native_view| is added as a subview of |new_parent| unless it is the + // contentView of a top-level Widget. If |native_view| is |ns_view()|, |this| + // also becomes a child window of |new_parent|'s NSWindow. + void ReparentNativeView(NSView* native_view, NSView* new_parent); + bool target_fullscreen_state() const { return target_fullscreen_state_; } bool window_visible() const { return window_visible_; } bool wants_to_be_visible() const { return wants_to_be_visible_; } @@ -247,6 +254,9 @@ class VIEWS_EXPORT BridgedNativeWidget // 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; @@ -328,6 +338,9 @@ 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 b4a4de3018c..132ca1e4e20 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget.mm @@ -21,6 +21,7 @@ #include "ui/base/ime/input_method.h" #include "ui/base/ime/input_method_factory.h" #include "ui/base/layout.h" +#include "ui/compositor/compositor_switches.h" #include "ui/gfx/geometry/dip_util.h" #import "ui/gfx/mac/coordinate_conversion.h" #import "ui/gfx/mac/nswindow_frame_controls.h" @@ -126,6 +127,20 @@ bool PositionWindowInScreenCoordinates(views::Widget* widget, return widget && widget->is_top_level(); } +// Returns true if the content_view is reparented. +bool PositionWindowInNativeViewParent(NSView* content_view) { + return [[content_view window] contentView] != content_view; +} + +// Return the offset of the parent native view from the window. +gfx::Vector2d GetNativeViewParentOffset(NSView* content_view) { + NSWindow* window = [content_view window]; + NSView* parent_view = [content_view superview]; + NSPoint p = NSMakePoint(0, NSHeight([parent_view frame])); + p = [parent_view convertPoint:p toView:nil]; + return gfx::Vector2d(p.x, NSHeight([window frame]) - p.y); +} + // Return the content size for a minimum or maximum widget size. gfx::Size GetClientSizeForWindowSize(NSWindow* window, const gfx::Size& window_size) { @@ -358,7 +373,8 @@ BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) target_fullscreen_state_(false), in_fullscreen_transition_(false), window_visible_(false), - wants_to_be_visible_(false) { + wants_to_be_visible_(false), + mouse_down_monitor_(nullptr) { if (BridgedNativeWidget::ShouldUseDragEventMonitor()) SetupDragEventMonitor(); @@ -406,6 +422,17 @@ 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]); @@ -520,6 +547,9 @@ void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds) { if (parent_ && !PositionWindowInScreenCoordinates(widget, widget_type_)) actual_new_bounds.Offset(parent_->GetChildWindowOffset()); + if (PositionWindowInNativeViewParent(bridged_view_)) + actual_new_bounds.Offset(GetNativeViewParentOffset(bridged_view_)); + [window_ setFrame:gfx::ScreenRectToNSRect(actual_new_bounds) display:YES animate:NO]; @@ -643,7 +673,7 @@ void BridgedNativeWidget::AcquireCapture() { // will reset the mouse cursor to an arrow. Asking the window for an update // here will restore what we want. However, it can sometimes cause the cursor // to flicker, once, on the initial mouseDown. - // TOOD(tapted): Make this unnecessary by only asking for global mouse capture + // TODO(tapted): Make this unnecessary by only asking for global mouse capture // for the cases that need it (e.g. menus, but not drag and drop). [window_ cursorUpdate:[NSApp currentEvent]]; } @@ -725,6 +755,10 @@ void BridgedNativeWidget::OnWindowWillClose() { parent_ = nullptr; } [[NSNotificationCenter defaultCenter] removeObserver:window_delegate_]; + if (mouse_down_monitor_) { + [NSEvent removeMonitor:mouse_down_monitor_]; + mouse_down_monitor_ = nullptr; + } [window_ setDelegate:nil]; native_widget_mac_->OnWindowDestroyed(); // Note: |this| is deleted here. @@ -1064,6 +1098,42 @@ void BridgedNativeWidget::ReorderChildViews() { [bridged_view_ sortSubviewsUsingFunction:&SubviewSorter context:&rank]; } +void BridgedNativeWidget::ReparentNativeView(NSView* native_view, + NSView* new_parent) { + DCHECK([new_parent window]); + DCHECK([native_view isDescendantOf:bridged_view_]); + DCHECK(window_ && ![window_ isSheet]); + + BridgedNativeWidget* parent_bridge = + NativeWidgetMac::GetBridgeForNativeWindow([new_parent window]); + if (native_view == bridged_view_.get() && parent_bridge != parent_) { + if (parent_) + parent_->RemoveChildWindow(this); + + if (parent_bridge) { + parent_ = parent_bridge; + parent_bridge->child_windows_.push_back(this); + } else { + parent_ = new WidgetOwnerNSWindowAdapter(this, new_parent); + } + + [[new_parent window] addChildWindow:window_ ordered:NSWindowAbove]; + } + + if (!native_widget_mac_->GetWidget()->is_top_level() || + native_view != bridged_view_.get()) { + // Make native_view be a child of new_parent by adding it as a subview. + // The window_ must remain visible because it controls the bounds and + // visibility of the ui::Layer. So just hide it by setting alpha value to + // zero. + [new_parent addSubview:native_view]; + if (native_view == bridged_view_.get()) { + [window_ setAlphaValue:0]; + [window_ setIgnoresMouseEvents:YES]; + } + } +} + //////////////////////////////////////////////////////////////////////////////// // BridgedNativeWidget, internal::InputMethodDelegate: @@ -1275,7 +1345,8 @@ void BridgedNativeWidget::CreateCompositor() { compositor_.reset(new ui::Compositor( context_factory_private->AllocateFrameSinkId(), context_factory, context_factory_private, GetCompositorTaskRunner(), - false /* enable_surface_synchronization */)); + false /* enable_surface_synchronization */, + ui::IsPixelCanvasRecordingEnabled())); compositor_->SetAcceleratedWidget(compositor_widget_->accelerated_widget()); compositor_widget_->SetNSView(this); } @@ -1435,4 +1506,26 @@ void BridgedNativeWidget::SetDraggable(bool draggable) { [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 c7dfd87266b..a959ac0fa4d 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm @@ -9,6 +9,7 @@ #import "base/mac/mac_util.h" #import "base/mac/sdk_forward_declarations.h" #include "base/macros.h" +#include "base/run_loop.h" #include "ui/base/hit_test.h" #import "ui/base/test/nswindow_fullscreen_notification_waiter.h" #include "ui/base/test/ui_controls.h" diff --git a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm index a500e998e22..733bd508122 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm @@ -40,6 +40,7 @@ using base::ASCIIToUTF16; using base::SysNSStringToUTF8; using base::SysNSStringToUTF16; using base::SysUTF8ToNSString; +using base::SysUTF16ToNSString; #define EXPECT_EQ_RANGE(a, b) \ EXPECT_EQ(a.location, b.location); \ diff --git a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm index af19ce7c26e..a61d582b79c 100644 --- a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm +++ b/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm @@ -21,6 +21,7 @@ - (ViewsNSWindowDelegate*)viewsNSWindowDelegate; - (views::Widget*)viewsWidget; - (BOOL)hasViewsMenuActive; +- (id)rootAccessibilityObject; // Private API on NSWindow, determines whether the title is drawn on the title // bar. The title is still visible in menus, Expose, etc. @@ -74,6 +75,11 @@ return menuController && menuController->owner() == [self viewsWidget]; } +- (id)rootAccessibilityObject { + views::Widget* widget = [self viewsWidget]; + return widget ? widget->GetRootView()->GetNativeViewAccessible() : nil; +} + // NSWindow overrides. - (BOOL)_isTitleHidden { @@ -222,4 +228,39 @@ forHandler:commandHandler_]; } +// NSWindow overrides (NSAccessibility informal protocol implementation). + +- (id)accessibilityFocusedUIElement { + // The SDK documents this as "The deepest descendant of the accessibility + // hierarchy that has the focus" and says "if a child element does not have + // the focus, either return self or, if available, invoke the superclass's + // implementation." + // The behavior of NSWindow is usually to return null, except when the window + // is first shown, when it returns self. But in the second case, we can + // provide richer a11y information by reporting the views::RootView instead. + // Additionally, if we don't do this, VoiceOver reads out the partial a11y + // properties on the NSWindow and repeats them when focusing an item in the + // RootView's a11y group. See http://crbug.com/748221. + views::Widget* widget = [self viewsWidget]; + id superFocus = [super accessibilityFocusedUIElement]; + if (!widget || superFocus != self) + return superFocus; + + return widget->GetRootView()->GetNativeViewAccessible(); +} + +- (id)accessibilityAttributeValue:(NSString*)attribute { + // Check when NSWindow is asked for its title to provide the title given by + // the views::RootView (and WidgetDelegate::GetAccessibleWindowTitle()). For + // all other attributes, use what NSWindow provides by default since diverging + // from NSWindow's behavior can easily break VoiceOver integration. + if (![attribute isEqualToString:NSAccessibilityTitleAttribute]) + return [super accessibilityAttributeValue:attribute]; + + id viewsValue = + [[self rootAccessibilityObject] accessibilityAttributeValue:attribute]; + return viewsValue ? viewsValue + : [super accessibilityAttributeValue:attribute]; +} + @end diff --git a/chromium/ui/views/cocoa/views_nswindow_delegate.mm b/chromium/ui/views/cocoa/views_nswindow_delegate.mm index d0d50a0eada..96acf216de7 100644 --- a/chromium/ui/views/cocoa/views_nswindow_delegate.mm +++ b/chromium/ui/views/cocoa/views_nswindow_delegate.mm @@ -92,6 +92,12 @@ parent_->OnWindowKeyStatusChangedTo(false); } +- (BOOL)windowShouldClose:(id)sender { + views::NonClientView* nonClientView = + [self nativeWidgetMac]->GetWidget()->non_client_view(); + return !nonClientView || nonClientView->CanClose(); +} + - (void)windowWillClose:(NSNotification*)notification { // Retain |self|. |parent_| should be cleared. OnWindowWillClose() may delete // |parent_|, but it may also dealloc |self| before returning. However, the diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc index 14aa0bb4d8b..0291ab49358 100644 --- a/chromium/ui/views/controls/button/button.cc +++ b/chromium/ui/views/controls/button/button.cc @@ -7,12 +7,71 @@ #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/base/class_property.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "ui/gfx/animation/throb_animation.h" +#include "ui/gfx/color_palette.h" +#include "ui/native_theme/native_theme.h" +#include "ui/views/animation/ink_drop_highlight.h" +#include "ui/views/animation/ink_drop_impl.h" +#include "ui/views/controls/button/blue_button.h" +#include "ui/views/controls/button/checkbox.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/button/menu_button.h" +#include "ui/views/controls/button/radio_button.h" +#include "ui/views/controls/button/toggle_button.h" +#include "ui/views/painter.h" +#include "ui/views/style/platform_style.h" +#include "ui/views/widget/widget.h" + +#if defined(USE_AURA) +#include "ui/aura/client/capture_client.h" +#include "ui/aura/window.h" +#endif + namespace views { +namespace { + +DEFINE_LOCAL_UI_CLASS_PROPERTY_KEY(bool, kIsButtonProperty, false); + +// How long the hover animation takes if uninterrupted. +const int kHoverFadeDurationMs = 150; + +Button::KeyClickAction GetKeyClickActionForEvent(const ui::KeyEvent& event) { + if (event.key_code() == ui::VKEY_SPACE) + return PlatformStyle::kKeyClickActionOnSpace; + if (event.key_code() == ui::VKEY_RETURN && + PlatformStyle::kReturnClicksFocusedControl) + return Button::CLICK_ON_KEY_PRESS; + return Button::CLICK_NONE; +} + +} // namespace + //////////////////////////////////////////////////////////////////////////////// // Button, static public: // static +const char Button::kViewClassName[] = "Button"; + +// static +const Button* Button::AsButton(const views::View* view) { + return AsButton(const_cast<View*>(view)); +} + +// static +Button* Button::AsButton(views::View* view) { + if (view && view->GetProperty(kIsButtonProperty)) + return static_cast<Button*>(view); + return nullptr; +} + +// static Button::ButtonState Button::GetButtonStateFrom(ui::NativeTheme::State state) { switch (state) { case ui::NativeTheme::kDisabled: return Button::STATE_DISABLED; @@ -27,8 +86,7 @@ Button::ButtonState Button::GetButtonStateFrom(ui::NativeTheme::State state) { //////////////////////////////////////////////////////////////////////////////// // Button, public: -Button::~Button() { -} +Button::~Button() {} void Button::SetFocusForPlatform() { #if defined(OS_MACOSX) @@ -49,10 +107,253 @@ void Button::SetTooltipText(const base::string16& tooltip_text) { void Button::SetAccessibleName(const base::string16& name) { accessible_name_ = name; } +void Button::SetState(ButtonState state) { + if (state == state_) + return; + + if (animate_on_state_change_ && + (!is_throbbing_ || !hover_animation_.is_animating())) { + is_throbbing_ = false; + if ((state_ == STATE_HOVERED) && (state == STATE_NORMAL)) { + // For HOVERED -> NORMAL, animate from hovered (1) to not hovered (0). + hover_animation_.Hide(); + } else if (state != STATE_HOVERED) { + // For HOVERED -> PRESSED/DISABLED, or any transition not involving + // HOVERED at all, simply set the state to not hovered (0). + hover_animation_.Reset(); + } else if (state_ == STATE_NORMAL) { + // For NORMAL -> HOVERED, animate from not hovered (0) to hovered (1). + hover_animation_.Show(); + } else { + // For PRESSED/DISABLED -> HOVERED, simply set the state to hovered (1). + hover_animation_.Reset(1); + } + } + + ButtonState old_state = state_; + state_ = state; + StateChanged(old_state); + SchedulePaint(); +} + +void Button::StartThrobbing(int cycles_til_stop) { + if (!animate_on_state_change_) + return; + is_throbbing_ = true; + hover_animation_.StartThrobbing(cycles_til_stop); +} + +void Button::StopThrobbing() { + if (hover_animation_.is_animating()) { + hover_animation_.Stop(); + SchedulePaint(); + } +} + +void Button::SetAnimationDuration(int duration) { + hover_animation_.SetSlideDuration(duration); +} + +void Button::SetHotTracked(bool is_hot_tracked) { + if (state_ != STATE_DISABLED) + SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL); + + if (is_hot_tracked) + NotifyAccessibilityEvent(ui::AX_EVENT_HOVER, true); +} + +bool Button::IsHotTracked() const { + return state_ == STATE_HOVERED; +} + +void Button::SetFocusPainter(std::unique_ptr<Painter> focus_painter) { + focus_painter_ = std::move(focus_painter); +} //////////////////////////////////////////////////////////////////////////////// // Button, View overrides: +void Button::OnEnabledChanged() { + if (enabled() ? (state_ != STATE_DISABLED) : (state_ == STATE_DISABLED)) + return; + + if (enabled()) { + bool should_enter_hover_state = ShouldEnterHoveredState(); + SetState(should_enter_hover_state ? STATE_HOVERED : STATE_NORMAL); + GetInkDrop()->SetHovered(should_enter_hover_state); + } else { + SetState(STATE_DISABLED); + GetInkDrop()->SetHovered(false); + } +} + +const char* Button::GetClassName() const { + return kViewClassName; +} + +bool Button::OnMousePressed(const ui::MouseEvent& event) { + if (state_ == STATE_DISABLED) + return true; + if (state_ != STATE_PRESSED && ShouldEnterPushedState(event) && + HitTestPoint(event.location())) { + SetState(STATE_PRESSED); + AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event); + } + if (request_focus_on_press_) + RequestFocus(); + if (IsTriggerableEvent(event) && notify_action_ == NOTIFY_ON_PRESS) { + NotifyClick(event); + // NOTE: We may be deleted at this point (by the listener's notification + // handler). + } + return true; +} + +bool Button::OnMouseDragged(const ui::MouseEvent& event) { + if (state_ != STATE_DISABLED) { + const bool should_enter_pushed = ShouldEnterPushedState(event); + const bool should_show_pending = + should_enter_pushed && notify_action_ == NOTIFY_ON_RELEASE && !InDrag(); + if (HitTestPoint(event.location())) { + SetState(should_enter_pushed ? STATE_PRESSED : STATE_HOVERED); + if (should_show_pending && GetInkDrop()->GetTargetInkDropState() == + views::InkDropState::HIDDEN) { + AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event); + } + } else { + SetState(STATE_NORMAL); + if (should_show_pending && GetInkDrop()->GetTargetInkDropState() == + views::InkDropState::ACTION_PENDING) { + AnimateInkDrop(views::InkDropState::HIDDEN, &event); + } + } + } + return true; +} + +void Button::OnMouseReleased(const ui::MouseEvent& event) { + if (state_ != STATE_DISABLED) { + if (!HitTestPoint(event.location())) { + SetState(STATE_NORMAL); + } else { + SetState(STATE_HOVERED); + if (IsTriggerableEvent(event) && notify_action_ == NOTIFY_ON_RELEASE) { + NotifyClick(event); + // NOTE: We may be deleted at this point (by the listener's notification + // handler). + return; + } + } + } + if (notify_action_ == NOTIFY_ON_RELEASE) + OnClickCanceled(event); +} + +void Button::OnMouseCaptureLost() { + // Starting a drag results in a MouseCaptureLost. Reset button state. + // TODO(varkha): Reset the state even while in drag. The same logic may + // applies everywhere so gather any feedback and update. + if (state_ != STATE_DISABLED) + SetState(STATE_NORMAL); + AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */); + GetInkDrop()->SetHovered(false); + InkDropHostView::OnMouseCaptureLost(); +} + +void Button::OnMouseEntered(const ui::MouseEvent& event) { + if (state_ != STATE_DISABLED) + SetState(STATE_HOVERED); +} + +void Button::OnMouseExited(const ui::MouseEvent& event) { + // Starting a drag results in a MouseExited, we need to ignore it. + if (state_ != STATE_DISABLED && !InDrag()) + SetState(STATE_NORMAL); +} + +void Button::OnMouseMoved(const ui::MouseEvent& event) { + if (state_ != STATE_DISABLED) + SetState(HitTestPoint(event.location()) ? STATE_HOVERED : STATE_NORMAL); +} + +bool Button::OnKeyPressed(const ui::KeyEvent& event) { + if (state_ == STATE_DISABLED) + return false; + + switch (GetKeyClickActionForEvent(event)) { + case KeyClickAction::CLICK_ON_KEY_RELEASE: + SetState(STATE_PRESSED); + if (GetInkDrop()->GetTargetInkDropState() != + InkDropState::ACTION_PENDING) { + AnimateInkDrop(InkDropState::ACTION_PENDING, nullptr /* event */); + } + return true; + case KeyClickAction::CLICK_ON_KEY_PRESS: + SetState(STATE_NORMAL); + NotifyClick(event); + return true; + case KeyClickAction::CLICK_NONE: + return false; + } + + NOTREACHED(); + return false; +} + +bool Button::OnKeyReleased(const ui::KeyEvent& event) { + const bool click_button = + state_ == STATE_PRESSED && + GetKeyClickActionForEvent(event) == KeyClickAction::CLICK_ON_KEY_RELEASE; + if (!click_button) + return false; + + SetState(STATE_NORMAL); + NotifyClick(event); + return true; +} + +void Button::OnGestureEvent(ui::GestureEvent* event) { + if (state_ == STATE_DISABLED) { + InkDropHostView::OnGestureEvent(event); + return; + } + + if (event->type() == ui::ET_GESTURE_TAP && IsTriggerableEvent(*event)) { + // Set the button state to hot and start the animation fully faded in. The + // GESTURE_END event issued immediately after will set the state to + // STATE_NORMAL beginning the fade out animation. See + // http://crbug.com/131184. + SetState(STATE_HOVERED); + hover_animation_.Reset(1.0); + NotifyClick(*event); + event->StopPropagation(); + } else if (event->type() == ui::ET_GESTURE_TAP_DOWN && + ShouldEnterPushedState(*event)) { + SetState(STATE_PRESSED); + if (request_focus_on_press_) + RequestFocus(); + event->StopPropagation(); + } else if (event->type() == ui::ET_GESTURE_TAP_CANCEL || + event->type() == ui::ET_GESTURE_END) { + SetState(STATE_NORMAL); + } + if (!event->handled()) + InkDropHostView::OnGestureEvent(event); +} + +bool Button::AcceleratorPressed(const ui::Accelerator& accelerator) { + SetState(STATE_NORMAL); + NotifyClick(accelerator.ToKeyEvent()); + return true; +} + +bool Button::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { + // If this button is focused and the user presses space or enter, don't let + // that be treated as an accelerator if there is a key click action + // corresponding to it. + return GetKeyClickActionForEvent(event) != KeyClickAction::CLICK_NONE; +} + bool Button::GetTooltipText(const gfx::Point& p, base::string16* tooltip) const { if (tooltip_text_.empty()) @@ -62,6 +363,36 @@ bool Button::GetTooltipText(const gfx::Point& p, return true; } +void Button::ShowContextMenu(const gfx::Point& p, + ui::MenuSourceType source_type) { + if (!context_menu_controller()) + return; + + // We're about to show the context menu. Showing the context menu likely means + // we won't get a mouse exited and reset state. Reset it now to be sure. + if (state_ != STATE_DISABLED) + SetState(STATE_NORMAL); + if (hide_ink_drop_when_showing_context_menu_) { + GetInkDrop()->SetHovered(false); + AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */); + } + InkDropHostView::ShowContextMenu(p, source_type); +} + +void Button::OnDragDone() { + // Only reset the state to normal if the button isn't currently disabled + // (since disabled buttons may still be able to be dragged). + if (state_ != STATE_DISABLED) + SetState(STATE_NORMAL); + AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */); +} + +void Button::OnPaint(gfx::Canvas* canvas) { + InkDropHostView::OnPaint(canvas); + PaintButtonContents(canvas); + Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); +} + void Button::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ui::AX_ROLE_BUTTON; node_data->SetName(accessible_name_); @@ -69,24 +400,154 @@ void Button::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->AddIntAttribute(ui::AX_ATTR_RESTRICTION, ui::AX_RESTRICTION_DISABLED); } + + switch (state_) { + case STATE_HOVERED: + node_data->AddState(ui::AX_STATE_HOVERED); + break; + case STATE_PRESSED: + node_data->AddIntAttribute(ui::AX_ATTR_CHECKED_STATE, + ui::AX_CHECKED_STATE_TRUE); + break; + case STATE_DISABLED: + node_data->AddIntAttribute(ui::AX_ATTR_RESTRICTION, + ui::AX_RESTRICTION_DISABLED); + break; + case STATE_NORMAL: + case STATE_COUNT: + // No additional accessibility node_data set for this button node_data. + break; + } + if (enabled()) { + node_data->AddIntAttribute(ui::AX_ATTR_DEFAULT_ACTION_VERB, + ui::AX_DEFAULT_ACTION_VERB_PRESS); + } +} + +void Button::VisibilityChanged(View* starting_from, bool visible) { + InkDropHostView::VisibilityChanged(starting_from, visible); + if (state_ == STATE_DISABLED) + return; + SetState(visible && ShouldEnterHoveredState() ? STATE_HOVERED : STATE_NORMAL); +} + +void Button::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) { + if (!details.is_add && state_ != STATE_DISABLED) + SetState(STATE_NORMAL); +} + +void Button::OnFocus() { + InkDropHostView::OnFocus(); + if (focus_painter_) + SchedulePaint(); +} + +void Button::OnBlur() { + InkDropHostView::OnBlur(); + if (IsHotTracked() || state_ == STATE_PRESSED) { + SetState(STATE_NORMAL); + if (GetInkDrop()->GetTargetInkDropState() != views::InkDropState::HIDDEN) + AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */); + // TODO(bruthig) : Fix Buttons to work well when multiple input + // methods are interacting with a button. e.g. By animating to HIDDEN here + // it is possible for a Mouse Release to trigger an action however there + // would be no visual cue to the user that this will occur. + } + if (focus_painter_) + SchedulePaint(); +} + +std::unique_ptr<InkDrop> Button::CreateInkDrop() { + std::unique_ptr<views::InkDropImpl> ink_drop = CreateDefaultInkDropImpl(); + ink_drop->SetShowHighlightOnFocus(true); + return std::move(ink_drop); +} + +SkColor Button::GetInkDropBaseColor() const { + return ink_drop_base_color_; +} + +//////////////////////////////////////////////////////////////////////////////// +// Button, gfx::AnimationDelegate implementation: + +void Button::AnimationProgressed(const gfx::Animation* animation) { + SchedulePaint(); } //////////////////////////////////////////////////////////////////////////////// // Button, protected: Button::Button(ButtonListener* listener) - : listener_(listener), - tag_(-1) { + : listener_(listener), ink_drop_base_color_(gfx::kPlaceholderColor) { SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); + SetProperty(kIsButtonProperty, true); + hover_animation_.SetSlideDuration(kHoverFadeDurationMs); } void Button::NotifyClick(const ui::Event& event) { + if (has_ink_drop_action_on_click_) { + AnimateInkDrop(InkDropState::ACTION_TRIGGERED, + ui::LocatedEvent::FromIfValid(&event)); + } // We can be called when there is no listener, in cases like double clicks on // menu buttons etc. if (listener_) listener_->ButtonPressed(this, event); } -void Button::OnClickCanceled(const ui::Event& event) {} +void Button::OnClickCanceled(const ui::Event& event) { + if (ShouldUpdateInkDropOnClickCanceled()) { + if (GetInkDrop()->GetTargetInkDropState() == + views::InkDropState::ACTION_PENDING || + GetInkDrop()->GetTargetInkDropState() == + views::InkDropState::ALTERNATE_ACTION_PENDING) { + AnimateInkDrop(views::InkDropState::HIDDEN, + ui::LocatedEvent::FromIfValid(&event)); + } + } +} + +void Button::StateChanged(ButtonState old_state) {} + +bool Button::IsTriggerableEvent(const ui::Event& event) { + return event.type() == ui::ET_GESTURE_TAP_DOWN || + event.type() == ui::ET_GESTURE_TAP || + (event.IsMouseEvent() && + (triggerable_event_flags_ & event.flags()) != 0); +} + +bool Button::ShouldUpdateInkDropOnClickCanceled() const { + return true; +} + +bool Button::ShouldEnterPushedState(const ui::Event& event) { + return IsTriggerableEvent(event); +} + +void Button::PaintButtonContents(gfx::Canvas* canvas) {} + +bool Button::ShouldEnterHoveredState() { + if (!visible()) + return false; + + bool check_mouse_position = true; +#if defined(USE_AURA) + // If another window has capture, we shouldn't check the current mouse + // position because the button won't receive any mouse events - so if the + // mouse was hovered, the button would be stuck in a hovered state (since it + // would never receive OnMouseExited). + const Widget* widget = GetWidget(); + if (widget && widget->GetNativeWindow()) { + aura::Window* root_window = widget->GetNativeWindow()->GetRootWindow(); + aura::client::CaptureClient* capture_client = + aura::client::GetCaptureClient(root_window); + aura::Window* capture_window = + capture_client ? capture_client->GetGlobalCaptureWindow() : nullptr; + check_mouse_position = !capture_window || capture_window == root_window; + } +#endif + + return check_mouse_position && IsMouseHovered(); +} } // namespace views diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h index 62aecd7734e..26c852d8717 100644 --- a/chromium/ui/views/controls/button/button.h +++ b/chromium/ui/views/controls/button/button.h @@ -6,8 +6,14 @@ #define UI_VIEWS_CONTROLS_BUTTON_BUTTON_H_ #include "base/macros.h" +#include "build/build_config.h" +#include "ui/events/event_constants.h" +#include "ui/gfx/animation/animation_delegate.h" +#include "ui/gfx/animation/throb_animation.h" #include "ui/native_theme/native_theme.h" #include "ui/views/animation/ink_drop_host_view.h" +#include "ui/views/animation/ink_drop_state.h" +#include "ui/views/painter.h" namespace views { @@ -24,9 +30,11 @@ class VIEWS_EXPORT ButtonListener { virtual ~ButtonListener() {} }; -// A View representing a button. Depending on the specific type, the button -// could be implemented by a native control or custom rendered. -class VIEWS_EXPORT Button : public InkDropHostView { +// A View representing a button. A Button is not focusable by default and will +// not be part of the focus chain, unless in accessibility mode (see +// SetFocusForPlatform()). +class VIEWS_EXPORT Button : public InkDropHostView, + public gfx::AnimationDelegate { public: ~Button() override; @@ -47,6 +55,26 @@ class VIEWS_EXPORT Button : public InkDropHostView { STYLE_COUNT, }; + // An enum describing the events on which a button should notify its listener. + enum NotifyAction { + NOTIFY_ON_PRESS, + NOTIFY_ON_RELEASE, + }; + + // An enum describing the events on which a button should be clicked for a + // given key event. + enum KeyClickAction { + CLICK_ON_KEY_PRESS, + CLICK_ON_KEY_RELEASE, + CLICK_NONE, + }; + + // The menu button's class name. + static const char kViewClassName[]; + + static const Button* AsButton(const View* view); + static Button* AsButton(View* view); + static ButtonState GetButtonStateFrom(ui::NativeTheme::State state); // Make the button focusable as per the platform. @@ -59,13 +87,106 @@ class VIEWS_EXPORT Button : public InkDropHostView { void SetAccessibleName(const base::string16& name); + // Get/sets the current display state of the button. + ButtonState state() const { return state_; } + // Clients passing in STATE_DISABLED should consider calling + // SetEnabled(false) instead because the enabled flag can affect other things + // like event dispatching, focus traversals, etc. Calling SetEnabled(false) + // will also set the state of |this| to STATE_DISABLED. + void SetState(ButtonState state); + + // Starts throbbing. See HoverAnimation for a description of cycles_til_stop. + // This method does nothing if |animate_on_state_change_| is false. + void StartThrobbing(int cycles_til_stop); + + // Stops throbbing immediately. + void StopThrobbing(); + + // Set how long the hover animation will last for. + void SetAnimationDuration(int duration); + + void set_triggerable_event_flags(int triggerable_event_flags) { + triggerable_event_flags_ = triggerable_event_flags; + } + int triggerable_event_flags() const { return triggerable_event_flags_; } + + // Sets whether |RequestFocus| should be invoked on a mouse press. The default + // is false. + void set_request_focus_on_press(bool value) { +// On Mac, buttons should not request focus on a mouse press. Hence keep the +// default value i.e. false. +#if !defined(OS_MACOSX) + request_focus_on_press_ = value; +#endif + } + + bool request_focus_on_press() const { return request_focus_on_press_; } + + // See description above field. + void set_animate_on_state_change(bool value) { + animate_on_state_change_ = value; + } + + // Sets the event on which the button should notify its listener. + void set_notify_action(NotifyAction notify_action) { + notify_action_ = notify_action; + } + + void set_hide_ink_drop_when_showing_context_menu( + bool hide_ink_drop_when_showing_context_menu) { + hide_ink_drop_when_showing_context_menu_ = + hide_ink_drop_when_showing_context_menu; + } + + void set_ink_drop_base_color(SkColor color) { ink_drop_base_color_ = color; } + void set_has_ink_drop_action_on_click(bool has_ink_drop_action_on_click) { + has_ink_drop_action_on_click_ = has_ink_drop_action_on_click; + } + + void SetHotTracked(bool is_hot_tracked); + bool IsHotTracked() const; + + void SetFocusPainter(std::unique_ptr<Painter> focus_painter); + // Overridden from View: + void OnEnabledChanged() override; + const char* GetClassName() const override; + bool OnMousePressed(const ui::MouseEvent& event) override; + bool OnMouseDragged(const ui::MouseEvent& event) override; + void OnMouseReleased(const ui::MouseEvent& event) override; + void OnMouseCaptureLost() override; + void OnMouseEntered(const ui::MouseEvent& event) override; + void OnMouseExited(const ui::MouseEvent& event) override; + void OnMouseMoved(const ui::MouseEvent& event) override; + bool OnKeyPressed(const ui::KeyEvent& event) override; + bool OnKeyReleased(const ui::KeyEvent& event) override; + void OnGestureEvent(ui::GestureEvent* event) override; + bool AcceleratorPressed(const ui::Accelerator& accelerator) override; + bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override; bool GetTooltipText(const gfx::Point& p, base::string16* tooltip) const override; + void ShowContextMenu(const gfx::Point& p, + ui::MenuSourceType source_type) override; + void OnDragDone() override; + // Instead of overriding this, subclasses that want custom painting should use + // PaintButtonContents. + void OnPaint(gfx::Canvas* canvas) final; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + void VisibilityChanged(View* starting_from, bool is_visible) override; + void ViewHierarchyChanged( + const ViewHierarchyChangedDetails& details) override; + void OnFocus() override; + void OnBlur() override; + + // Overridden from InkDropHostView: + std::unique_ptr<InkDrop> CreateInkDrop() override; + SkColor GetInkDropBaseColor() const override; + + // Overridden from gfx::AnimationDelegate: + void AnimationProgressed(const gfx::Animation* animation) override; protected: - // Construct the Button with a Listener. The listener can be NULL. This can be + // Construct the Button with a Listener. The listener can be null. This can be // true of buttons that don't have a listener - e.g. menubuttons where there's // no default action and checkboxes. explicit Button(ButtonListener* listener); @@ -78,10 +199,48 @@ class VIEWS_EXPORT Button : public InkDropHostView { // events. virtual void OnClickCanceled(const ui::Event& event); + // Invoked from SetState() when SetState() is passed a value that differs from + // the current node_data. Button's implementation of StateChanged() does + // nothing; this method is provided for subclasses that wish to do something + // on state changes. + virtual void StateChanged(ButtonState old_state); + + // Returns true if the event is one that can trigger notifying the listener. + // This implementation returns true if the left mouse button is down. + virtual bool IsTriggerableEvent(const ui::Event& event); + + // Returns true if the ink drop should be updated by Button when + // OnClickCanceled() is called. This method is provided for subclasses. + // If the method is overriden and returns false, the subclass is responsible + // will be responsible for updating the ink drop. + virtual bool ShouldUpdateInkDropOnClickCanceled() const; + + // Returns true if the button should become pressed when the user + // holds the mouse down over the button. For this implementation, + // we simply return IsTriggerableEvent(event). + virtual bool ShouldEnterPushedState(const ui::Event& event); + + // Override to paint custom button contents. Any background or border set on + // the view will be painted before this is called and |focus_painter_| will be + // painted afterwards. + virtual void PaintButtonContents(gfx::Canvas* canvas); + + // Returns true if the button should enter hovered state; that is, if the + // mouse is over the button, and no other window has capture (which would + // prevent the button from receiving MouseExited events and updating its + // node_data). This does not take into account enabled node_data. + bool ShouldEnterHoveredState(); + + const gfx::ThrobAnimation& hover_animation() const { + return hover_animation_; + } + // The button's listener. Notified when clicked. ButtonListener* listener_; private: + FRIEND_TEST_ALL_PREFIXES(BlueButtonTest, Border); + // The text shown in a tooltip. base::string16 tooltip_text_; @@ -90,7 +249,39 @@ class VIEWS_EXPORT Button : public InkDropHostView { // The id tag associated with this button. Used to disambiguate buttons in // the ButtonListener implementation. - int tag_; + int tag_ = -1; + + ButtonState state_ = STATE_NORMAL; + + gfx::ThrobAnimation hover_animation_{this}; + + // Should we animate when the state changes? + bool animate_on_state_change_ = false; + + // Is the hover animation running because StartThrob was invoked? + bool is_throbbing_ = false; + + // Mouse event flags which can trigger button actions. + int triggerable_event_flags_ = ui::EF_LEFT_MOUSE_BUTTON; + + // See description above setter. + bool request_focus_on_press_ = false; + + // The event on which the button should notify its listener. + NotifyAction notify_action_ = NOTIFY_ON_RELEASE; + + // True when a button click should trigger an animation action on + // ink_drop_delegate(). + bool has_ink_drop_action_on_click_ = false; + + // When true, the ink drop ripple and hover will be hidden prior to showing + // the context menu. + bool hide_ink_drop_when_showing_context_menu_ = true; + + // The color of the ripple and hover. + SkColor ink_drop_base_color_; + + std::unique_ptr<Painter> focus_painter_; DISALLOW_COPY_AND_ASSIGN(Button); }; diff --git a/chromium/ui/views/controls/button/custom_button_unittest.cc b/chromium/ui/views/controls/button/button_unittest.cc index b9406604a13..ec700c8a9d0 100644 --- a/chromium/ui/views/controls/button/custom_button_unittest.cc +++ b/chromium/ui/views/controls/button/button_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/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -58,31 +58,28 @@ class TestContextMenuController : public ContextMenuController { DISALLOW_COPY_AND_ASSIGN(TestContextMenuController); }; -class TestCustomButton : public CustomButton, public ButtonListener { +class TestButton : public Button, public ButtonListener { public: - explicit TestCustomButton(bool has_ink_drop_action_on_click) - : CustomButton(this) { + explicit TestButton(bool has_ink_drop_action_on_click) : Button(this) { set_has_ink_drop_action_on_click(has_ink_drop_action_on_click); } - ~TestCustomButton() override {} + ~TestButton() override {} void ButtonPressed(Button* sender, const ui::Event& event) override { pressed_ = true; } - void OnClickCanceled(const ui::Event& event) override { - canceled_ = true; - } + void OnClickCanceled(const ui::Event& event) override { canceled_ = true; } // InkDropHostView: void AddInkDropLayer(ui::Layer* ink_drop_layer) override { ++ink_drop_layer_add_count_; - CustomButton::AddInkDropLayer(ink_drop_layer); + Button::AddInkDropLayer(ink_drop_layer); } void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override { ++ink_drop_layer_remove_count_; - CustomButton::RemoveInkDropLayer(ink_drop_layer); + Button::RemoveInkDropLayer(ink_drop_layer); } bool pressed() { return pressed_; } @@ -96,7 +93,7 @@ class TestCustomButton : public CustomButton, public ButtonListener { } // Raised visibility of OnFocus() to public - void OnFocus() override { CustomButton::OnFocus(); } + void OnFocus() override { Button::OnFocus(); } private: bool pressed_ = false; @@ -105,20 +102,20 @@ class TestCustomButton : public CustomButton, public ButtonListener { int ink_drop_layer_add_count_ = 0; int ink_drop_layer_remove_count_ = 0; - DISALLOW_COPY_AND_ASSIGN(TestCustomButton); + DISALLOW_COPY_AND_ASSIGN(TestButton); }; } // namespace -class CustomButtonTest : public ViewsTestBase { +class ButtonTest : public ViewsTestBase { public: - CustomButtonTest() {} - ~CustomButtonTest() override {} + ButtonTest() {} + ~ButtonTest() override {} void SetUp() override { ViewsTestBase::SetUp(); - // Create a widget so that the CustomButton can query the hover state + // Create a widget so that the Button can query the hover state // correctly. widget_.reset(new Widget); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); @@ -127,7 +124,7 @@ class CustomButtonTest : public ViewsTestBase { widget_->Init(params); widget_->Show(); - button_ = new TestCustomButton(false); + button_ = new TestButton(false); widget_->SetContentsView(button_); } @@ -139,14 +136,14 @@ class CustomButtonTest : public ViewsTestBase { void CreateButtonWithInkDrop(std::unique_ptr<InkDrop> ink_drop, bool has_ink_drop_action_on_click) { delete button_; - button_ = new TestCustomButton(has_ink_drop_action_on_click); + button_ = new TestButton(has_ink_drop_action_on_click); InkDropHostViewTestApi(button_).SetInkDrop(std::move(ink_drop)); widget_->SetContentsView(button_); } void CreateButtonWithRealInkDrop() { delete button_; - button_ = new TestCustomButton(false); + button_ = new TestButton(false); InkDropHostViewTestApi(button_).SetInkDrop( base::MakeUnique<InkDropImpl>(button_, button_->size())); widget_->SetContentsView(button_); @@ -154,39 +151,39 @@ class CustomButtonTest : public ViewsTestBase { protected: Widget* widget() { return widget_.get(); } - TestCustomButton* button() { return button_; } + TestButton* button() { return button_; } void SetDraggedView(View* dragged_view) { widget_->dragged_view_ = dragged_view; } private: std::unique_ptr<Widget> widget_; - TestCustomButton* button_ = nullptr; + TestButton* button_ = nullptr; - DISALLOW_COPY_AND_ASSIGN(CustomButtonTest); + DISALLOW_COPY_AND_ASSIGN(ButtonTest); }; // Tests that hover state changes correctly when visiblity/enableness changes. -TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { +TEST_F(ButtonTest, HoverStateOnVisibilityChange) { ui::test::EventGenerator generator(widget()->GetNativeWindow()); generator.PressLeftButton(); - EXPECT_EQ(CustomButton::STATE_PRESSED, button()->state()); + EXPECT_EQ(Button::STATE_PRESSED, button()->state()); generator.ReleaseLeftButton(); - EXPECT_EQ(CustomButton::STATE_HOVERED, button()->state()); + EXPECT_EQ(Button::STATE_HOVERED, button()->state()); button()->SetEnabled(false); - EXPECT_EQ(CustomButton::STATE_DISABLED, button()->state()); + EXPECT_EQ(Button::STATE_DISABLED, button()->state()); button()->SetEnabled(true); - EXPECT_EQ(CustomButton::STATE_HOVERED, button()->state()); + EXPECT_EQ(Button::STATE_HOVERED, button()->state()); button()->SetVisible(false); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); button()->SetVisible(true); - EXPECT_EQ(CustomButton::STATE_HOVERED, button()->state()); + EXPECT_EQ(Button::STATE_HOVERED, button()->state()); #if defined(USE_AURA) { @@ -201,16 +198,16 @@ TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { second_widget.GetNativeWindow()->SetCapture(); button()->SetEnabled(false); - EXPECT_EQ(CustomButton::STATE_DISABLED, button()->state()); + EXPECT_EQ(Button::STATE_DISABLED, button()->state()); button()->SetEnabled(true); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); button()->SetVisible(false); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); button()->SetVisible(true); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); } #endif @@ -225,43 +222,43 @@ TEST_F(CustomButtonTest, HoverStateOnVisibilityChange) { cursor_client.DisableMouseEvents(); button()->SetEnabled(false); - EXPECT_EQ(CustomButton::STATE_DISABLED, button()->state()); + EXPECT_EQ(Button::STATE_DISABLED, button()->state()); button()->SetEnabled(true); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); button()->SetVisible(false); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); button()->SetVisible(true); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); #endif // !defined(OS_MACOSX) || defined(USE_AURA) } // Tests the different types of NotifyActions. -TEST_F(CustomButtonTest, NotifyAction) { +TEST_F(ButtonTest, NotifyAction) { gfx::Point center(10, 10); // By default the button should notify its listener on mouse release. button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - EXPECT_EQ(CustomButton::STATE_PRESSED, button()->state()); + EXPECT_EQ(Button::STATE_PRESSED, button()->state()); EXPECT_FALSE(button()->pressed()); button()->OnMouseReleased(ui::MouseEvent( ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - EXPECT_EQ(CustomButton::STATE_HOVERED, button()->state()); + EXPECT_EQ(Button::STATE_HOVERED, button()->state()); EXPECT_TRUE(button()->pressed()); // Set the notify action to its listener on mouse press. button()->Reset(); - button()->set_notify_action(CustomButton::NOTIFY_ON_PRESS); + button()->set_notify_action(Button::NOTIFY_ON_PRESS); button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - EXPECT_EQ(CustomButton::STATE_PRESSED, button()->state()); + EXPECT_EQ(Button::STATE_PRESSED, button()->state()); EXPECT_TRUE(button()->pressed()); // The button should no longer notify on mouse release. @@ -269,13 +266,13 @@ TEST_F(CustomButtonTest, NotifyAction) { button()->OnMouseReleased(ui::MouseEvent( ui::ET_MOUSE_RELEASED, center, center, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - EXPECT_EQ(CustomButton::STATE_HOVERED, button()->state()); + EXPECT_EQ(Button::STATE_HOVERED, button()->state()); EXPECT_FALSE(button()->pressed()); } // Tests that OnClickCanceled gets called when NotifyClick is not expected // anymore. -TEST_F(CustomButtonTest, NotifyActionNoClick) { +TEST_F(ButtonTest, NotifyActionNoClick) { gfx::Point center(10, 10); // By default the button should notify its listener on mouse release. @@ -291,7 +288,7 @@ TEST_F(CustomButtonTest, NotifyActionNoClick) { // Set the notify action to its listener on mouse press. button()->Reset(); - button()->set_notify_action(CustomButton::NOTIFY_ON_PRESS); + button()->set_notify_action(Button::NOTIFY_ON_PRESS); button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), ui::EF_RIGHT_MOUSE_BUTTON, ui::EF_RIGHT_MOUSE_BUTTON)); @@ -311,7 +308,7 @@ TEST_F(CustomButtonTest, NotifyActionNoClick) { namespace { -void PerformGesture(CustomButton* button, ui::EventType event_type) { +void PerformGesture(Button* button, ui::EventType event_type) { ui::GestureEventDetails gesture_details(event_type); ui::GestureEvent gesture_event(0, 0, 0, base::TimeTicks(), gesture_details); button->OnGestureEvent(&gesture_event); @@ -320,61 +317,61 @@ void PerformGesture(CustomButton* button, ui::EventType event_type) { } // namespace // Tests that gesture events correctly change the button state. -TEST_F(CustomButtonTest, GestureEventsSetState) { +TEST_F(ButtonTest, GestureEventsSetState) { aura::test::TestCursorClient cursor_client( widget()->GetNativeView()->GetRootWindow()); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); PerformGesture(button(), ui::ET_GESTURE_TAP_DOWN); - EXPECT_EQ(CustomButton::STATE_PRESSED, button()->state()); + EXPECT_EQ(Button::STATE_PRESSED, button()->state()); PerformGesture(button(), ui::ET_GESTURE_SHOW_PRESS); - EXPECT_EQ(CustomButton::STATE_PRESSED, button()->state()); + EXPECT_EQ(Button::STATE_PRESSED, button()->state()); PerformGesture(button(), ui::ET_GESTURE_TAP_CANCEL); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); } #endif // !defined(OS_MACOSX) || defined(USE_AURA) -// Ensure subclasses of CustomButton are correctly recognized as CustomButton. -TEST_F(CustomButtonTest, AsCustomButton) { +// Ensure subclasses of Button are correctly recognized as Button. +TEST_F(ButtonTest, AsButton) { base::string16 text; LabelButton label_button(NULL, text); - EXPECT_TRUE(CustomButton::AsCustomButton(&label_button)); + EXPECT_TRUE(Button::AsButton(&label_button)); ImageButton image_button(NULL); - EXPECT_TRUE(CustomButton::AsCustomButton(&image_button)); + EXPECT_TRUE(Button::AsButton(&image_button)); Checkbox checkbox(text); - EXPECT_TRUE(CustomButton::AsCustomButton(&checkbox)); + EXPECT_TRUE(Button::AsButton(&checkbox)); RadioButton radio_button(text, 0); - EXPECT_TRUE(CustomButton::AsCustomButton(&radio_button)); + EXPECT_TRUE(Button::AsButton(&radio_button)); MenuButton menu_button(text, NULL, false); - EXPECT_TRUE(CustomButton::AsCustomButton(&menu_button)); + EXPECT_TRUE(Button::AsButton(&menu_button)); ToggleButton toggle_button(NULL); - EXPECT_TRUE(CustomButton::AsCustomButton(&toggle_button)); + EXPECT_TRUE(Button::AsButton(&toggle_button)); Label label; - EXPECT_FALSE(CustomButton::AsCustomButton(&label)); + EXPECT_FALSE(Button::AsButton(&label)); Link link(text); - EXPECT_FALSE(CustomButton::AsCustomButton(&link)); + EXPECT_FALSE(Button::AsButton(&link)); Textfield textfield; - EXPECT_FALSE(CustomButton::AsCustomButton(&textfield)); + EXPECT_FALSE(Button::AsButton(&textfield)); } // Tests that pressing a button shows the ink drop and releasing the button // does not hide the ink drop. -// Note: Ink drop is not hidden upon release because CustomButton descendants +// Note: Ink drop is not hidden upon release because Button descendants // may enter a different ink drop state. -TEST_F(CustomButtonTest, ButtonClickTogglesInkDrop) { +TEST_F(ButtonTest, ButtonClickTogglesInkDrop) { TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); @@ -389,7 +386,7 @@ TEST_F(CustomButtonTest, ButtonClickTogglesInkDrop) { // Tests that pressing a button shows and releasing capture hides ink drop. // Releasing capture should also reset PRESSED button state to NORMAL. -TEST_F(CustomButtonTest, CaptureLossHidesInkDrop) { +TEST_F(ButtonTest, CaptureLossHidesInkDrop) { TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); @@ -407,7 +404,7 @@ TEST_F(CustomButtonTest, CaptureLossHidesInkDrop) { EXPECT_EQ(Button::ButtonState::STATE_NORMAL, button()->state()); } -TEST_F(CustomButtonTest, HideInkDropWhenShowingContextMenu) { +TEST_F(ButtonTest, HideInkDropWhenShowingContextMenu) { TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); TestContextMenuController context_menu_controller; @@ -423,7 +420,7 @@ TEST_F(CustomButtonTest, HideInkDropWhenShowingContextMenu) { EXPECT_EQ(InkDropState::HIDDEN, ink_drop->GetTargetInkDropState()); } -TEST_F(CustomButtonTest, DontHideInkDropWhenShowingContextMenu) { +TEST_F(ButtonTest, DontHideInkDropWhenShowingContextMenu) { TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); TestContextMenuController context_menu_controller; @@ -439,7 +436,7 @@ TEST_F(CustomButtonTest, DontHideInkDropWhenShowingContextMenu) { EXPECT_EQ(InkDropState::ACTION_PENDING, ink_drop->GetTargetInkDropState()); } -TEST_F(CustomButtonTest, HideInkDropOnBlur) { +TEST_F(ButtonTest, HideInkDropOnBlur) { gfx::Point center(10, 10); TestInkDrop* ink_drop = new TestInkDrop(); @@ -461,7 +458,7 @@ TEST_F(CustomButtonTest, HideInkDropOnBlur) { EXPECT_TRUE(button()->pressed()); } -TEST_F(CustomButtonTest, HideInkDropHighlightOnDisable) { +TEST_F(ButtonTest, HideInkDropHighlightOnDisable) { TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); @@ -474,7 +471,7 @@ TEST_F(CustomButtonTest, HideInkDropHighlightOnDisable) { EXPECT_TRUE(ink_drop->is_hovered()); } -TEST_F(CustomButtonTest, InkDropAfterTryingToShowContextMenu) { +TEST_F(ButtonTest, InkDropAfterTryingToShowContextMenu) { TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); button()->set_context_menu_controller(nullptr); @@ -490,13 +487,13 @@ TEST_F(CustomButtonTest, InkDropAfterTryingToShowContextMenu) { // Tests that when button is set to notify on release, dragging mouse out and // back transitions ink drop states correctly. -TEST_F(CustomButtonTest, InkDropShowHideOnMouseDraggedNotifyOnRelease) { +TEST_F(ButtonTest, InkDropShowHideOnMouseDraggedNotifyOnRelease) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), false); - button()->set_notify_action(CustomButton::NOTIFY_ON_RELEASE); + button()->set_notify_action(Button::NOTIFY_ON_RELEASE); button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), @@ -531,13 +528,13 @@ TEST_F(CustomButtonTest, InkDropShowHideOnMouseDraggedNotifyOnRelease) { // Tests that when button is set to notify on press, dragging mouse out and back // does not change the ink drop state. -TEST_F(CustomButtonTest, InkDropShowHideOnMouseDraggedNotifyOnPress) { +TEST_F(ButtonTest, InkDropShowHideOnMouseDraggedNotifyOnPress) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); TestInkDrop* ink_drop = new TestInkDrop(); CreateButtonWithInkDrop(base::WrapUnique(ink_drop), true); - button()->set_notify_action(CustomButton::NOTIFY_ON_PRESS); + button()->set_notify_action(Button::NOTIFY_ON_PRESS); button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), @@ -571,7 +568,7 @@ TEST_F(CustomButtonTest, InkDropShowHideOnMouseDraggedNotifyOnPress) { EXPECT_EQ(InkDropState::ACTION_TRIGGERED, ink_drop->GetTargetInkDropState()); } -TEST_F(CustomButtonTest, InkDropStaysHiddenWhileDragging) { +TEST_F(ButtonTest, InkDropStaysHiddenWhileDragging) { gfx::Point center(10, 10); gfx::Point oob(-1, -1); @@ -607,7 +604,7 @@ TEST_F(CustomButtonTest, InkDropStaysHiddenWhileDragging) { // Test that hiding or closing a Widget doesn't attempt to add a layer due to // changed visibility states. -TEST_F(CustomButtonTest, NoLayerAddedForWidgetVisibilityChanges) { +TEST_F(ButtonTest, NoLayerAddedForWidgetVisibilityChanges) { CreateButtonWithRealInkDrop(); EXPECT_TRUE(button()->visible()); @@ -640,7 +637,7 @@ TEST_F(CustomButtonTest, NoLayerAddedForWidgetVisibilityChanges) { // Verify that the Space key clicks the button on key-press on Mac, and // key-release on other platforms. -TEST_F(CustomButtonTest, ActionOnSpace) { +TEST_F(ButtonTest, ActionOnSpace) { // Give focus to the button. button()->SetFocusForPlatform(); button()->RequestFocus(); @@ -650,10 +647,10 @@ TEST_F(CustomButtonTest, ActionOnSpace) { EXPECT_TRUE(button()->OnKeyPressed(space_press)); #if defined(OS_MACOSX) - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); EXPECT_TRUE(button()->pressed()); #else - EXPECT_EQ(CustomButton::STATE_PRESSED, button()->state()); + EXPECT_EQ(Button::STATE_PRESSED, button()->state()); EXPECT_FALSE(button()->pressed()); #endif @@ -665,14 +662,14 @@ TEST_F(CustomButtonTest, ActionOnSpace) { EXPECT_TRUE(button()->OnKeyReleased(space_release)); #endif - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); EXPECT_TRUE(button()->pressed()); } // Verify that the Return key clicks the button on key-press on all platforms // except Mac. On Mac, the Return key performs the default action associated // with a dialog, even if a button has focus. -TEST_F(CustomButtonTest, ActionOnReturn) { +TEST_F(ButtonTest, ActionOnReturn) { // Give focus to the button. button()->SetFocusForPlatform(); button()->RequestFocus(); @@ -682,11 +679,11 @@ TEST_F(CustomButtonTest, ActionOnReturn) { #if defined(OS_MACOSX) EXPECT_FALSE(button()->OnKeyPressed(return_press)); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); EXPECT_FALSE(button()->pressed()); #else EXPECT_TRUE(button()->OnKeyPressed(return_press)); - EXPECT_EQ(CustomButton::STATE_NORMAL, button()->state()); + EXPECT_EQ(Button::STATE_NORMAL, button()->state()); EXPECT_TRUE(button()->pressed()); #endif diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc index d3535f0f548..14dc8b31154 100644 --- a/chromium/ui/views/controls/button/checkbox.cc +++ b/chromium/ui/views/controls/button/checkbox.cc @@ -23,10 +23,13 @@ #include "ui/views/painter.h" #include "ui/views/resources/grit/views_resources.h" #include "ui/views/style/platform_style.h" +#include "ui/views/style/typography.h" #include "ui/views/vector_icons.h" namespace views { +constexpr int kFocusRingThicknessDip = 2; + // View used to paint the focus ring around the Checkbox icon. // The icon is painted separately. class IconFocusRing : public View { @@ -50,7 +53,7 @@ IconFocusRing::IconFocusRing(Checkbox* checkbox) : checkbox_(checkbox) { void IconFocusRing::Layout() { gfx::Rect focus_bounds = checkbox_->image()->bounds(); - focus_bounds.Inset(gfx::Insets(-2.f)); + focus_bounds.Inset(-kFocusRingThicknessDip, -kFocusRingThicknessDip); SetBoundsRect(focus_bounds); } @@ -205,18 +208,15 @@ std::unique_ptr<InkDropRipple> Checkbox::CreateInkDropRipple() const { } SkColor Checkbox::GetInkDropBaseColor() const { - return GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_LabelEnabledColor); + // Usually ink drop ripples match the text color. Checkboxes use the color of + // the unchecked icon. + return GetIconImageColor(false); } gfx::ImageSkia Checkbox::GetImage(ButtonState for_state) const { if (UseMd()) { - return gfx::CreateVectorIcon( - GetVectorIcon(), 16, - // When not checked, the icon color matches the button text color. - GetNativeTheme()->GetSystemColor( - checked_ ? ui::NativeTheme::kColorId_FocusedBorderColor - : ui::NativeTheme::kColorId_LabelEnabledColor)); + return gfx::CreateVectorIcon(GetVectorIcon(), 16, + GetIconImageColor(checked_)); } const size_t checked_index = checked_ ? 1 : 0; @@ -248,13 +248,26 @@ void Checkbox::SetCustomImage(bool checked, void Checkbox::PaintFocusRing(View* view, gfx::Canvas* canvas, const cc::PaintFlags& flags) { - canvas->DrawRoundRect(view->GetLocalBounds(), 2.f, flags); + gfx::RectF bounds(view->GetLocalBounds()); + bounds.Inset(kFocusRingThicknessDip, kFocusRingThicknessDip); + canvas->DrawRoundRect(bounds, kFocusRingThicknessDip, flags); } const gfx::VectorIcon& Checkbox::GetVectorIcon() const { return checked() ? kCheckboxActiveIcon : kCheckboxNormalIcon; } +SkColor Checkbox::GetIconImageColor(bool checked) const { + DCHECK(UseMd()); + const ui::NativeTheme* theme = GetNativeTheme(); + return checked + ? theme->GetSystemColor( + ui::NativeTheme::kColorId_FocusedBorderColor) + // When unchecked, the icon color matches push button text color. + : style::GetColor(style::CONTEXT_BUTTON_MD, style::STYLE_PRIMARY, + theme); +} + void Checkbox::NotifyClick(const ui::Event& event) { SetChecked(!checked()); LabelButton::NotifyClick(event); diff --git a/chromium/ui/views/controls/button/checkbox.h b/chromium/ui/views/controls/button/checkbox.h index deb26a213f8..d260a8692ba 100644 --- a/chromium/ui/views/controls/button/checkbox.h +++ b/chromium/ui/views/controls/button/checkbox.h @@ -70,6 +70,8 @@ class VIEWS_EXPORT Checkbox : public LabelButton { private: friend class IconFocusRing; + SkColor GetIconImageColor(bool checked) const; + // Button: void NotifyClick(const ui::Event& event) override; diff --git a/chromium/ui/views/controls/button/custom_button.cc b/chromium/ui/views/controls/button/custom_button.cc deleted file mode 100644 index 95e8aad340b..00000000000 --- a/chromium/ui/views/controls/button/custom_button.cc +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/controls/button/custom_button.h" - -#include "ui/accessibility/ax_node_data.h" -#include "ui/events/event.h" -#include "ui/events/event_utils.h" -#include "ui/events/keycodes/keyboard_codes.h" -#include "ui/gfx/animation/throb_animation.h" -#include "ui/gfx/color_palette.h" -#include "ui/native_theme/native_theme.h" -#include "ui/views/animation/ink_drop_highlight.h" -#include "ui/views/animation/ink_drop_impl.h" -#include "ui/views/controls/button/blue_button.h" -#include "ui/views/controls/button/checkbox.h" -#include "ui/views/controls/button/image_button.h" -#include "ui/views/controls/button/label_button.h" -#include "ui/views/controls/button/menu_button.h" -#include "ui/views/controls/button/radio_button.h" -#include "ui/views/controls/button/toggle_button.h" -#include "ui/views/painter.h" -#include "ui/views/style/platform_style.h" -#include "ui/views/widget/widget.h" - -#if defined(USE_AURA) -#include "ui/aura/client/capture_client.h" -#include "ui/aura/window.h" -#endif - -namespace views { - -namespace { - -// How long the hover animation takes if uninterrupted. -const int kHoverFadeDurationMs = 150; - -CustomButton::KeyClickAction GetKeyClickActionForEvent( - const ui::KeyEvent& event) { - if (event.key_code() == ui::VKEY_SPACE) - return PlatformStyle::kKeyClickActionOnSpace; - if (event.key_code() == ui::VKEY_RETURN && - PlatformStyle::kReturnClicksFocusedControl) - return CustomButton::CLICK_ON_KEY_PRESS; - return CustomButton::CLICK_NONE; -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// CustomButton, public: - -// static -const char CustomButton::kViewClassName[] = "CustomButton"; - -// static -const CustomButton* CustomButton::AsCustomButton(const views::View* view) { - return AsCustomButton(const_cast<View*>(view)); -} - -// static -CustomButton* CustomButton::AsCustomButton(views::View* view) { - if (view) { - const char* classname = view->GetClassName(); - if (!strcmp(classname, Checkbox::kViewClassName) || - !strcmp(classname, CustomButton::kViewClassName) || - !strcmp(classname, ImageButton::kViewClassName) || - !strcmp(classname, LabelButton::kViewClassName) || - !strcmp(classname, RadioButton::kViewClassName) || - !strcmp(classname, ToggleButton::kViewClassName) || - !strcmp(classname, MenuButton::kViewClassName)) { - return static_cast<CustomButton*>(view); - } - } - return NULL; -} - -CustomButton::~CustomButton() {} - -void CustomButton::SetState(ButtonState state) { - if (state == state_) - return; - - if (animate_on_state_change_ && - (!is_throbbing_ || !hover_animation_.is_animating())) { - is_throbbing_ = false; - if ((state_ == STATE_HOVERED) && (state == STATE_NORMAL)) { - // For HOVERED -> NORMAL, animate from hovered (1) to not hovered (0). - hover_animation_.Hide(); - } else if (state != STATE_HOVERED) { - // For HOVERED -> PRESSED/DISABLED, or any transition not involving - // HOVERED at all, simply set the state to not hovered (0). - hover_animation_.Reset(); - } else if (state_ == STATE_NORMAL) { - // For NORMAL -> HOVERED, animate from not hovered (0) to hovered (1). - hover_animation_.Show(); - } else { - // For PRESSED/DISABLED -> HOVERED, simply set the state to hovered (1). - hover_animation_.Reset(1); - } - } - - ButtonState old_state = state_; - state_ = state; - StateChanged(old_state); - SchedulePaint(); -} - -void CustomButton::StartThrobbing(int cycles_til_stop) { - if (!animate_on_state_change_) - return; - is_throbbing_ = true; - hover_animation_.StartThrobbing(cycles_til_stop); -} - -void CustomButton::StopThrobbing() { - if (hover_animation_.is_animating()) { - hover_animation_.Stop(); - SchedulePaint(); - } -} - -void CustomButton::SetAnimationDuration(int duration) { - hover_animation_.SetSlideDuration(duration); -} - -void CustomButton::SetHotTracked(bool is_hot_tracked) { - if (state_ != STATE_DISABLED) - SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL); - - if (is_hot_tracked) - NotifyAccessibilityEvent(ui::AX_EVENT_HOVER, true); -} - -bool CustomButton::IsHotTracked() const { - return state_ == STATE_HOVERED; -} - -void CustomButton::SetFocusPainter(std::unique_ptr<Painter> focus_painter) { - focus_painter_ = std::move(focus_painter); -} - -//////////////////////////////////////////////////////////////////////////////// -// CustomButton, View overrides: - -void CustomButton::OnEnabledChanged() { - // TODO(bruthig): Is there any reason we are not calling - // Button::OnEnabledChanged() here? - if (enabled() ? (state_ != STATE_DISABLED) : (state_ == STATE_DISABLED)) - return; - - if (enabled()) { - bool should_enter_hover_state = ShouldEnterHoveredState(); - SetState(should_enter_hover_state ? STATE_HOVERED : STATE_NORMAL); - GetInkDrop()->SetHovered(should_enter_hover_state); - } else { - SetState(STATE_DISABLED); - GetInkDrop()->SetHovered(false); - } -} - -const char* CustomButton::GetClassName() const { - return kViewClassName; -} - -bool CustomButton::OnMousePressed(const ui::MouseEvent& event) { - if (state_ == STATE_DISABLED) - return true; - if (state_ != STATE_PRESSED && ShouldEnterPushedState(event) && - HitTestPoint(event.location())) { - SetState(STATE_PRESSED); - AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event); - } - if (request_focus_on_press_) - RequestFocus(); - if (IsTriggerableEvent(event) && notify_action_ == NOTIFY_ON_PRESS) { - NotifyClick(event); - // NOTE: We may be deleted at this point (by the listener's notification - // handler). - } - return true; -} - -bool CustomButton::OnMouseDragged(const ui::MouseEvent& event) { - if (state_ != STATE_DISABLED) { - const bool should_enter_pushed = ShouldEnterPushedState(event); - const bool should_show_pending = - should_enter_pushed && notify_action_ == NOTIFY_ON_RELEASE && !InDrag(); - if (HitTestPoint(event.location())) { - SetState(should_enter_pushed ? STATE_PRESSED : STATE_HOVERED); - if (should_show_pending && - GetInkDrop()->GetTargetInkDropState() == - views::InkDropState::HIDDEN) { - AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event); - } - } else { - SetState(STATE_NORMAL); - if (should_show_pending && - GetInkDrop()->GetTargetInkDropState() == - views::InkDropState::ACTION_PENDING) { - AnimateInkDrop(views::InkDropState::HIDDEN, &event); - } - } - } - return true; -} - -void CustomButton::OnMouseReleased(const ui::MouseEvent& event) { - if (state_ != STATE_DISABLED) { - if (!HitTestPoint(event.location())) { - SetState(STATE_NORMAL); - } else { - SetState(STATE_HOVERED); - if (IsTriggerableEvent(event) && notify_action_ == NOTIFY_ON_RELEASE) { - NotifyClick(event); - // NOTE: We may be deleted at this point (by the listener's notification - // handler). - return; - } - } - } - if (notify_action_ == NOTIFY_ON_RELEASE) - OnClickCanceled(event); -} - -void CustomButton::OnMouseCaptureLost() { - // Starting a drag results in a MouseCaptureLost. Reset button state. - // TODO(varkha): Reset the state even while in drag. The same logic may - // applies everywhere so gather any feedback and update. - if (state_ != STATE_DISABLED) - SetState(STATE_NORMAL); - AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */); - GetInkDrop()->SetHovered(false); - Button::OnMouseCaptureLost(); -} - -void CustomButton::OnMouseEntered(const ui::MouseEvent& event) { - if (state_ != STATE_DISABLED) - SetState(STATE_HOVERED); -} - -void CustomButton::OnMouseExited(const ui::MouseEvent& event) { - // Starting a drag results in a MouseExited, we need to ignore it. - if (state_ != STATE_DISABLED && !InDrag()) - SetState(STATE_NORMAL); -} - -void CustomButton::OnMouseMoved(const ui::MouseEvent& event) { - if (state_ != STATE_DISABLED) - SetState(HitTestPoint(event.location()) ? STATE_HOVERED : STATE_NORMAL); -} - -bool CustomButton::OnKeyPressed(const ui::KeyEvent& event) { - if (state_ == STATE_DISABLED) - return false; - - switch (GetKeyClickActionForEvent(event)) { - case KeyClickAction::CLICK_ON_KEY_RELEASE: - SetState(STATE_PRESSED); - if (GetInkDrop()->GetTargetInkDropState() != - InkDropState::ACTION_PENDING) { - AnimateInkDrop(InkDropState::ACTION_PENDING, nullptr /* event */); - } - return true; - case KeyClickAction::CLICK_ON_KEY_PRESS: - SetState(STATE_NORMAL); - NotifyClick(event); - return true; - case KeyClickAction::CLICK_NONE: - return false; - } - - NOTREACHED(); - return false; -} - -bool CustomButton::OnKeyReleased(const ui::KeyEvent& event) { - const bool click_button = - state_ == STATE_PRESSED && - GetKeyClickActionForEvent(event) == KeyClickAction::CLICK_ON_KEY_RELEASE; - if (!click_button) - return false; - - SetState(STATE_NORMAL); - NotifyClick(event); - return true; -} - -void CustomButton::OnGestureEvent(ui::GestureEvent* event) { - if (state_ == STATE_DISABLED) { - Button::OnGestureEvent(event); - return; - } - - if (event->type() == ui::ET_GESTURE_TAP && IsTriggerableEvent(*event)) { - // Set the button state to hot and start the animation fully faded in. The - // GESTURE_END event issued immediately after will set the state to - // STATE_NORMAL beginning the fade out animation. See - // http://crbug.com/131184. - SetState(STATE_HOVERED); - hover_animation_.Reset(1.0); - NotifyClick(*event); - event->StopPropagation(); - } else if (event->type() == ui::ET_GESTURE_TAP_DOWN && - ShouldEnterPushedState(*event)) { - SetState(STATE_PRESSED); - if (request_focus_on_press_) - RequestFocus(); - event->StopPropagation(); - } else if (event->type() == ui::ET_GESTURE_TAP_CANCEL || - event->type() == ui::ET_GESTURE_END) { - SetState(STATE_NORMAL); - } - if (!event->handled()) - Button::OnGestureEvent(event); -} - -bool CustomButton::AcceleratorPressed(const ui::Accelerator& accelerator) { - SetState(STATE_NORMAL); - NotifyClick(accelerator.ToKeyEvent()); - return true; -} - -bool CustomButton::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { - // If this button is focused and the user presses space or enter, don't let - // that be treated as an accelerator if there is a key click action - // corresponding to it. - return GetKeyClickActionForEvent(event) != KeyClickAction::CLICK_NONE; -} - -void CustomButton::ShowContextMenu(const gfx::Point& p, - ui::MenuSourceType source_type) { - if (!context_menu_controller()) - return; - - // We're about to show the context menu. Showing the context menu likely means - // we won't get a mouse exited and reset state. Reset it now to be sure. - if (state_ != STATE_DISABLED) - SetState(STATE_NORMAL); - if (hide_ink_drop_when_showing_context_menu_) { - GetInkDrop()->SetHovered(false); - AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */); - } - View::ShowContextMenu(p, source_type); -} - -void CustomButton::OnDragDone() { - // Only reset the state to normal if the button isn't currently disabled - // (since disabled buttons may still be able to be dragged). - if (state_ != STATE_DISABLED) - SetState(STATE_NORMAL); - AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */); -} - -void CustomButton::OnPaint(gfx::Canvas* canvas) { - Button::OnPaint(canvas); - PaintButtonContents(canvas); - Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); -} - -void CustomButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { - Button::GetAccessibleNodeData(node_data); - switch (state_) { - case STATE_HOVERED: - node_data->AddState(ui::AX_STATE_HOVERED); - break; - case STATE_PRESSED: - node_data->AddIntAttribute(ui::AX_ATTR_CHECKED_STATE, - ui::AX_CHECKED_STATE_TRUE); - break; - case STATE_DISABLED: - node_data->AddIntAttribute(ui::AX_ATTR_RESTRICTION, - ui::AX_RESTRICTION_DISABLED); - break; - case STATE_NORMAL: - case STATE_COUNT: - // No additional accessibility node_data set for this button node_data. - break; - } - if (enabled()) { - node_data->AddIntAttribute(ui::AX_ATTR_DEFAULT_ACTION_VERB, - ui::AX_DEFAULT_ACTION_VERB_PRESS); - } -} - -void CustomButton::VisibilityChanged(View* starting_from, bool visible) { - Button::VisibilityChanged(starting_from, visible); - if (state_ == STATE_DISABLED) - return; - SetState(visible && ShouldEnterHoveredState() ? STATE_HOVERED : STATE_NORMAL); -} - -std::unique_ptr<InkDrop> CustomButton::CreateInkDrop() { - std::unique_ptr<views::InkDropImpl> ink_drop = CreateDefaultInkDropImpl(); - ink_drop->SetShowHighlightOnFocus(true); - return std::move(ink_drop); -} - -SkColor CustomButton::GetInkDropBaseColor() const { - return ink_drop_base_color_; -} - -//////////////////////////////////////////////////////////////////////////////// -// CustomButton, gfx::AnimationDelegate implementation: - -void CustomButton::AnimationProgressed(const gfx::Animation* animation) { - SchedulePaint(); -} - -//////////////////////////////////////////////////////////////////////////////// -// CustomButton, View overrides (public): - -void CustomButton::ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) { - if (!details.is_add && state_ != STATE_DISABLED) - SetState(STATE_NORMAL); -} - -void CustomButton::OnFocus() { - Button::OnFocus(); - if (focus_painter_) - SchedulePaint(); -} - -void CustomButton::OnBlur() { - Button::OnBlur(); - if (IsHotTracked() || state_ == STATE_PRESSED) { - SetState(STATE_NORMAL); - if (GetInkDrop()->GetTargetInkDropState() != views::InkDropState::HIDDEN) - AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */); - // TODO(bruthig) : Fix CustomButtons to work well when multiple input - // methods are interacting with a button. e.g. By animating to HIDDEN here - // it is possible for a Mouse Release to trigger an action however there - // would be no visual cue to the user that this will occur. - } - if (focus_painter_) - SchedulePaint(); -} - -//////////////////////////////////////////////////////////////////////////////// -// CustomButton, protected: - -CustomButton::CustomButton(ButtonListener* listener) - : Button(listener), - state_(STATE_NORMAL), - hover_animation_(this), - is_throbbing_(false), - triggerable_event_flags_(ui::EF_LEFT_MOUSE_BUTTON), - request_focus_on_press_(false), - notify_action_(NOTIFY_ON_RELEASE), - has_ink_drop_action_on_click_(false), - hide_ink_drop_when_showing_context_menu_(true), - ink_drop_base_color_(gfx::kPlaceholderColor) { - hover_animation_.SetSlideDuration(kHoverFadeDurationMs); -} - -void CustomButton::StateChanged(ButtonState old_state) {} - -bool CustomButton::IsTriggerableEvent(const ui::Event& event) { - return event.type() == ui::ET_GESTURE_TAP_DOWN || - event.type() == ui::ET_GESTURE_TAP || - (event.IsMouseEvent() && - (triggerable_event_flags_ & event.flags()) != 0); -} - -bool CustomButton::ShouldUpdateInkDropOnClickCanceled() const { - return true; -} - -bool CustomButton::ShouldEnterPushedState(const ui::Event& event) { - return IsTriggerableEvent(event); -} - -void CustomButton::PaintButtonContents(gfx::Canvas* canvas) {} - -bool CustomButton::ShouldEnterHoveredState() { - if (!visible()) - return false; - - bool check_mouse_position = true; -#if defined(USE_AURA) - // If another window has capture, we shouldn't check the current mouse - // position because the button won't receive any mouse events - so if the - // mouse was hovered, the button would be stuck in a hovered state (since it - // would never receive OnMouseExited). - const Widget* widget = GetWidget(); - if (widget && widget->GetNativeWindow()) { - aura::Window* root_window = widget->GetNativeWindow()->GetRootWindow(); - aura::client::CaptureClient* capture_client = - aura::client::GetCaptureClient(root_window); - aura::Window* capture_window = - capture_client ? capture_client->GetGlobalCaptureWindow() : nullptr; - check_mouse_position = !capture_window || capture_window == root_window; - } -#endif - - return check_mouse_position && IsMouseHovered(); -} - -//////////////////////////////////////////////////////////////////////////////// -// CustomButton, Button overrides (protected): - -void CustomButton::NotifyClick(const ui::Event& event) { - if (has_ink_drop_action_on_click_) { - AnimateInkDrop(InkDropState::ACTION_TRIGGERED, - ui::LocatedEvent::FromIfValid(&event)); - } - Button::NotifyClick(event); -} - -void CustomButton::OnClickCanceled(const ui::Event& event) { - if (ShouldUpdateInkDropOnClickCanceled()) { - if (GetInkDrop()->GetTargetInkDropState() == - views::InkDropState::ACTION_PENDING || - GetInkDrop()->GetTargetInkDropState() == - views::InkDropState::ALTERNATE_ACTION_PENDING) { - AnimateInkDrop(views::InkDropState::HIDDEN, - ui::LocatedEvent::FromIfValid(&event)); - } - } - Button::OnClickCanceled(event); -} - -} // namespace views diff --git a/chromium/ui/views/controls/button/custom_button.h b/chromium/ui/views/controls/button/custom_button.h deleted file mode 100644 index b76b2f87a54..00000000000 --- a/chromium/ui/views/controls/button/custom_button.h +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_ -#define UI_VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_ - -#include <memory> - -#include "base/macros.h" -#include "build/build_config.h" -#include "ui/events/event_constants.h" -#include "ui/gfx/animation/animation_delegate.h" -#include "ui/gfx/animation/throb_animation.h" -#include "ui/views/animation/ink_drop_state.h" -#include "ui/views/controls/button/button.h" - -namespace views { - -class Painter; - -// A button with custom rendering. The base of ImageButton and LabelButton. -// Note that this type of button is not focusable by default and will not be -// part of the focus chain, unless in accessibility mode. Call -// SetFocusForPlatform() to make it part of the focus chain. -class VIEWS_EXPORT CustomButton : public Button, public gfx::AnimationDelegate { - public: - // An enum describing the events on which a button should notify its listener. - enum NotifyAction { - NOTIFY_ON_PRESS, - NOTIFY_ON_RELEASE, - }; - - // An enum describing the events on which a button should be clicked for a - // given key event. - enum KeyClickAction { - CLICK_ON_KEY_PRESS, - CLICK_ON_KEY_RELEASE, - CLICK_NONE, - }; - - // The menu button's class name. - static const char kViewClassName[]; - - static const CustomButton* AsCustomButton(const View* view); - static CustomButton* AsCustomButton(View* view); - - ~CustomButton() override; - - // Get/sets the current display state of the button. - ButtonState state() const { return state_; } - // Clients passing in STATE_DISABLED should consider calling - // SetEnabled(false) instead because the enabled flag can affect other things - // like event dispatching, focus traversals, etc. Calling SetEnabled(false) - // will also set the state of |this| to STATE_DISABLED. - void SetState(ButtonState state); - - // Starts throbbing. See HoverAnimation for a description of cycles_til_stop. - // This method does nothing if |animate_on_state_change_| is false. - void StartThrobbing(int cycles_til_stop); - - // Stops throbbing immediately. - void StopThrobbing(); - - // Set how long the hover animation will last for. - void SetAnimationDuration(int duration); - - void set_triggerable_event_flags(int triggerable_event_flags) { - triggerable_event_flags_ = triggerable_event_flags; - } - int triggerable_event_flags() const { return triggerable_event_flags_; } - - // Sets whether |RequestFocus| should be invoked on a mouse press. The default - // is false. - void set_request_focus_on_press(bool value) { -// On Mac, buttons should not request focus on a mouse press. Hence keep the -// default value i.e. false. -#if !defined(OS_MACOSX) - request_focus_on_press_ = value; -#endif - } - - bool request_focus_on_press() const { return request_focus_on_press_; } - - // See description above field. - void set_animate_on_state_change(bool value) { - animate_on_state_change_ = value; - } - - // Sets the event on which the button should notify its listener. - void set_notify_action(NotifyAction notify_action) { - notify_action_ = notify_action; - } - - void set_hide_ink_drop_when_showing_context_menu( - bool hide_ink_drop_when_showing_context_menu) { - hide_ink_drop_when_showing_context_menu_ = - hide_ink_drop_when_showing_context_menu; - } - - void set_ink_drop_base_color(SkColor color) { ink_drop_base_color_ = color; } - void set_has_ink_drop_action_on_click(bool has_ink_drop_action_on_click) { - has_ink_drop_action_on_click_ = has_ink_drop_action_on_click; - } - - void SetHotTracked(bool is_hot_tracked); - bool IsHotTracked() const; - - void SetFocusPainter(std::unique_ptr<Painter> focus_painter); - - // Overridden from View: - void OnEnabledChanged() override; - const char* GetClassName() const override; - bool OnMousePressed(const ui::MouseEvent& event) override; - bool OnMouseDragged(const ui::MouseEvent& event) override; - void OnMouseReleased(const ui::MouseEvent& event) override; - void OnMouseCaptureLost() override; - void OnMouseEntered(const ui::MouseEvent& event) override; - void OnMouseExited(const ui::MouseEvent& event) override; - void OnMouseMoved(const ui::MouseEvent& event) override; - bool OnKeyPressed(const ui::KeyEvent& event) override; - bool OnKeyReleased(const ui::KeyEvent& event) override; - void OnGestureEvent(ui::GestureEvent* event) override; - bool AcceleratorPressed(const ui::Accelerator& accelerator) override; - bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override; - void ShowContextMenu(const gfx::Point& p, - ui::MenuSourceType source_type) override; - void OnDragDone() override; - // Instead of overriding this, subclasses that want custom painting should use - // PaintButtonContents. - void OnPaint(gfx::Canvas* canvas) final; - void GetAccessibleNodeData(ui::AXNodeData* node_data) override; - void VisibilityChanged(View* starting_from, bool is_visible) override; - - // Overridden from InkDropHostView: - std::unique_ptr<InkDrop> CreateInkDrop() override; - SkColor GetInkDropBaseColor() const override; - - // Overridden from gfx::AnimationDelegate: - void AnimationProgressed(const gfx::Animation* animation) override; - - // Overridden from View: - void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) override; - void OnFocus() override; - void OnBlur() override; - - protected: - // Construct the Button with a Listener. See comment for Button's ctor. - explicit CustomButton(ButtonListener* listener); - - // Invoked from SetState() when SetState() is passed a value that differs from - // the current node_data. CustomButton's implementation of StateChanged() does - // nothing; this method is provided for subclasses that wish to do something - // on state changes. - virtual void StateChanged(ButtonState old_state); - - // Returns true if the event is one that can trigger notifying the listener. - // This implementation returns true if the left mouse button is down. - virtual bool IsTriggerableEvent(const ui::Event& event); - - // Returns true if the ink drop should be updated by CustomButton when - // OnClickCanceled() is called. This method is provided for subclasses. - // If the method is overriden and returns false, the subclass is responsible - // will be responsible for updating the ink drop. - virtual bool ShouldUpdateInkDropOnClickCanceled() const; - - // Returns true if the button should become pressed when the user - // holds the mouse down over the button. For this implementation, - // we simply return IsTriggerableEvent(event). - virtual bool ShouldEnterPushedState(const ui::Event& event); - - // Override to paint custom button contents. Any background or border set on - // the view will be painted before this is called and |focus_painter_| will be - // painted afterwards. - virtual void PaintButtonContents(gfx::Canvas* canvas); - - // Returns true if the button should enter hovered state; that is, if the - // mouse is over the button, and no other window has capture (which would - // prevent the button from receiving MouseExited events and updating its - // node_data). This does not take into account enabled node_data. - bool ShouldEnterHoveredState(); - - // Overridden from Button: - void NotifyClick(const ui::Event& event) override; - void OnClickCanceled(const ui::Event& event) override; - - const gfx::ThrobAnimation& hover_animation() const { - return hover_animation_; - } - - private: - FRIEND_TEST_ALL_PREFIXES(BlueButtonTest, Border); - - ButtonState state_; - - gfx::ThrobAnimation hover_animation_; - - // Should we animate when the state changes? - bool animate_on_state_change_ = false; - - // Is the hover animation running because StartThrob was invoked? - bool is_throbbing_; - - // Mouse event flags which can trigger button actions. - int triggerable_event_flags_; - - // See description above setter. - bool request_focus_on_press_; - - // The event on which the button should notify its listener. - NotifyAction notify_action_; - - // True when a button click should trigger an animation action on - // ink_drop_delegate(). - bool has_ink_drop_action_on_click_; - - // When true, the ink drop ripple and hover will be hidden prior to showing - // the context menu. - bool hide_ink_drop_when_showing_context_menu_; - - // The color of the ripple and hover. - SkColor ink_drop_base_color_; - - std::unique_ptr<Painter> focus_painter_; - - DISALLOW_COPY_AND_ASSIGN(CustomButton); -}; - -} // namespace views - -#endif // UI_VIEWS_CONTROLS_BUTTON_CUSTOM_BUTTON_H_ diff --git a/chromium/ui/views/controls/button/image_button.cc b/chromium/ui/views/controls/button/image_button.cc index 49e6d5fcf23..86da89c1729 100644 --- a/chromium/ui/views/controls/button/image_button.cc +++ b/chromium/ui/views/controls/button/image_button.cc @@ -29,7 +29,7 @@ const char ImageButton::kViewClassName[] = "ImageButton"; // ImageButton, public: ImageButton::ImageButton(ButtonListener* listener) - : CustomButton(listener), + : Button(listener), h_alignment_(ALIGN_LEFT), v_alignment_(ALIGN_TOP), draw_image_mirrored_(false) { @@ -112,6 +112,19 @@ gfx::Size ImageButton::CalculatePreferredSize() const { return size; } +views::PaintInfo::ScaleType ImageButton::GetPaintScaleType() const { + // ImageButton contains an image which is rastered at the device scale factor. + // By default, the paint commands are recorded at a scale factor slighlty + // different from the device scale factor. Re-rastering the image at this + // paint recording scale will result in a distorted image. Paint recording + // scale might also not be uniform along the x and y axis, thus resulting in + // further distortion in the aspect ratio of the final image. + // |kUniformScaling| ensures that the paint recording scale is uniform along + // the x & y axis and keeps the scale equal to the device scale factor. + // See http://crbug.com/754010 for more details. + return views::PaintInfo::ScaleType::kUniformScaling; +} + void ImageButton::PaintButtonContents(gfx::Canvas* canvas) { // TODO(estade|tdanderson|bruthig): The ink drop layer should be positioned // behind the button's image which means the image needs to be painted to its diff --git a/chromium/ui/views/controls/button/image_button.h b/chromium/ui/views/controls/button/image_button.h index 419a5176eb0..61c0ca338c1 100644 --- a/chromium/ui/views/controls/button/image_button.h +++ b/chromium/ui/views/controls/button/image_button.h @@ -11,7 +11,7 @@ #include "base/macros.h" #include "ui/base/layout.h" #include "ui/gfx/image/image_skia.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" namespace views { @@ -19,7 +19,7 @@ namespace views { // Note that this type of button is not focusable by default and will not be // part of the focus chain, unless in accessibility mode. Call // SetFocusForPlatform() to make it part of the focus chain. -class VIEWS_EXPORT ImageButton : public CustomButton { +class VIEWS_EXPORT ImageButton : public Button { public: static const char kViewClassName[]; @@ -72,9 +72,10 @@ class VIEWS_EXPORT ImageButton : public CustomButton { // Overridden from View: const char* GetClassName() const override; gfx::Size CalculatePreferredSize() const override; + views::PaintInfo::ScaleType GetPaintScaleType() const override; protected: - // Overridden from CustomButton: + // Overridden from Button: void PaintButtonContents(gfx::Canvas* canvas) override; // Returns the image to paint. This is invoked from paint and returns a value diff --git a/chromium/ui/views/controls/button/image_button_factory.cc b/chromium/ui/views/controls/button/image_button_factory.cc index e1cafa904e0..6e7ddda2763 100644 --- a/chromium/ui/views/controls/button/image_button_factory.cc +++ b/chromium/ui/views/controls/button/image_button_factory.cc @@ -8,7 +8,7 @@ #include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/vector_icon_types.h" #include "ui/views/border.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/painter.h" @@ -17,7 +17,7 @@ namespace views { ImageButton* CreateVectorImageButton(ButtonListener* listener) { ImageButton* button = new ImageButton(listener); - button->SetInkDropMode(CustomButton::InkDropMode::ON); + button->SetInkDropMode(Button::InkDropMode::ON); button->set_has_ink_drop_action_on_click(true); button->SetImageAlignment(ImageButton::ALIGN_CENTER, ImageButton::ALIGN_MIDDLE); @@ -33,9 +33,9 @@ void SetImageFromVectorIcon(ImageButton* button, const SkColor icon_color = color_utils::DeriveDefaultIconColor(related_text_color); const SkColor disabled_color = SkColorSetA(icon_color, 0xff / 2); - button->SetImage(CustomButton::STATE_NORMAL, + button->SetImage(Button::STATE_NORMAL, gfx::CreateVectorIcon(icon, icon_color)); - button->SetImage(CustomButton::STATE_DISABLED, + button->SetImage(Button::STATE_DISABLED, gfx::CreateVectorIcon(icon, disabled_color)); button->set_ink_drop_base_color(icon_color); } 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 f1e4ec7136f..21700f67388 100644 --- a/chromium/ui/views/controls/button/image_button_factory_unittest.cc +++ b/chromium/ui/views/controls/button/image_button_factory_unittest.cc @@ -26,8 +26,8 @@ TEST_F(ImageButtonFactoryTest, CreateVectorImageButton) { TEST_F(ImageButtonFactoryTest, SetImageFromVectorIcon) { ImageButton* button = CreateVectorImageButton(nullptr); SetImageFromVectorIcon(button, vector_icons::kCloseIcon, SK_ColorRED); - EXPECT_FALSE(button->GetImage(CustomButton::STATE_NORMAL).isNull()); - EXPECT_FALSE(button->GetImage(CustomButton::STATE_DISABLED).isNull()); + EXPECT_FALSE(button->GetImage(Button::STATE_NORMAL).isNull()); + EXPECT_FALSE(button->GetImage(Button::STATE_DISABLED).isNull()); EXPECT_EQ(color_utils::DeriveDefaultIconColor(SK_ColorRED), button->GetInkDropBaseColor()); diff --git a/chromium/ui/views/controls/button/image_button_unittest.cc b/chromium/ui/views/controls/button/image_button_unittest.cc index ccd62cef046..baf9a75d18b 100644 --- a/chromium/ui/views/controls/button/image_button_unittest.cc +++ b/chromium/ui/views/controls/button/image_button_unittest.cc @@ -57,7 +57,7 @@ TEST_F(ImageButtonTest, Basics) { // Set a normal image. gfx::ImageSkia normal_image = CreateTestImage(10, 20); - button.SetImage(CustomButton::STATE_NORMAL, &normal_image); + button.SetImage(Button::STATE_NORMAL, &normal_image); // Image uses normal image for painting. EXPECT_FALSE(button.GetImageToPaint().isNull()); @@ -69,7 +69,7 @@ TEST_F(ImageButtonTest, Basics) { // Set a pushed image. gfx::ImageSkia pushed_image = CreateTestImage(11, 21); - button.SetImage(CustomButton::STATE_PRESSED, &pushed_image); + button.SetImage(Button::STATE_PRESSED, &pushed_image); // By convention, preferred size doesn't change, even though pushed image // is bigger. @@ -118,7 +118,7 @@ TEST_F(ImageButtonTest, SetAndGetImage) { TEST_F(ImageButtonTest, ImagePositionWithBorder) { ImageButton button(NULL); gfx::ImageSkia image = CreateTestImage(20, 30); - button.SetImage(CustomButton::STATE_NORMAL, &image); + button.SetImage(Button::STATE_NORMAL, &image); // The image should be painted at the top-left corner. EXPECT_EQ(gfx::Point().ToString(), @@ -152,7 +152,7 @@ TEST_F(ImageButtonTest, ImagePositionWithBorder) { TEST_F(ImageButtonTest, LeftAlignedMirrored) { ImageButton button(NULL); gfx::ImageSkia image = CreateTestImage(20, 30); - button.SetImage(CustomButton::STATE_NORMAL, &image); + button.SetImage(Button::STATE_NORMAL, &image); button.SetBounds(0, 0, 50, 30); button.SetImageAlignment(ImageButton::ALIGN_LEFT, ImageButton::ALIGN_BOTTOM); @@ -167,7 +167,7 @@ TEST_F(ImageButtonTest, LeftAlignedMirrored) { TEST_F(ImageButtonTest, RightAlignedMirrored) { ImageButton button(NULL); gfx::ImageSkia image = CreateTestImage(20, 30); - button.SetImage(CustomButton::STATE_NORMAL, &image); + button.SetImage(Button::STATE_NORMAL, &image); button.SetBounds(0, 0, 50, 30); button.SetImageAlignment(ImageButton::ALIGN_RIGHT, ImageButton::ALIGN_BOTTOM); @@ -184,18 +184,18 @@ TEST_F(ImageButtonTest, PreferredSizeInvalidation) { ImageButton button(nullptr); gfx::ImageSkia first_image = CreateTestImage(20, 30); gfx::ImageSkia second_image = CreateTestImage(50, 50); - button.SetImage(CustomButton::STATE_NORMAL, &first_image); + button.SetImage(Button::STATE_NORMAL, &first_image); parent.AddChildView(&button); ASSERT_EQ(0, parent.pref_size_changed_calls()); - button.SetImage(CustomButton::STATE_NORMAL, &first_image); + button.SetImage(Button::STATE_NORMAL, &first_image); EXPECT_EQ(0, parent.pref_size_changed_calls()); - button.SetImage(CustomButton::STATE_HOVERED, &second_image); + button.SetImage(Button::STATE_HOVERED, &second_image); EXPECT_EQ(0, parent.pref_size_changed_calls()); // Changing normal state image size leads to a change in preferred size. - button.SetImage(CustomButton::STATE_NORMAL, &second_image); + button.SetImage(Button::STATE_NORMAL, &second_image); EXPECT_EQ(1, parent.pref_size_changed_calls()); } diff --git a/chromium/ui/views/controls/button/label_button.cc b/chromium/ui/views/controls/button/label_button.cc index 1d9e7b82cd5..68be16e39a1 100644 --- a/chromium/ui/views/controls/button/label_button.cc +++ b/chromium/ui/views/controls/button/label_button.cc @@ -40,7 +40,7 @@ const char LabelButton::kViewClassName[] = "LabelButton"; LabelButton::LabelButton(ButtonListener* listener, const base::string16& text, int button_context) - : CustomButton(listener), + : Button(listener), image_(new ImageView()), label_(new LabelButtonLabel(text, button_context)), ink_drop_container_(new InkDropContainerView()), @@ -298,7 +298,7 @@ void LabelButton::Layout() { } label_->SetBoundsRect(label_bounds); - CustomButton::Layout(); + Button::Layout(); } const char* LabelButton::GetClassName() const { @@ -306,7 +306,7 @@ const char* LabelButton::GetClassName() const { } void LabelButton::EnableCanvasFlippingForRTLUI(bool flip) { - CustomButton::EnableCanvasFlippingForRTLUI(flip); + Button::EnableCanvasFlippingForRTLUI(flip); image_->EnableCanvasFlippingForRTLUI(flip); } @@ -339,13 +339,13 @@ bool LabelButton::ShouldUseFloodFillInkDrop() const { } void LabelButton::OnFocus() { - CustomButton::OnFocus(); + Button::OnFocus(); // Typically the border renders differently when focused. SchedulePaint(); } void LabelButton::OnBlur() { - CustomButton::OnBlur(); + Button::OnBlur(); // Typically the border renders differently when focused. SchedulePaint(); } @@ -376,7 +376,7 @@ void LabelButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { std::unique_ptr<InkDrop> LabelButton::CreateInkDrop() { return ShouldUseFloodFillInkDrop() ? CreateDefaultFloodFillInkDropImpl() - : CustomButton::CreateInkDrop(); + : Button::CreateInkDrop(); } std::unique_ptr<views::InkDropRipple> LabelButton::CreateInkDropRipple() const { diff --git a/chromium/ui/views/controls/button/label_button.h b/chromium/ui/views/controls/button/label_button.h index 0fb03378c74..bc5190a4bc7 100644 --- a/chromium/ui/views/controls/button/label_button.h +++ b/chromium/ui/views/controls/button/label_button.h @@ -13,7 +13,7 @@ #include "base/macros.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/image/image_skia.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/image_view.h" #include "ui/views/controls/label.h" #include "ui/views/native_theme_delegate.h" @@ -26,8 +26,7 @@ class LabelButtonBorder; class LabelButtonLabel; // LabelButton is a button with text and an icon, it's not focusable by default. -class VIEWS_EXPORT LabelButton : public CustomButton, - public NativeThemeDelegate { +class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate { public: // The length of the hover fade animation. static const int kHoverAnimationDurationMs; @@ -130,7 +129,7 @@ class VIEWS_EXPORT LabelButton : public CustomButton, void OnBlur() override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; - // CustomButton: + // Button: void StateChanged(ButtonState old_state) override; // Fills |params| with information about the button. @@ -181,7 +180,7 @@ class VIEWS_EXPORT LabelButton : public CustomButton, void ResetCachedPreferredSize(); // Updates additional state related to focus or default status, rather than - // merely the CustomButton::state(). E.g. ensures the label text color is + // merely the Button::state(). E.g. ensures the label text color is // correct for the current background. void ResetLabelEnabledColor(); diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc index 9ba1771edde..74b34b038a5 100644 --- a/chromium/ui/views/controls/button/label_button_unittest.cc +++ b/chromium/ui/views/controls/button/label_button_unittest.cc @@ -64,7 +64,7 @@ class LabelButtonTest : public test::WidgetTest { TestLabelButton* AddStyledButton(const char* label, bool is_default) { TestLabelButton* button = new TestLabelButton; button->SetText(ASCIIToUTF16(label)); - button->SetStyleDeprecated(CustomButton::STYLE_BUTTON); + button->SetStyleDeprecated(Button::STYLE_BUTTON); if (is_default) button->SetIsDefault(true); button_->GetWidget()->GetContentsView()->AddChildView(button); @@ -474,7 +474,7 @@ class InkDropLabelButtonTest : public ViewsTestBase { switches::kTopChromeMD, switches::kTopChromeMDMaterial); ViewsTestBase::SetUp(); - // Create a widget so that the CustomButton can query the hover state + // Create a widget so that the Button can query the hover state // correctly. widget_.reset(new Widget); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc index 114cbe5dae1..5657285f45d 100644 --- a/chromium/ui/views/controls/button/md_text_button.cc +++ b/chromium/ui/views/controls/button/md_text_button.cc @@ -6,6 +6,7 @@ #include "base/i18n/case_conversion.h" #include "base/memory/ptr_util.h" +#include "build/build_config.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" @@ -43,7 +44,7 @@ LabelButton* CreateButton(ButtonListener* listener, return MdTextButton::Create(listener, text, style::CONTEXT_BUTTON_MD); LabelButton* button = new LabelButton(listener, text, style::CONTEXT_BUTTON); - button->SetStyleDeprecated(CustomButton::STYLE_BUTTON); + button->SetStyleDeprecated(Button::STYLE_BUTTON); return button; } @@ -243,6 +244,7 @@ void MdTextButton::UpdatePadding() { void MdTextButton::UpdateColors() { ui::NativeTheme* theme = GetNativeTheme(); + bool is_disabled = state() == STATE_DISABLED; SkColor enabled_text_color = style::GetColor( label()->text_context(), is_prominent_ ? style::STYLE_DIALOG_BUTTON_DEFAULT : style::STYLE_PRIMARY, @@ -250,6 +252,15 @@ void MdTextButton::UpdateColors() { if (!explicitly_set_normal_color()) { const auto colors = explicitly_set_colors(); LabelButton::SetEnabledTextColors(enabled_text_color); + // Non-prominent, disabled buttons need the disabled color explicitly set. + // This ensures that label()->enabled_color() returns the correct color as + // the basis for calculating the stroke color. enabled_text_color isn't used + // since a descendant could have overridden the label enabled color. + if (is_disabled && !is_prominent_) { + LabelButton::SetTextColor(STATE_DISABLED, + style::GetColor(label()->text_context(), + style::STYLE_DISABLED, theme)); + } set_explicitly_set_colors(colors); } @@ -267,9 +278,10 @@ void MdTextButton::UpdateColors() { } else if (is_prominent_) { bg_color = theme->GetSystemColor( ui::NativeTheme::kColorId_ProminentButtonColor); - if (state() == STATE_DISABLED) + if (is_disabled) { bg_color = color_utils::BlendTowardOppositeLuma( bg_color, gfx::kDisabledControlAlpha); + } } if (state() == STATE_PRESSED) { @@ -278,19 +290,41 @@ void MdTextButton::UpdateColors() { bg_color = color_utils::GetResultingPaintColor(shade, bg_color); } - // Specified text color: 5a5a5a @ 1.0 alpha - // Specified stroke color: 000000 @ 0.2 alpha - // 000000 @ 0.2 is very close to 5a5a5a @ 0.308 (== 0x4e); both are cccccc @ - // 1.0, and this way if NativeTheme changes the button color, the button - // stroke will also change colors to match. - SkColor stroke_color = - is_prominent_ ? SK_ColorTRANSPARENT : SkColorSetA(text_color, 0x4e); - - // Disabled, non-prominent buttons need their stroke lightened. Prominent - // buttons need it left at SK_ColorTRANSPARENT from above. - if (state() == STATE_DISABLED && !is_prominent_) { - stroke_color = color_utils::BlendTowardOppositeLuma( - stroke_color, gfx::kDisabledControlAlpha); + SkColor stroke_color; + if (is_prominent_) { + stroke_color = SK_ColorTRANSPARENT; + } else { + int stroke_alpha; + if (is_disabled) { + // Disabled, non-prominent buttons need a lighter stroke. This alpha + // value will take the disabled button colors, a1a192 @ 1.0 alpha for + // non-Harmony, 9e9e9e @ 1.0 alpha for Harmony and turn it into + // e6e6e6 @ 1.0 alpha (or very close to it) or an effective 000000 @ 0.1 + // alpha for the stroke color. The same alpha value will work with both + // Harmony and non-Harmony colors. + stroke_alpha = 0x43; + } else { + // These alpha values will take the enabled button colors, 5a5a5a @ 1.0 + // alpha for non-Harmony, 757575 @ 1.0 alpha for Harmony and turn it into + // an effective b2b2b2 @ 1.0 alpha or 000000 @ 0.3 for the stroke_color. + stroke_alpha = UseMaterialSecondaryButtons() ? 0x8f : 0x77; +#if defined(OS_MACOSX) + // Without full secondary UI MD support, the text color is solid black, + // and so the border is too dark on Mac. On Retina it looks OK, so + // heuristically determine the scale factor as well. + if (!ui::MaterialDesignController::IsSecondaryUiMaterial()) { + // The Compositor may only be set when attached to a Widget. But, since + // that also determines the theme, UpdateColors() will always be called + // after attaching to a Widget. + // TODO(tapted): Move this into SolidRoundRectPainter if we like this + // logic for Harmony. + auto* compositor = layer()->GetCompositor(); + if (compositor && compositor->device_scale_factor() == 1) + stroke_alpha = 0x4d; // Chosen to match full secondary UI MD (0.3). + } +#endif + } + stroke_color = SkColorSetA(text_color, stroke_alpha); } DCHECK_EQ(SK_AlphaOPAQUE, static_cast<int>(SkColorGetA(bg_color))); diff --git a/chromium/ui/views/controls/button/menu_button.cc b/chromium/ui/views/controls/button/menu_button.cc index 0e4194e4625..8a823199304 100644 --- a/chromium/ui/views/controls/button/menu_button.cc +++ b/chromium/ui/views/controls/button/menu_button.cc @@ -282,14 +282,14 @@ bool MenuButton::OnKeyPressed(const ui::KeyEvent& event) { } bool MenuButton::OnKeyReleased(const ui::KeyEvent& event) { - // Override CustomButton's implementation, which presses the button when + // Override Button's implementation, which presses the button when // you press space and clicks it when you release space. For a MenuButton // we always activate the menu on key press. return false; } void MenuButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { - CustomButton::GetAccessibleNodeData(node_data); + Button::GetAccessibleNodeData(node_data); node_data->role = ui::AX_ROLE_POP_UP_BUTTON; node_data->AddState(ui::AX_STATE_HASPOPUP); if (enabled()) { diff --git a/chromium/ui/views/controls/button/menu_button.h b/chromium/ui/views/controls/button/menu_button.h index 9adbee93848..e38200b8a43 100644 --- a/chromium/ui/views/controls/button/menu_button.h +++ b/chromium/ui/views/controls/button/menu_button.h @@ -95,7 +95,7 @@ class VIEWS_EXPORT MenuButton : public LabelButton { // Overridden from LabelButton: gfx::Rect GetChildAreaBounds() override; - // Overridden from CustomButton: + // Overridden from Button: bool IsTriggerableEvent(const ui::Event& event) override; bool ShouldEnterPushedState(const ui::Event& event) override; void StateChanged(ButtonState old_state) override; diff --git a/chromium/ui/views/controls/button/menu_button_unittest.cc b/chromium/ui/views/controls/button/menu_button_unittest.cc index 21eb775c6e5..500bbc91a5d 100644 --- a/chromium/ui/views/controls/button/menu_button_unittest.cc +++ b/chromium/ui/views/controls/button/menu_button_unittest.cc @@ -21,6 +21,7 @@ #if defined(USE_AURA) #include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/client/drag_drop_client_observer.h" #include "ui/events/event.h" #include "ui/events/event_handler.h" #endif @@ -134,9 +135,9 @@ class TestButtonListener : public ButtonListener { void ButtonPressed(Button* sender, const ui::Event& event) override { last_sender_ = sender; - CustomButton* custom_button = CustomButton::AsCustomButton(sender); - DCHECK(custom_button); - last_sender_state_ = custom_button->state(); + Button* button = Button::AsButton(sender); + DCHECK(button); + last_sender_state_ = button->state(); last_event_type_ = event.type(); } @@ -162,9 +163,9 @@ class TestMenuButtonListener : public MenuButtonListener { const gfx::Point& point, const ui::Event* event) override { last_source_ = source; - CustomButton* custom_button = CustomButton::AsCustomButton(source); - DCHECK(custom_button); - last_source_state_ = custom_button->state(); + Button* button = Button::AsButton(source); + DCHECK(button); + last_source_state_ = button->state(); } View* last_source() { return last_source_; } @@ -253,6 +254,9 @@ class TestDragDropClient : public aura::client::DragDropClient, ui::DragDropTypes::DragEventSource source) override; void DragCancel() override; bool IsDragDropInProgress() override; + void AddObserver(aura::client::DragDropClientObserver* observer) override {} + void RemoveObserver(aura::client::DragDropClientObserver* observer) override { + } // ui::EventHandler: void OnMouseEvent(ui::MouseEvent* event) override; diff --git a/chromium/ui/views/controls/button/radio_button_unittest.cc b/chromium/ui/views/controls/button/radio_button_unittest.cc new file mode 100644 index 00000000000..c68b6ad15ec --- /dev/null +++ b/chromium/ui/views/controls/button/radio_button_unittest.cc @@ -0,0 +1,96 @@ +// 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/radio_button.h" + +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/test/views_test_base.h" + +namespace { +// Group ID of the test radio buttons. +constexpr int kGroup = 1; +} // namespace + +namespace views { + +class RadioButtonTest : public ViewsTestBase { + public: + RadioButtonTest() : button_container_(nullptr) {} + + void SetUp() override { + ViewsTestBase::SetUp(); + + // Create a Widget so the radio buttons can find their group siblings. + widget_ = base::MakeUnique<Widget>(); + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + widget_->Init(params); + widget_->Show(); + + button_container_ = new View(); + widget_->SetContentsView(button_container_); + } + + void TearDown() override { + button_container_ = nullptr; + widget_.reset(); + ViewsTestBase::TearDown(); + } + + protected: + View& button_container() { return *button_container_; } + + private: + View* button_container_; + std::unique_ptr<Widget> widget_; + + DISALLOW_COPY_AND_ASSIGN(RadioButtonTest); +}; + +TEST_F(RadioButtonTest, Basics) { + RadioButton* button1 = new RadioButton(base::ASCIIToUTF16("Blah"), kGroup); + button_container().AddChildView(button1); + RadioButton* button2 = new RadioButton(base::ASCIIToUTF16("Blah"), kGroup); + button_container().AddChildView(button2); + + button1->SetChecked(true); + EXPECT_TRUE(button1->checked()); + EXPECT_FALSE(button2->checked()); + + button2->SetChecked(true); + EXPECT_FALSE(button1->checked()); + EXPECT_TRUE(button2->checked()); +} + +TEST_F(RadioButtonTest, Focus) { + RadioButton* button1 = new RadioButton(base::ASCIIToUTF16("Blah"), kGroup); + button_container().AddChildView(button1); + RadioButton* button2 = new RadioButton(base::ASCIIToUTF16("Blah"), kGroup); + button_container().AddChildView(button2); + + // Tabbing through only focuses the checked button. + button1->SetChecked(true); + auto* focus_manager = button_container().GetFocusManager(); + ui::KeyEvent pressed_tab(ui::ET_KEY_PRESSED, ui::VKEY_TAB, ui::EF_NONE); + focus_manager->OnKeyEvent(pressed_tab); + EXPECT_EQ(button1, focus_manager->GetFocusedView()); + focus_manager->OnKeyEvent(pressed_tab); + EXPECT_EQ(button1, focus_manager->GetFocusedView()); + + // The checked button can be moved using arrow keys. + focus_manager->OnKeyEvent( + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_DOWN, ui::EF_NONE)); + EXPECT_EQ(button2, focus_manager->GetFocusedView()); + EXPECT_FALSE(button1->checked()); + EXPECT_TRUE(button2->checked()); + + focus_manager->OnKeyEvent( + ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_UP, ui::EF_NONE)); + EXPECT_EQ(button1, focus_manager->GetFocusedView()); + EXPECT_TRUE(button1->checked()); + EXPECT_FALSE(button2->checked()); +} + +} // namespace views diff --git a/chromium/ui/views/controls/button/toggle_button.cc b/chromium/ui/views/controls/button/toggle_button.cc index 5585524516d..b62c2de33e0 100644 --- a/chromium/ui/views/controls/button/toggle_button.cc +++ b/chromium/ui/views/controls/button/toggle_button.cc @@ -110,10 +110,7 @@ class ToggleButton::ThumbView : public InkDropHostView { const char ToggleButton::kViewClassName[] = "ToggleButton"; ToggleButton::ToggleButton(ButtonListener* listener) - : CustomButton(listener), - is_on_(false), - slide_animation_(this), - thumb_view_(new ThumbView()) { + : Button(listener), thumb_view_(new ThumbView()) { slide_animation_.SetSlideDuration(80 /* ms */); slide_animation_.SetTweenType(gfx::Tween::LINEAR); AddChildView(thumb_view_); @@ -185,6 +182,10 @@ const char* ToggleButton::GetClassName() const { return kViewClassName; } +bool ToggleButton::CanAcceptEvent(const ui::Event& event) { + return accepts_events_ && Button::CanAcceptEvent(event); +} + void ToggleButton::OnBoundsChanged(const gfx::Rect& previous_bounds) { UpdateThumb(); } @@ -194,7 +195,7 @@ void ToggleButton::OnNativeThemeChanged(const ui::NativeTheme* theme) { } void ToggleButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { - CustomButton::GetAccessibleNodeData(node_data); + Button::GetAccessibleNodeData(node_data); node_data->role = ui::AX_ROLE_SWITCH; const ui::AXCheckedState checked_state = @@ -202,9 +203,32 @@ void ToggleButton::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->AddIntAttribute(ui::AX_ATTR_CHECKED_STATE, checked_state); } +void ToggleButton::OnFocus() { + Button::OnFocus(); + AnimateInkDrop(views::InkDropState::ACTION_PENDING, nullptr); +} + +void ToggleButton::OnBlur() { + Button::OnBlur(); + + // The ink drop may have already gone away if the user clicked after focusing. + if (GetInkDrop()->GetTargetInkDropState() == + views::InkDropState::ACTION_PENDING) { + AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED, nullptr); + } +} + void ToggleButton::NotifyClick(const ui::Event& event) { SetIsOn(!is_on(), true); - CustomButton::NotifyClick(event); + + // Skip over Button::NotifyClick, to customize the ink drop animation. + // Leave the ripple in place when the button is activated via the keyboard. + if (!event.IsKeyEvent()) { + AnimateInkDrop(InkDropState::ACTION_TRIGGERED, + ui::LocatedEvent::FromIfValid(&event)); + } + + Button::NotifyClick(event); } void ToggleButton::PaintButtonContents(gfx::Canvas* canvas) { @@ -234,8 +258,7 @@ void ToggleButton::RemoveInkDropLayer(ui::Layer* ink_drop_layer) { } std::unique_ptr<InkDrop> ToggleButton::CreateInkDrop() { - std::unique_ptr<InkDropImpl> ink_drop = - CustomButton::CreateDefaultInkDropImpl(); + std::unique_ptr<InkDropImpl> ink_drop = Button::CreateDefaultInkDropImpl(); ink_drop->SetShowHighlightOnHover(false); return std::move(ink_drop); } @@ -247,7 +270,7 @@ std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const { } SkColor ToggleButton::GetInkDropBaseColor() const { - return GetTrackColor(is_on()); + return GetTrackColor(is_on() || HasFocus()); } void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { @@ -258,7 +281,7 @@ void ToggleButton::AnimationProgressed(const gfx::Animation* animation) { SchedulePaint(); return; } - CustomButton::AnimationProgressed(animation); + Button::AnimationProgressed(animation); } } // namespace views diff --git a/chromium/ui/views/controls/button/toggle_button.h b/chromium/ui/views/controls/button/toggle_button.h index 7b356885031..87b97add21f 100644 --- a/chromium/ui/views/controls/button/toggle_button.h +++ b/chromium/ui/views/controls/button/toggle_button.h @@ -6,14 +6,14 @@ #define UI_VIEWS_CONTROLS_BUTTON_TOGGLE_BUTTON_H_ #include "ui/gfx/animation/slide_animation.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" namespace views { // This view presents a button that has two states: on and off. This is similar // to a checkbox but has no text and looks more like a two-state horizontal // slider. -class VIEWS_EXPORT ToggleButton : public CustomButton { +class VIEWS_EXPORT ToggleButton : public Button { public: static const char kViewClassName[]; @@ -23,6 +23,10 @@ class VIEWS_EXPORT ToggleButton : public CustomButton { void SetIsOn(bool is_on, bool animate); bool is_on() const { return is_on_; } + void set_accepts_events(bool accepts_events) { + accepts_events_ = accepts_events; + } + // views::View: gfx::Size CalculatePreferredSize() const override; @@ -43,11 +47,14 @@ class VIEWS_EXPORT ToggleButton : public CustomButton { // views::View: const char* GetClassName() const override; + bool CanAcceptEvent(const ui::Event& event) override; void OnBoundsChanged(const gfx::Rect& previous_bounds) override; void OnNativeThemeChanged(const ui::NativeTheme* theme) override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + void OnFocus() override; + void OnBlur() override; - // CustomButton: + // Button: void NotifyClick(const ui::Event& event) override; void PaintButtonContents(gfx::Canvas* canvas) override; void AddInkDropLayer(ui::Layer* ink_drop_layer) override; @@ -59,10 +66,14 @@ class VIEWS_EXPORT ToggleButton : public CustomButton { // gfx::AnimationDelegate: void AnimationProgressed(const gfx::Animation* animation) override; - bool is_on_; - gfx::SlideAnimation slide_animation_; + bool is_on_ = false; + gfx::SlideAnimation slide_animation_{this}; ThumbView* thumb_view_; + // When false, this button won't accept input. Different from View::SetEnabled + // in that the view retains focus when this is false but not when disabled. + bool accepts_events_ = true; + DISALLOW_COPY_AND_ASSIGN(ToggleButton); }; diff --git a/chromium/ui/views/controls/button/toggle_button_unittest.cc b/chromium/ui/views/controls/button/toggle_button_unittest.cc index 8ea3f033795..ac6191418b0 100644 --- a/chromium/ui/views/controls/button/toggle_button_unittest.cc +++ b/chromium/ui/views/controls/button/toggle_button_unittest.cc @@ -9,7 +9,7 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event_utils.h" -#include "ui/views/style/platform_style.h" +#include "ui/events/test/event_generator.h" #include "ui/views/test/views_test_base.h" namespace views { @@ -25,6 +25,8 @@ class TestToggleButton : public ToggleButton { SetInkDropMode(InkDropMode::OFF); } + using View::Focus; + protected: // ToggleButton: void AddInkDropLayer(ui::Layer* ink_drop_layer) override { @@ -54,7 +56,8 @@ class ToggleButtonTest : public ViewsTestBase { // Create a widget so that the ToggleButton can query the hover state // correctly. widget_.reset(new Widget); - Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + Widget::InitParams params = + CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS); params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; params.bounds = gfx::Rect(0, 0, 650, 650); widget_->Init(params); @@ -91,13 +94,55 @@ TEST_F(ToggleButtonTest, ToggleButtonDestroyed) { button()->OnMousePressed(ui::MouseEvent( ui::ET_MOUSE_PRESSED, center, center, ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); - // On platforms with no ripples, there should never be an ink drop layer. - if (PlatformStyle::kUseRipples) - EXPECT_EQ(1, counter()); - else - EXPECT_EQ(0, counter()); + EXPECT_EQ(1, counter()); delete button(); EXPECT_EQ(0, counter()); } +// Make sure nothing bad happens when the widget is destroyed while the +// ToggleButton has focus (and is showing a ripple). +TEST_F(ToggleButtonTest, ShutdownWithFocus) { + button()->RequestFocus(); + EXPECT_EQ(1, counter()); +} + +// Verify that ToggleButton::accepts_events_ works as expected. +TEST_F(ToggleButtonTest, AcceptEvents) { + EXPECT_FALSE(button()->is_on()); + ui::test::EventGenerator generator(widget()->GetNativeWindow()); + + // Clicking toggles. + generator.ClickLeftButton(); + EXPECT_TRUE(button()->is_on()); + generator.ClickLeftButton(); + EXPECT_FALSE(button()->is_on()); + + // Spacebar toggles. + button()->RequestFocus(); + generator.PressKey(ui::VKEY_SPACE, ui::EF_NONE); + generator.ReleaseKey(ui::VKEY_SPACE, ui::EF_NONE); + EXPECT_TRUE(button()->is_on()); + generator.PressKey(ui::VKEY_SPACE, ui::EF_NONE); + generator.ReleaseKey(ui::VKEY_SPACE, ui::EF_NONE); + EXPECT_FALSE(button()->is_on()); + + // Spacebar and clicking do nothing when not accepting events, but focus is + // not affected. + button()->set_accepts_events(false); + EXPECT_TRUE(button()->HasFocus()); + generator.PressKey(ui::VKEY_SPACE, ui::EF_NONE); + generator.ReleaseKey(ui::VKEY_SPACE, ui::EF_NONE); + EXPECT_FALSE(button()->is_on()); + generator.ClickLeftButton(); + EXPECT_FALSE(button()->is_on()); + + // Re-enable events and clicking and spacebar resume working. + button()->set_accepts_events(true); + generator.PressKey(ui::VKEY_SPACE, ui::EF_NONE); + generator.ReleaseKey(ui::VKEY_SPACE, ui::EF_NONE); + EXPECT_TRUE(button()->is_on()); + generator.ClickLeftButton(); + EXPECT_FALSE(button()->is_on()); +} + } // namespace views diff --git a/chromium/ui/views/controls/combobox/combobox.cc b/chromium/ui/views/controls/combobox/combobox.cc index 195114dc2d6..b489f9722d3 100644 --- a/chromium/ui/views/controls/combobox/combobox.cc +++ b/chromium/ui/views/controls/combobox/combobox.cc @@ -32,7 +32,7 @@ #include "ui/views/animation/ink_drop_highlight.h" #include "ui/views/animation/ink_drop_impl.h" #include "ui/views/background.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/combobox/combobox_listener.h" #include "ui/views/controls/focus_ring.h" @@ -119,10 +119,10 @@ gfx::Rect PositionArrowWithinContainer(const gfx::Rect& container_bounds, } // The transparent button which holds a button state but is not rendered. -class TransparentButton : public CustomButton { +class TransparentButton : public Button { public: TransparentButton(ButtonListener* listener, bool animate_state_change) - : CustomButton(listener) { + : Button(listener) { set_animate_on_state_change(animate_state_change); if (animate_state_change) SetAnimationDuration(LabelButton::kHoverAnimationDurationMs); @@ -142,7 +142,7 @@ class TransparentButton : public CustomButton { // platforms they do. parent()->RequestFocus(); #endif - return CustomButton::OnMousePressed(mouse_event); + return Button::OnMousePressed(mouse_event); } double GetAnimationValue() const { diff --git a/chromium/ui/views/controls/combobox/combobox.h b/chromium/ui/views/controls/combobox/combobox.h index 42a2051fdf8..511cad8bece 100644 --- a/chromium/ui/views/controls/combobox/combobox.h +++ b/chromium/ui/views/controls/combobox/combobox.h @@ -27,7 +27,7 @@ class ComboboxTestApi; } class ComboboxListener; -class CustomButton; +class Button; class MenuRunner; class Painter; class PrefixSelector; @@ -214,8 +214,8 @@ class VIEWS_EXPORT Combobox : public View, // STYLE_NOTIFY_ON_CLICK, a Combobox renders the button images according to // these button states. // The base View takes the ownerships of these as child views. - CustomButton* text_button_; - CustomButton* arrow_button_; + Button* text_button_; + Button* arrow_button_; // Set while the dropdown is showing. Ensures the menu is closed if |this| is // destroyed. diff --git a/chromium/ui/views/controls/focusable_border.cc b/chromium/ui/views/controls/focusable_border.cc index e96914d5697..61166088b75 100644 --- a/chromium/ui/views/controls/focusable_border.cc +++ b/chromium/ui/views/controls/focusable_border.cc @@ -36,10 +36,6 @@ void FocusableBorder::SetColorId( } void FocusableBorder::Paint(const View& view, gfx::Canvas* canvas) { - // In harmony, the focus indicator is a FocusRing. - if (ui::MaterialDesignController::IsSecondaryUiMaterial() && view.HasFocus()) - return; - cc::PaintFlags flags; flags.setStyle(cc::PaintFlags::kStroke_Style); flags.setColor(GetCurrentColor(view)); @@ -85,10 +81,14 @@ void FocusableBorder::SetInsets(int top, int left, int bottom, int right) { SkColor FocusableBorder::GetCurrentColor(const View& view) const { ui::NativeTheme::ColorId color_id = ui::NativeTheme::kColorId_UnfocusedBorderColor; - if (override_color_id_) + if (override_color_id_) { color_id = *override_color_id_; - else if (view.HasFocus()) + } else if (view.HasFocus() && + !ui::MaterialDesignController::IsSecondaryUiMaterial()) { + // Note with --secondary-ui-md there is a FocusRing indicator, so the border + // retains its unfocused color. color_id = ui::NativeTheme::kColorId_FocusedBorderColor; + } SkColor color = view.GetNativeTheme()->GetSystemColor(color_id); if (ui::MaterialDesignController::IsSecondaryUiMaterial() && diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc index a1ca6ee4433..bc748e3552d 100644 --- a/chromium/ui/views/controls/image_view.cc +++ b/chromium/ui/views/controls/image_view.cc @@ -180,6 +180,19 @@ gfx::Size ImageView::CalculatePreferredSize() const { return size; } +views::PaintInfo::ScaleType ImageView::GetPaintScaleType() const { + // ImageView contains an image which is rastered at the device scale factor. + // By default, the paint commands are recorded at a scale factor slighlty + // different from the device scale factor. Re-rastering the image at this + // paint recording scale will result in a distorted image. Paint recording + // scale might also not be uniform along the x & y axis, thus resulting in + // further distortion in the aspect ratio of the final image. + // |kUniformScaling| ensures that the paint recording scale is uniform along + // the x & y axis and keeps the scale equal to the device scale factor. + // See http://crbug.com/754010 for more details. + return views::PaintInfo::ScaleType::kUniformScaling; +} + void ImageView::OnPaintImage(gfx::Canvas* canvas) { last_paint_scale_ = canvas->image_scale(); last_painted_bitmap_pixels_ = NULL; diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h index 393b934592a..e850c033cb2 100644 --- a/chromium/ui/views/controls/image_view.h +++ b/chromium/ui/views/controls/image_view.h @@ -79,6 +79,7 @@ class VIEWS_EXPORT ImageView : public View { bool GetTooltipText(const gfx::Point& p, base::string16* tooltip) const override; gfx::Size CalculatePreferredSize() const override; + views::PaintInfo::ScaleType GetPaintScaleType() const override; private: void OnPaintImage(gfx::Canvas* canvas); diff --git a/chromium/ui/views/controls/label.cc b/chromium/ui/views/controls/label.cc index 05c933848e5..16aec515d72 100644 --- a/chromium/ui/views/controls/label.cc +++ b/chromium/ui/views/controls/label.cc @@ -187,6 +187,14 @@ void Label::SetMultiLine(bool multi_line) { ResetLayout(); } +void Label::SetMaxLines(int max_lines) { + if (max_lines_ == max_lines) + return; + is_first_paint_text_ = true; + max_lines_ = max_lines; + ResetLayout(); +} + void Label::SetObscured(bool obscured) { if (this->obscured() == obscured) return; @@ -329,6 +337,8 @@ gfx::Size Label::CalculatePreferredSize() const { if (multi_line() && max_width_ != 0 && max_width_ < size.width()) return gfx::Size(max_width_, GetHeightForWidth(max_width_)); + if (multi_line() && max_lines() > 0) + return gfx::Size(size.width(), GetHeightForWidth(size.width())); return size; } @@ -356,8 +366,9 @@ int Label::GetHeightForWidth(int w) const { w -= GetInsets().width(); int height = 0; + int base_line_height = std::max(line_height(), font_list().GetHeight()); if (!multi_line() || text().empty() || w <= 0) { - height = std::max(line_height(), font_list().GetHeight()); + height = base_line_height; } else if (render_text_->MultilineSupported()) { // SetDisplayRect() has a side effect for later calls of GetStringSize(). // Be careful to invoke |render_text_->SetDisplayRect(gfx::Rect())| to @@ -366,7 +377,12 @@ int Label::GetHeightForWidth(int w) const { // managers invoke GetHeightForWidth() for the same width multiple times // and |render_text_| can cache the height. render_text_->SetDisplayRect(gfx::Rect(0, 0, w, 0)); - height = render_text_->GetStringSize().height(); + int string_height = render_text_->GetStringSize().height(); + // Cap the number of lines to |max_lines()| if multi-line and non-zero + // |max_lines()|. + height = multi_line() && max_lines() > 0 + ? std::min(max_lines() * base_line_height, string_height) + : string_height; } else { std::vector<base::string16> lines = GetLinesForWidth(w); height = lines.size() * std::max(line_height(), font_list().GetHeight()); @@ -819,6 +835,7 @@ void Label::Init(const base::string16& text, const gfx::FontList& font_list) { subpixel_rendering_enabled_ = true; auto_color_readability_ = true; multi_line_ = false; + max_lines_ = 0; UpdateColorsFromTheme(GetNativeTheme()); handles_tooltips_ = true; collapse_when_hidden_ = false; @@ -867,15 +884,17 @@ void Label::MaybeBuildRenderTextLines() const { rtl ? gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR; } - // Text eliding is not supported for multi-lined Labels. - // TODO(mukai): Add multi-lined elided text support. + // Multi-line labels only support NO_ELIDE and ELIDE_TAIL for now. + // TODO(warx): Investigate more elide text support. gfx::ElideBehavior elide_behavior = - multi_line() ? gfx::NO_ELIDE : elide_behavior_; + multi_line() && (elide_behavior_ != gfx::NO_ELIDE) ? gfx::ELIDE_TAIL + : elide_behavior_; if (!multi_line() || render_text_->MultilineSupported()) { std::unique_ptr<gfx::RenderText> render_text = CreateRenderText(text(), alignment, directionality, elide_behavior); render_text->SetDisplayRect(rect); render_text->SetMultiline(multi_line()); + render_text->SetMaxLines(multi_line() ? max_lines() : 0); render_text->SetWordWrapBehavior(render_text_->word_wrap_behavior()); // Setup render text for selection controller. @@ -887,6 +906,8 @@ void Label::MaybeBuildRenderTextLines() const { lines_.push_back(std::move(render_text)); } else { + // TODO(warx): Apply max_lines property when RenderText doesn't support + // multi-line (crbug.com/758720). std::vector<base::string16> lines = GetLinesForWidth(rect.width()); if (lines.size() > 1) rect.set_height(std::max(line_height(), font_list().GetHeight())); diff --git a/chromium/ui/views/controls/label.h b/chromium/ui/views/controls/label.h index 38eb5f3cd2b..7cae587453c 100644 --- a/chromium/ui/views/controls/label.h +++ b/chromium/ui/views/controls/label.h @@ -132,6 +132,11 @@ class VIEWS_EXPORT Label : public View, bool multi_line() const { return multi_line_; } void SetMultiLine(bool multi_line); + // If multi-line, a non-zero value will cap the number of lines rendered, and + // elide the rest (currently only ELIDE_TAIL supported). See gfx::RenderText. + int max_lines() const { return max_lines_; } + void SetMaxLines(int max_lines); + // Get or set if the label text should be obscured before rendering (e.g. // should "Password!" display as "*********"); default is false. bool obscured() const { return render_text_->obscured(); } @@ -359,6 +364,7 @@ class VIEWS_EXPORT Label : public View, bool auto_color_readability_; // TODO(mukai): remove |multi_line_| when all RenderText can render multiline. bool multi_line_; + int max_lines_; base::string16 tooltip_text_; bool handles_tooltips_; // Whether to collapse the label when it's not visible. diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc index b2cf18f40af..820567cc99e 100644 --- a/chromium/ui/views/controls/label_unittest.cc +++ b/chromium/ui/views/controls/label_unittest.cc @@ -56,7 +56,10 @@ class TestLabel : public Label { void SimulatePaint() { SkBitmap bitmap; SkColor color = SK_ColorTRANSPARENT; - Paint(ui::CanvasPainter(&bitmap, bounds().size(), 1.f, color).context()); + Paint(PaintInfo::CreateRootPaintInfo( + ui::CanvasPainter(&bitmap, bounds().size(), 1.f, color, false) + .context(), + bounds().size())); } // View: @@ -569,8 +572,7 @@ TEST_F(LabelTest, SingleLineSizing) { EXPECT_EQ(size, label()->GetPreferredSize()); const gfx::Insets border(10, 20, 30, 40); - label()->SetBorder(CreateEmptyBorder(border.top(), border.left(), - border.bottom(), border.right())); + label()->SetBorder(CreateEmptyBorder(border)); const gfx::Size size_with_border = label()->GetPreferredSize(); EXPECT_EQ(size_with_border.height(), size.height() + border.height()); EXPECT_EQ(size_with_border.width(), size.width() + border.width()); @@ -649,8 +651,7 @@ TEST_F(LabelTest, MultiLineSizing) { // Test everything with borders. gfx::Insets border(10, 20, 30, 40); - label()->SetBorder(CreateEmptyBorder(border.top(), border.left(), - border.bottom(), border.right())); + label()->SetBorder(CreateEmptyBorder(border)); // SizeToFit and borders. label()->SizeToFit(0); @@ -682,6 +683,52 @@ TEST_F(LabelTest, MultiLineSizing) { required_size.width() + border.width()); } +#if !defined(OS_MACOSX) +// TODO(warx): Remove !defined(OS_MACOSX) once SetMaxLines() is applied to MAC +// (crbug.com/758720). +TEST_F(LabelTest, MultiLineSetMaxLines) { + // Ensure SetMaxLines clamps the line count of a string with returns. + label()->SetText(ASCIIToUTF16("first line\nsecond line\nthird line")); + label()->SetMultiLine(true); + gfx::Size string_size = label()->GetPreferredSize(); + label()->SetMaxLines(2); + gfx::Size two_line_size = label()->GetPreferredSize(); + EXPECT_EQ(string_size.width(), two_line_size.width()); + EXPECT_GT(string_size.height(), two_line_size.height()); + + // Ensure GetHeightForWidth also respects SetMaxLines. + int height = label()->GetHeightForWidth(string_size.width() / 2); + EXPECT_EQ(height, two_line_size.height()); + + // Ensure SetMaxLines also works with line wrapping for SizeToFit. + label()->SetText(ASCIIToUTF16("A long string that will be wrapped")); + label()->SetMaxLines(0); // Used to get the uncapped height. + label()->SizeToFit(0); // Used to get the uncapped width. + label()->SizeToFit(label()->GetPreferredSize().width() / 4); + string_size = label()->GetPreferredSize(); + label()->SetMaxLines(2); + two_line_size = label()->GetPreferredSize(); + EXPECT_EQ(string_size.width(), two_line_size.width()); + EXPECT_GT(string_size.height(), two_line_size.height()); + + // Ensure SetMaxLines also works with line wrapping for SetMaximumWidth. + label()->SetMaxLines(0); // Used to get the uncapped height. + label()->SizeToFit(0); // Used to get the uncapped width. + label()->SetMaximumWidth(label()->GetPreferredSize().width() / 4); + string_size = label()->GetPreferredSize(); + label()->SetMaxLines(2); + two_line_size = label()->GetPreferredSize(); + EXPECT_EQ(string_size.width(), two_line_size.width()); + EXPECT_GT(string_size.height(), two_line_size.height()); + + // Ensure SetMaxLines respects the requested inset height. + const gfx::Insets border(1, 2, 3, 4); + label()->SetBorder(CreateEmptyBorder(border)); + EXPECT_EQ(two_line_size.height() + border.height(), + label()->GetPreferredSize().height()); +} +#endif + // Verifies if the combination of text eliding and multiline doesn't cause // any side effects of size / layout calculation. TEST_F(LabelTest, MultiLineSizingWithElide) { @@ -696,7 +743,7 @@ TEST_F(LabelTest, MultiLineSizingWithElide) { label()->SetBoundsRect(gfx::Rect(required_size)); label()->SetElideBehavior(gfx::ELIDE_TAIL); - EXPECT_EQ(required_size.ToString(), label()->GetPreferredSize().ToString()); + EXPECT_EQ(required_size, label()->GetPreferredSize()); EXPECT_EQ(text, label()->GetDisplayTextForTesting()); label()->SizeToFit(required_size.width() - 1); @@ -706,12 +753,12 @@ TEST_F(LabelTest, MultiLineSizingWithElide) { // SetBounds() doesn't change the preferred size. label()->SetBounds(0, 0, narrow_size.width() - 1, narrow_size.height()); - EXPECT_EQ(narrow_size.ToString(), label()->GetPreferredSize().ToString()); + EXPECT_EQ(narrow_size, label()->GetPreferredSize()); // Paint() doesn't change the preferred size. gfx::Canvas canvas; label()->OnPaint(&canvas); - EXPECT_EQ(narrow_size.ToString(), label()->GetPreferredSize().ToString()); + EXPECT_EQ(narrow_size, label()->GetPreferredSize()); } // Check that labels support GetTooltipHandlerForPoint. @@ -766,7 +813,7 @@ TEST_F(LabelTest, ResetRenderTextData) { label()->SizeToPreferredSize(); gfx::Size preferred_size = label()->GetPreferredSize(); - EXPECT_NE(gfx::Size().ToString(), preferred_size.ToString()); + EXPECT_NE(gfx::Size(), preferred_size); EXPECT_EQ(0u, label()->lines_.size()); gfx::Canvas canvas(preferred_size, 1.0f, true); @@ -783,7 +830,7 @@ TEST_F(LabelTest, ResetRenderTextData) { EXPECT_EQ(ASCIIToUTF16("Example"), label()->text()); EXPECT_EQ(0u, label()->lines_.size()); - EXPECT_EQ(preferred_size.ToString(), label()->GetPreferredSize().ToString()); + EXPECT_EQ(preferred_size, label()->GetPreferredSize()); EXPECT_EQ(0u, label()->lines_.size()); // RenderText data should be back when it's necessary. @@ -893,7 +940,7 @@ TEST_P(MDLabelTest, FocusBounds) { label()->SizeToPreferredSize(); gfx::Rect focus_bounds = label()->GetFocusRingBounds(); - EXPECT_EQ(label()->GetLocalBounds().ToString(), focus_bounds.ToString()); + EXPECT_EQ(label()->GetLocalBounds(), focus_bounds); gfx::Size focusable_size = normal_label_size; label()->SetBounds( @@ -903,7 +950,7 @@ TEST_P(MDLabelTest, FocusBounds) { EXPECT_EQ(0, focus_bounds.x()); EXPECT_LT(0, focus_bounds.y()); EXPECT_GT(label()->bounds().bottom(), focus_bounds.bottom()); - EXPECT_EQ(focusable_size.ToString(), focus_bounds.size().ToString()); + EXPECT_EQ(focusable_size, focus_bounds.size()); label()->SetHorizontalAlignment(gfx::ALIGN_RIGHT); focus_bounds = label()->GetFocusRingBounds(); @@ -911,7 +958,7 @@ TEST_P(MDLabelTest, FocusBounds) { EXPECT_EQ(label()->bounds().right(), focus_bounds.right()); EXPECT_LT(0, focus_bounds.y()); EXPECT_GT(label()->bounds().bottom(), focus_bounds.bottom()); - EXPECT_EQ(focusable_size.ToString(), focus_bounds.size().ToString()); + EXPECT_EQ(focusable_size, focus_bounds.size()); label()->SetHorizontalAlignment(gfx::ALIGN_LEFT); label()->SetElideBehavior(gfx::FADE_TAIL); diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index e9125d68fbd..9c0a30130cd 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -103,15 +103,15 @@ bool TitleMatchesMnemonic(MenuItemView* menu, base::char16 key) { } // Returns the first descendant of |view| that is hot tracked. -CustomButton* GetFirstHotTrackedView(View* view) { +Button* GetFirstHotTrackedView(View* view) { if (!view) return NULL; - CustomButton* button = CustomButton::AsCustomButton(view); + Button* button = Button::AsButton(view); if (button && button->IsHotTracked()) return button; for (int i = 0; i < view->child_count(); ++i) { - CustomButton* hot_view = GetFirstHotTrackedView(view->child_at(i)); + Button* hot_view = GetFirstHotTrackedView(view->child_at(i)); if (hot_view) return hot_view; } @@ -570,7 +570,7 @@ bool MenuController::OnMousePressed(SubmenuView* source, ConvertLocatedEventForRootView(source, forward_to_root, &event_for_root); View* view = forward_to_root->GetEventHandlerForPoint(event_for_root.location()); - CustomButton* button = CustomButton::AsCustomButton(view); + Button* button = Button::AsButton(view); if (hot_button_ != button) SetHotTrackedButton(button); @@ -756,7 +756,7 @@ void MenuController::OnMouseMoved(SubmenuView* source, ConvertLocatedEventForRootView(source, root_view, &event_for_root); View* view = root_view->GetEventHandlerForPoint(event_for_root.location()); - CustomButton* button = CustomButton::AsCustomButton(view); + Button* button = Button::AsButton(view); if (button && button->IsHotTracked()) SetHotTrackedButton(button); } @@ -785,7 +785,7 @@ void MenuController::OnGestureEvent(SubmenuView* source, ConvertLocatedEventForRootView(source, root_view, &event_for_root); View* view = root_view->GetEventHandlerForPoint(event_for_root.location()); - CustomButton* button = CustomButton::AsCustomButton(view); + Button* button = Button::AsButton(view); if (hot_button_ && hot_button_ != button) SetHotTrackedButton(nullptr); } @@ -1350,7 +1350,7 @@ void MenuController::OnKeyDown(ui::KeyboardCode key_code) { break; case ui::VKEY_APPS: { - CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item); + Button* hot_view = GetFirstHotTrackedView(pending_state_.item); if (hot_view) { hot_view->ShowContextMenu(hot_view->GetKeyboardContextMenuLocation(), ui::MENU_SOURCE_KEYBOARD); @@ -1422,7 +1422,7 @@ MenuController::~MenuController() { } bool MenuController::SendAcceleratorToHotTrackedView() { - CustomButton* hot_view = GetFirstHotTrackedView(pending_state_.item); + Button* hot_view = GetFirstHotTrackedView(pending_state_.item); if (!hot_view) return false; @@ -1431,7 +1431,7 @@ bool MenuController::SendAcceleratorToHotTrackedView() { hot_view->AcceleratorPressed(accelerator); // An accelerator may have canceled the menu after activation. if (this_ref) { - CustomButton* button = static_cast<CustomButton*>(hot_view); + Button* button = static_cast<Button*>(hot_view); SetHotTrackedButton(button); } return true; @@ -2203,7 +2203,7 @@ void MenuController::IncrementSelection( } if (item->has_children()) { - CustomButton* button = GetFirstHotTrackedView(item); + Button* button = GetFirstHotTrackedView(item); if (button) { DCHECK_EQ(hot_button_, button); SetHotTrackedButton(nullptr); @@ -2212,7 +2212,7 @@ void MenuController::IncrementSelection( View* to_make_hot = button ? GetNextFocusableView(item, button, direction_is_down) : GetInitialFocusableView(item, direction_is_down); - CustomButton* hot_button = CustomButton::AsCustomButton(to_make_hot); + Button* hot_button = Button::AsButton(to_make_hot); if (hot_button) { SetHotTrackedButton(hot_button); return; @@ -2695,10 +2695,10 @@ void MenuController::SetInitialHotTrackedView( SetSelection(item, SELECTION_DEFAULT); View* hot_view = GetInitialFocusableView(item, direction == INCREMENT_SELECTION_DOWN); - SetHotTrackedButton(CustomButton::AsCustomButton(hot_view)); + SetHotTrackedButton(Button::AsButton(hot_view)); } -void MenuController::SetHotTrackedButton(CustomButton* hot_button) { +void MenuController::SetHotTrackedButton(Button* hot_button) { if (hot_button == hot_button_) { // Hot-tracked state may change outside of the MenuController. Correct it. if (hot_button && !hot_button->IsHotTracked()) { diff --git a/chromium/ui/views/controls/menu/menu_controller.h b/chromium/ui/views/controls/menu/menu_controller.h index 06fc19b5c90..2a58d296567 100644 --- a/chromium/ui/views/controls/menu/menu_controller.h +++ b/chromium/ui/views/controls/menu/menu_controller.h @@ -238,7 +238,7 @@ class VIEWS_EXPORT MenuController // Used to capture a hot tracked child button when a nested menu is opened // and to restore the hot tracked state when exiting a nested menu. - CustomButton* hot_button; + Button* hot_button; // If item has a submenu this indicates if the submenu is showing. bool submenu_open; @@ -545,7 +545,7 @@ class VIEWS_EXPORT MenuController SelectionIncrementDirectionType direction); // Updates the current |hot_button_| and its hot tracked state. - void SetHotTrackedButton(CustomButton* hot_button); + void SetHotTrackedButton(Button* hot_button); // The active instance. static MenuController* active_instance_; @@ -646,7 +646,7 @@ class VIEWS_EXPORT MenuController std::unique_ptr<ViewTracker> active_mouse_view_tracker_; // Current hot tracked child button if any. - CustomButton* hot_button_; + Button* hot_button_; internal::MenuControllerDelegate* delegate_; diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc index c5aa7abc00d..32668272f71 100644 --- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -36,6 +36,7 @@ #if defined(USE_AURA) #include "ui/aura/client/drag_drop_client.h" +#include "ui/aura/client/drag_drop_client_observer.h" #include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window.h" #include "ui/views/controls/menu/menu_pre_target_handler.h" @@ -172,6 +173,10 @@ class TestDragDropClient : public aura::client::DragDropClient { void DragCancel() override; bool IsDragDropInProgress() override; + void AddObserver(aura::client::DragDropClientObserver* observer) override {} + void RemoveObserver(aura::client::DragDropClientObserver* observer) override { + } + private: base::Closure start_drag_and_drop_callback_; bool drag_in_progress_; @@ -489,11 +494,9 @@ class MenuControllerTest : public ViewsTestBase { void DestroyMenuItem() { menu_item_.reset(); } - CustomButton* GetHotButton() { - return menu_controller_->hot_button_; - } + Button* GetHotButton() { return menu_controller_->hot_button_; } - void SetHotTrackedButton(CustomButton* hot_button) { + void SetHotTrackedButton(Button* hot_button) { menu_controller_->SetHotTrackedButton(hot_button); } @@ -770,14 +773,11 @@ TEST_F(MenuControllerTest, SelectChildButtonView) { AddButtonMenuItems(); View* buttons_view = menu_item()->GetSubmenu()->child_at(4); ASSERT_NE(nullptr, buttons_view); - CustomButton* button1 = - CustomButton::AsCustomButton(buttons_view->child_at(0)); + Button* button1 = Button::AsButton(buttons_view->child_at(0)); ASSERT_NE(nullptr, button1); - CustomButton* button2 = - CustomButton::AsCustomButton(buttons_view->child_at(1)); + Button* button2 = Button::AsButton(buttons_view->child_at(1)); ASSERT_NE(nullptr, button2); - CustomButton* button3 = - CustomButton::AsCustomButton(buttons_view->child_at(2)); + Button* button3 = Button::AsButton(buttons_view->child_at(2)); ASSERT_NE(nullptr, button2); // Handle searching for 'f'; should find "Four". @@ -843,14 +843,11 @@ TEST_F(MenuControllerTest, DeleteChildButtonView) { View* buttons_view = menu_item()->GetSubmenu()->child_at(4); ASSERT_NE(nullptr, buttons_view); - CustomButton* button1 = - CustomButton::AsCustomButton(buttons_view->child_at(0)); + Button* button1 = Button::AsButton(buttons_view->child_at(0)); ASSERT_NE(nullptr, button1); - CustomButton* button2 = - CustomButton::AsCustomButton(buttons_view->child_at(1)); + Button* button2 = Button::AsButton(buttons_view->child_at(1)); ASSERT_NE(nullptr, button2); - CustomButton* button3 = - CustomButton::AsCustomButton(buttons_view->child_at(2)); + Button* button3 = Button::AsButton(buttons_view->child_at(2)); ASSERT_NE(nullptr, button2); EXPECT_FALSE(button1->IsHotTracked()); EXPECT_FALSE(button2->IsHotTracked()); @@ -877,7 +874,7 @@ TEST_F(MenuControllerTest, DeleteChildButtonView) { EXPECT_FALSE(button3->IsHotTracked()); } -// Creates a menu with CustomButton child views, simulates running a nested +// Creates a menu with Button child views, simulates running a nested // menu and tests that existing the nested run restores hot-tracked child view. TEST_F(MenuControllerTest, ChildButtonHotTrackedWhenNested) { AddButtonMenuItems(); @@ -888,14 +885,11 @@ TEST_F(MenuControllerTest, ChildButtonHotTrackedWhenNested) { View* buttons_view = menu_item()->GetSubmenu()->child_at(4); ASSERT_NE(nullptr, buttons_view); - CustomButton* button1 = - CustomButton::AsCustomButton(buttons_view->child_at(0)); + Button* button1 = Button::AsButton(buttons_view->child_at(0)); ASSERT_NE(nullptr, button1); - CustomButton* button2 = - CustomButton::AsCustomButton(buttons_view->child_at(1)); + Button* button2 = Button::AsButton(buttons_view->child_at(1)); ASSERT_NE(nullptr, button2); - CustomButton* button3 = - CustomButton::AsCustomButton(buttons_view->child_at(2)); + Button* button3 = Button::AsButton(buttons_view->child_at(2)); ASSERT_NE(nullptr, button2); EXPECT_FALSE(button1->IsHotTracked()); EXPECT_FALSE(button2->IsHotTracked()); diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 905690f8d40..fcf1e9aa0b7 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -184,6 +184,13 @@ void MenuItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) { // No additional accessibility states currently for these menu states. break; } + + base::char16 mnemonic = GetMnemonic(); + if (mnemonic != '\0') { + node_data->AddStringAttribute( + ui::AX_ATTR_KEY_SHORTCUTS, + base::UTF16ToUTF8(base::string16(1, mnemonic))); + } } // static @@ -334,8 +341,14 @@ MenuItemView* MenuItemView::AppendMenuItemImpl( } SubmenuView* MenuItemView::CreateSubmenu() { - if (!submenu_) + if (!submenu_) { submenu_ = new SubmenuView(this); + + // Initialize the submenu indicator icon (arrow). + submenu_arrow_image_view_ = new ImageView(); + AddChildView(submenu_arrow_image_view_); + } + return submenu_; } @@ -547,7 +560,11 @@ void MenuItemView::Layout() { int x = width() - (use_right_margin_ ? item_right_margin_ : 0); for (int i = child_count() - 1; i >= 0; --i) { View* child = child_at(i); - if (icon_view_ && (icon_view_ == child)) + if (icon_view_ == child) + continue; + if (radio_check_image_view_ == child) + continue; + if (submenu_arrow_image_view_ == child) continue; int width = child->GetPreferredSize().width(); child->SetBounds(x - width, 0, width, height()); @@ -566,6 +583,22 @@ void MenuItemView::Layout() { (height() + GetTopMargin() - GetBottomMargin() - size.height()) / 2; icon_view_->SetPosition(gfx::Point(x, y)); } + + if (radio_check_image_view_) { + int x = config.item_left_margin + left_icon_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 y = + (height() + GetTopMargin() - GetBottomMargin() - kSubmenuArrowSize) / + 2; + submenu_arrow_image_view_->SetBounds(x, y, config.arrow_width, + kSubmenuArrowSize); + } } } @@ -650,11 +683,25 @@ void MenuItemView::Init(MenuItemView* parent, selected_ = false; command_ = command; submenu_ = NULL; + radio_check_image_view_ = nullptr; + submenu_arrow_image_view_ = nullptr; show_mnemonics_ = false; // Assign our ID, this allows SubmenuItemView to find MenuItemViews. set_id(kMenuItemViewID); has_icons_ = false; + if (type_ == CHECKBOX || type_ == RADIO) { + radio_check_image_view_ = new ImageView(); + bool show_check_radio_icon = + type_ == RADIO || + (type_ == CHECKBOX && GetDelegate()->IsItemChecked(GetCommand())); + radio_check_image_view_->SetVisible(show_check_radio_icon); + AddChildView(radio_check_image_view_); + } + + if (submenu_arrow_image_view_) + submenu_arrow_image_view_->SetVisible(HasSubmenu()); + // Don't request enabled status from the root menu item as it is just // a container for real items. EMPTY items will be disabled. MenuDelegate* root_delegate = GetDelegate(); @@ -715,7 +762,7 @@ const gfx::FontList& MenuItemView::GetFontList() const { void MenuItemView::AddEmptyMenus() { DCHECK(HasSubmenu()); - if (!submenu_->has_children()) { + if (!submenu_->HasVisibleChildren()) { submenu_->AddChildViewAt(new EmptyMenuMenuItem(this), 0); } else { for (int i = 0, item_count = submenu_->GetMenuItemCount(); i < item_count; @@ -774,7 +821,6 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { ui::NativeTheme::ExtraParams()); } - const int icon_x = config.item_left_margin + left_icon_margin_; const int top_margin = GetTopMargin(); const int bottom_margin = GetBottomMargin(); const int available_height = height() - top_margin - bottom_margin; @@ -785,24 +831,10 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { // Render the check. if (type_ == CHECKBOX && delegate->IsItemChecked(GetCommand())) { - gfx::ImageSkia check = GetMenuCheckImage(icon_color); - // Don't use config.check_width here as it's padded - // to force more padding (AURA). - gfx::Rect check_bounds(icon_x, - top_margin + (available_height - check.height()) / 2, - check.width(), - check.height()); - AdjustBoundsForRTLUI(&check_bounds); - canvas->DrawImageInt(check, check_bounds.x(), check_bounds.y()); + radio_check_image_view_->SetImage(GetMenuCheckImage(icon_color)); } else if (type_ == RADIO) { - gfx::ImageSkia image = GetRadioButtonImage( - delegate->IsItemChecked(GetCommand()), render_selection, icon_color); - gfx::Rect radio_bounds(icon_x, - top_margin + (available_height - image.height()) / 2, - image.width(), - image.height()); - AdjustBoundsForRTLUI(&radio_bounds); - canvas->DrawImageInt(image, radio_bounds.x(), radio_bounds.y()); + radio_check_image_view_->SetImage(GetRadioButtonImage( + delegate->IsItemChecked(GetCommand()), render_selection, icon_color)); } // Render the foreground. @@ -835,17 +867,9 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { PaintMinorText(canvas, GetTextColor(true, render_selection, emphasized)); - // Render the submenu indicator (arrow). - if (HasSubmenu()) { - gfx::ImageSkia arrow = GetSubmenuArrowImage(icon_color); - gfx::Rect arrow_bounds(this->width() - config.arrow_width - - config.arrow_to_edge_padding, - top_margin + (available_height - arrow.height()) / 2, - config.arrow_width, - arrow.height()); - AdjustBoundsForRTLUI(&arrow_bounds); - canvas->DrawImageInt(arrow, arrow_bounds.x(), arrow_bounds.y()); - } + // Set the submenu indicator (arrow) image and color. + if (HasSubmenu()) + submenu_arrow_image_view_->SetImage(GetSubmenuArrowImage(icon_color)); } void MenuItemView::PaintMinorText(gfx::Canvas* canvas, SkColor color) { @@ -929,7 +953,11 @@ gfx::Size MenuItemView::GetChildPreferredSize() const { int width = 0; for (int i = 0; i < child_count(); ++i) { const View* child = child_at(i); - if (icon_view_ && (icon_view_ == child)) + if (icon_view_ == child) + continue; + if (radio_check_image_view_ == child) + continue; + if (submenu_arrow_image_view_ == child) continue; if (i) width += kChildXPadding; @@ -1037,7 +1065,9 @@ bool MenuItemView::IsContainer() const { int MenuItemView::NonIconChildViewsCount() const { // Note that what child_count() returns is the number of children, // not the number of menu items. - return child_count() - (icon_view_ ? 1 : 0); + return child_count() - (icon_view_ ? 1 : 0) - + (radio_check_image_view_ ? 1 : 0) - + (submenu_arrow_image_view_ ? 1 : 0); } int MenuItemView::GetMaxIconViewWidth() const { diff --git a/chromium/ui/views/controls/menu/menu_item_view.h b/chromium/ui/views/controls/menu/menu_item_view.h index a23101c16a3..f474f6f9dac 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.h +++ b/chromium/ui/views/controls/menu/menu_item_view.h @@ -43,6 +43,7 @@ class TestMenuItemViewShown; class MenuController; class MenuDelegate; +class TestMenuItemView; class SubmenuView; // MenuItemView -------------------------------------------------------------- @@ -351,6 +352,7 @@ class VIEWS_EXPORT MenuItemView : public View { private: friend class internal::MenuRunnerImpl; // For access to ~MenuItemView. friend class test::TestMenuItemViewShown; // for access to |submenu_|; + friend class TestMenuItemView; // For access to AddEmptyMenus(); enum PaintButtonMode { PB_NORMAL, PB_FOR_DRAG }; @@ -536,6 +538,12 @@ class VIEWS_EXPORT MenuItemView : public View { // containing other elements. bool use_right_margin_; + // Contains an image for the checkbox or radio icon. + ImageView* radio_check_image_view_; + + // The submenu indicator arrow icon in case the menu item has a Submenu. + ImageView* submenu_arrow_image_view_; + DISALLOW_COPY_AND_ASSIGN(MenuItemView); }; diff --git a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc index 7c9151f6625..ab5f10ec455 100644 --- a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc @@ -7,8 +7,11 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/strings/grit/ui_strings.h" #include "ui/views/controls/menu/submenu_view.h" -#include "ui/views/view.h" + +namespace views { namespace { @@ -23,15 +26,20 @@ class SquareView : public views::View { int GetHeightForWidth(int width) const override { return width; } }; +} // namespace + // A MenuItemView implementation with a public destructor (so we can clean up // in tests). -class TestMenuItemView : public views::MenuItemView { +class TestMenuItemView : public MenuItemView { public: - TestMenuItemView() : views::MenuItemView(NULL) {} + TestMenuItemView() : MenuItemView(NULL) {} ~TestMenuItemView() override {} -}; -} // namespace + void AddEmptyMenus() { MenuItemView::AddEmptyMenus(); } + + private: + DISALLOW_COPY_AND_ASSIGN(TestMenuItemView); +}; TEST(MenuItemViewUnitTest, TestMenuItemViewWithFlexibleWidthChild) { TestMenuItemView root_menu; @@ -70,3 +78,69 @@ TEST(MenuItemViewUnitTest, TestMenuItemViewWithFlexibleWidthChild) { EXPECT_EQ(label_size.height() + flex_height, submenu->GetPreferredSize().height()); } + +// Tests that the top-level menu item with hidden children should contain the +// "(empty)" menu item to display. +TEST(MenuItemViewUnitTest, TestEmptyTopLevelWhenAllItemsAreHidden) { + TestMenuItemView root_menu; + views::MenuItemView* item1 = + root_menu.AppendMenuItemWithLabel(1, base::ASCIIToUTF16("item 1")); + views::MenuItemView* item2 = + root_menu.AppendMenuItemWithLabel(2, base::ASCIIToUTF16("item 2")); + + // Set menu items to hidden. + item1->SetVisible(false); + item2->SetVisible(false); + + SubmenuView* submenu = root_menu.GetSubmenu(); + ASSERT_TRUE(submenu); + + EXPECT_EQ(2, submenu->child_count()); + + // Adds any empty menu items to the menu, if needed. + root_menu.AddEmptyMenus(); + + // Because all of the submenu's children are hidden, an empty menu item should + // have been added. + ASSERT_EQ(3, submenu->child_count()); + MenuItemView* empty_item = static_cast<MenuItemView*>(submenu->child_at(0)); + ASSERT_TRUE(empty_item); + ASSERT_EQ(MenuItemView::kEmptyMenuItemViewID, empty_item->id()); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_APP_MENU_EMPTY_SUBMENU), + empty_item->title()); +} + +// Tests that submenu with hidden children should contain the "(empty)" menu +// item to display. +TEST(MenuItemViewUnitTest, TestEmptySubmenuWhenAllChildItemsAreHidden) { + TestMenuItemView root_menu; + MenuItemView* submenu_item = + root_menu.AppendSubMenu(1, base::ASCIIToUTF16("My Submenu")); + MenuItemView* child1 = submenu_item->AppendMenuItemWithLabel( + 1, base::ASCIIToUTF16("submenu item 1")); + MenuItemView* child2 = submenu_item->AppendMenuItemWithLabel( + 2, base::ASCIIToUTF16("submenu item 2")); + + // Set submenu children to hidden. + child1->SetVisible(false); + child2->SetVisible(false); + + SubmenuView* submenu = submenu_item->GetSubmenu(); + ASSERT_TRUE(submenu); + + EXPECT_EQ(2, submenu->child_count()); + + // Adds any empty menu items to the menu, if needed. + root_menu.AddEmptyMenus(); + + // Because all of the submenu's children are hidden, an empty menu item should + // have been added. + ASSERT_EQ(3, submenu->child_count()); + MenuItemView* empty_item = static_cast<MenuItemView*>(submenu->child_at(0)); + ASSERT_TRUE(empty_item); + ASSERT_EQ(MenuItemView::kEmptyMenuItemViewID, empty_item->id()); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_APP_MENU_EMPTY_SUBMENU), + empty_item->title()); +} + +} // namespace views diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.h b/chromium/ui/views/controls/menu/menu_runner_impl.h index 78fcff7d482..22e490a1af5 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl.h +++ b/chromium/ui/views/controls/menu/menu_runner_impl.h @@ -32,9 +32,8 @@ class MenuRunnerDestructionTest; namespace internal { // A menu runner implementation that uses views::MenuItemView to show a menu. -class VIEWS_EXPORT MenuRunnerImpl - : NON_EXPORTED_BASE(public MenuRunnerImplInterface), - NON_EXPORTED_BASE(public MenuControllerDelegate) { +class VIEWS_EXPORT MenuRunnerImpl : public MenuRunnerImplInterface, + public MenuControllerDelegate { public: explicit MenuRunnerImpl(MenuItemView* menu); diff --git a/chromium/ui/views/controls/menu/menu_runner_impl_adapter.h b/chromium/ui/views/controls/menu/menu_runner_impl_adapter.h index bcb31cf102b..e70d7b60168 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl_adapter.h +++ b/chromium/ui/views/controls/menu/menu_runner_impl_adapter.h @@ -20,8 +20,7 @@ namespace internal { class MenuRunnerImpl; // Given a MenuModel, adapts MenuRunnerImpl which expects a MenuItemView. -class VIEWS_EXPORT MenuRunnerImplAdapter - : public NON_EXPORTED_BASE(MenuRunnerImplInterface) { +class VIEWS_EXPORT MenuRunnerImplAdapter : public MenuRunnerImplInterface { public: MenuRunnerImplAdapter(ui::MenuModel* menu_model, const base::Closure& on_menu_closed_callback); diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc index 77795b98d52..f7a75314ec4 100644 --- a/chromium/ui/views/controls/menu/submenu_view.cc +++ b/chromium/ui/views/controls/menu/submenu_view.cc @@ -61,6 +61,14 @@ SubmenuView::~SubmenuView() { delete scroll_view_container_; } +bool SubmenuView::HasVisibleChildren() { + for (int i = 0, item_count = GetMenuItemCount(); i < item_count; i++) { + if (GetMenuItemAt(i)->visible()) + return true; + } + return false; +} + int SubmenuView::GetMenuItemCount() { int count = 0; for (int i = 0; i < child_count(); ++i) { @@ -191,8 +199,8 @@ void SubmenuView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->AddState(ui::AX_STATE_VERTICAL); } -void SubmenuView::PaintChildren(const ui::PaintContext& context) { - View::PaintChildren(context); +void SubmenuView::PaintChildren(const PaintInfo& paint_info) { + View::PaintChildren(paint_info); bool paint_drop_indicator = false; if (drop_item_) { @@ -210,7 +218,7 @@ void SubmenuView::PaintChildren(const ui::PaintContext& context) { if (paint_drop_indicator) { gfx::Rect bounds = CalculateDropIndicatorBounds(drop_item_, drop_position_); - ui::PaintRecorder recorder(context, size()); + ui::PaintRecorder recorder(paint_info.context(), size()); recorder.canvas()->FillRect(bounds, kDropIndicatorColor); } } diff --git a/chromium/ui/views/controls/menu/submenu_view.h b/chromium/ui/views/controls/menu/submenu_view.h index 6bfacac7e51..c17c94449e8 100644 --- a/chromium/ui/views/controls/menu/submenu_view.h +++ b/chromium/ui/views/controls/menu/submenu_view.h @@ -51,6 +51,9 @@ class VIEWS_EXPORT SubmenuView : public View, explicit SubmenuView(MenuItemView* parent); ~SubmenuView() override; + // Returns true if the submenu has at least one visible child item. + bool HasVisibleChildren(); + // Returns the number of child views that are MenuItemViews. // MenuItemViews are identified by ID. int GetMenuItemCount(); @@ -69,7 +72,7 @@ class VIEWS_EXPORT SubmenuView : public View, void GetAccessibleNodeData(ui::AXNodeData* node_data) override; // Painting. - void PaintChildren(const ui::PaintContext& context) override; + void PaintChildren(const PaintInfo& paint_info) override; // Drag and drop methods. These are forwarded to the MenuController. bool GetDropFormats( diff --git a/chromium/ui/views/controls/native/native_view_host.cc b/chromium/ui/views/controls/native/native_view_host.cc index e926db1b5e4..64160a2f008 100644 --- a/chromium/ui/views/controls/native/native_view_host.cc +++ b/chromium/ui/views/controls/native/native_view_host.cc @@ -54,6 +54,10 @@ void NativeViewHost::SetPreferredSize(const gfx::Size& size) { PreferredSizeChanged(); } +bool NativeViewHost::SetCornerRadius(int corner_radius) { + return native_wrapper_->SetCornerRadius(corner_radius); +} + void NativeViewHost::NativeViewDestroyed() { // Detach so we can clear our state and notify the native_wrapper_ to release // ref on the native view. diff --git a/chromium/ui/views/controls/native/native_view_host.h b/chromium/ui/views/controls/native/native_view_host.h index 5b0183717b8..d838192ecc4 100644 --- a/chromium/ui/views/controls/native/native_view_host.h +++ b/chromium/ui/views/controls/native/native_view_host.h @@ -50,6 +50,11 @@ class VIEWS_EXPORT NativeViewHost : public View { // 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. + bool SetCornerRadius(int corner_radius); + // Fast resizing will move the native view and clip its visible region, this // will result in white areas and will not resize the content (so scrollbars // will be all wrong and content will flow offscreen). Only use this @@ -70,7 +75,6 @@ class VIEWS_EXPORT NativeViewHost : public View { return fast_resize_at_last_layout_; } - // Accessor for |native_view_|. gfx::NativeView native_view() const { return native_view_; } void NativeViewDestroyed(); 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 1bb47710a2d..40b01d9c3c4 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura.cc @@ -5,13 +5,16 @@ #include "ui/views/controls/native/native_view_host_aura.h" #include "base/logging.h" +#include "build/build_config.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/base/cursor/cursor.h" #include "ui/base/hit_test.h" +#include "ui/compositor/paint_recorder.h" #include "ui/views/controls/native/native_view_host.h" +#include "ui/views/painter.h" #include "ui/views/view_constants_aura.h" #include "ui/views/widget/widget.h" @@ -95,6 +98,7 @@ void NativeViewHostAura::AttachNativeView() { host_->native_view()->SetProperty(views::kHostViewKey, static_cast<View*>(host_)); AddClippingWindow(); + InstallMask(); } void NativeViewHostAura::NativeViewDetaching(bool destroyed) { @@ -132,6 +136,20 @@ 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) { clip_rect_.reset( new gfx::Rect(host_->ConvertRectToWidget(gfx::Rect(x, y, w, h)))); @@ -187,6 +205,13 @@ gfx::NativeCursor NativeViewHostAura::GetCursor(int x, int y) { return gfx::kNullCursor; } +void NativeViewHostAura::OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + if (mask_) + mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size())); +} + void NativeViewHostAura::OnWindowDestroying(aura::Window* window) { DCHECK(window == host_->native_view()); clipping_window_delegate_->set_native_view(NULL); @@ -234,4 +259,13 @@ void NativeViewHostAura::RemoveClippingWindow() { clipping_window_.parent()->RemoveChild(&clipping_window_); } +void NativeViewHostAura::InstallMask() { + if (!mask_) + return; + if (host_->native_view()) { + mask_->layer()->SetBounds(gfx::Rect(host_->native_view()->bounds().size())); + host_->native_view()->layer()->SetMaskLayer(mask_->layer()); + } +} + } // namespace views diff --git a/chromium/ui/views/controls/native/native_view_host_aura.h b/chromium/ui/views/controls/native/native_view_host_aura.h index 45e859082eb..86770c341bf 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.h +++ b/chromium/ui/views/controls/native/native_view_host_aura.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "ui/aura/window.h" #include "ui/aura/window_observer.h" +#include "ui/compositor/layer_owner.h" #include "ui/views/controls/native/native_view_host_wrapper.h" #include "ui/views/views_export.h" @@ -17,8 +18,8 @@ namespace views { class NativeViewHost; // Aura implementation of NativeViewHostWrapper. -class VIEWS_EXPORT NativeViewHostAura : public NativeViewHostWrapper, - public aura::WindowObserver { +class NativeViewHostAura : public NativeViewHostWrapper, + public aura::WindowObserver { public: explicit NativeViewHostAura(NativeViewHost* host); ~NativeViewHostAura() override; @@ -28,6 +29,7 @@ class VIEWS_EXPORT NativeViewHostAura : public NativeViewHostWrapper, void NativeViewDetaching(bool destroyed) override; void AddedToWidget() override; void RemovedFromWidget() override; + bool SetCornerRadius(int corner_radius) override; void InstallClip(int x, int y, int w, int h) override; bool HasInstalledClip() override; void UninstallClip() override; @@ -39,12 +41,14 @@ class VIEWS_EXPORT NativeViewHostAura : public NativeViewHostWrapper, private: friend class NativeViewHostAuraTest; - class ClippingWindowDelegate; // Overridden from aura::WindowObserver: void OnWindowDestroying(aura::Window* window) override; void OnWindowDestroyed(aura::Window* window) override; + void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) override; // Reparents the native view with the clipping window existing between it and // its old parent, so that the fast resize path works. @@ -54,6 +58,9 @@ class VIEWS_EXPORT NativeViewHostAura : public NativeViewHostWrapper, // undoes it. void RemoveClippingWindow(); + // Sets or updates the mask layer on the native view's layer. + void InstallMask(); + // Our associated NativeViewHost. NativeViewHost* host_; @@ -65,6 +72,9 @@ class VIEWS_EXPORT NativeViewHostAura : public NativeViewHostWrapper, aura::Window clipping_window_; std::unique_ptr<gfx::Rect> clip_rect_; + // This mask exists for the sake of SetCornerRadius(). + std::unique_ptr<ui::LayerOwner> mask_; + DISALLOW_COPY_AND_ASSIGN(NativeViewHostAura); }; diff --git a/chromium/ui/views/controls/native/native_view_host_mac.h b/chromium/ui/views/controls/native/native_view_host_mac.h index b47d1c2d441..74039d869f0 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac.h +++ b/chromium/ui/views/controls/native/native_view_host_mac.h @@ -15,7 +15,7 @@ namespace views { class NativeViewHost; // Mac implementation of NativeViewHostWrapper. -class VIEWS_EXPORT NativeViewHostMac : public NativeViewHostWrapper { +class NativeViewHostMac : public NativeViewHostWrapper { public: explicit NativeViewHostMac(NativeViewHost* host); ~NativeViewHostMac() override; @@ -25,6 +25,7 @@ class VIEWS_EXPORT NativeViewHostMac : public NativeViewHostWrapper { void NativeViewDetaching(bool destroyed) override; void AddedToWidget() override; void RemovedFromWidget() override; + bool SetCornerRadius(int corner_radius) override; void InstallClip(int x, int y, int w, int h) override; bool HasInstalledClip() override; void UninstallClip() override; diff --git a/chromium/ui/views/controls/native/native_view_host_mac.mm b/chromium/ui/views/controls/native/native_view_host_mac.mm index 45a315736a4..cb5553b6634 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac.mm +++ b/chromium/ui/views/controls/native/native_view_host_mac.mm @@ -96,6 +96,11 @@ void NativeViewHostMac::RemovedFromWidget() { NativeViewDetaching(false); } +bool NativeViewHostMac::SetCornerRadius(int corner_radius) { + NOTIMPLEMENTED(); + return false; +} + void NativeViewHostMac::InstallClip(int x, int y, int w, int h) { NOTIMPLEMENTED(); } diff --git a/chromium/ui/views/controls/native/native_view_host_wrapper.h b/chromium/ui/views/controls/native/native_view_host_wrapper.h index 1f9330b6cfc..d6c58dd4c62 100644 --- a/chromium/ui/views/controls/native/native_view_host_wrapper.h +++ b/chromium/ui/views/controls/native/native_view_host_wrapper.h @@ -15,7 +15,7 @@ class NativeViewHost; // An interface that implemented by an object that wraps a gfx::NativeView on // a specific platform, used to perform platform specific operations on that // native view when attached, detached, moved and sized. -class VIEWS_EXPORT NativeViewHostWrapper { +class NativeViewHostWrapper { public: virtual ~NativeViewHostWrapper() {} @@ -38,6 +38,10 @@ class VIEWS_EXPORT NativeViewHostWrapper { // rooted at a valid Widget. virtual void RemovedFromWidget() = 0; + // Sets the corner radius for clipping gfx::NativeView. Returns true on + // success or false if the platform doesn't support the operation. + virtual bool SetCornerRadius(int corner_radius) = 0; + // Installs a clip on the gfx::NativeView. These values are in the coordinate // space of the Widget, so if this method is called from ShowWidget // then the values need to be translated. diff --git a/chromium/ui/views/controls/scroll_view.cc b/chromium/ui/views/controls/scroll_view.cc index 61f853fe56d..95f4acca864 100644 --- a/chromium/ui/views/controls/scroll_view.cc +++ b/chromium/ui/views/controls/scroll_view.cc @@ -763,7 +763,7 @@ void ScrollView::ScrollToOffset(const gfx::ScrollOffset& offset) { // but will only be invoked (asynchronously) when a Compositor is present // and commits a frame, which isn't true in some tests. // See http://crbug.com/637521. - OnLayerScrolled(offset); + OnLayerScrolled(offset, contents_->layer()->element_id()); } else { contents_->SetPosition(gfx::Point(-offset.x(), -offset.y())); ScrollHeader(); @@ -787,7 +787,8 @@ void ScrollView::EnableViewPortLayer() { UpdateBackground(); } -void ScrollView::OnLayerScrolled(const gfx::ScrollOffset&) { +void ScrollView::OnLayerScrolled(const gfx::ScrollOffset&, + const cc::ElementId&) { UpdateScrollBarPositions(); ScrollHeader(); } diff --git a/chromium/ui/views/controls/scroll_view.h b/chromium/ui/views/controls/scroll_view.h index a39fbbd002b..dfb8f1e1aeb 100644 --- a/chromium/ui/views/controls/scroll_view.h +++ b/chromium/ui/views/controls/scroll_view.h @@ -172,7 +172,7 @@ class VIEWS_EXPORT ScrollView : public View, public ScrollBarController { bool ScrollsWithLayers() const; // Callback entrypoint when hosted Layers are scrolled by the Compositor. - void OnLayerScrolled(const gfx::ScrollOffset& offset); + void OnLayerScrolled(const gfx::ScrollOffset&, const cc::ElementId&); // Horizontally scrolls the header (if any) to match the contents. void ScrollHeader(); diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h index 5d077737cfa..7c89c6f2580 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar.h @@ -139,7 +139,7 @@ class VIEWS_EXPORT BaseScrollBar : public ScrollBar, // Called when the state of the thumb track changes (e.g. by the user // pressing the mouse button down in it). - void SetThumbTrackState(CustomButton::ButtonState state); + void SetThumbTrackState(Button::ButtonState state); BaseScrollBarThumb* thumb_; diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc index e2befaee43a..9d8b1a92ae6 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.cc @@ -12,10 +12,9 @@ namespace views { BaseScrollBarButton::BaseScrollBarButton(ButtonListener* listener) - : CustomButton(listener), + : Button(listener), repeater_(base::Bind(&BaseScrollBarButton::RepeaterNotifyClick, - base::Unretained(this))) { -} + base::Unretained(this))) {} BaseScrollBarButton::~BaseScrollBarButton() { } diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h index 6e8716bebb5..245d60f0861 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_button.h @@ -5,7 +5,7 @@ #ifndef UI_VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_BUTTON_H_ #define UI_VIEWS_CONTROLS_SCROLLBAR_BASE_SCROLL_BAR_BUTTON_H_ -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "base/macros.h" #include "build/build_config.h" @@ -22,7 +22,7 @@ namespace views { // down on the button. // /////////////////////////////////////////////////////////////////////////////// -class VIEWS_EXPORT BaseScrollBarButton : public CustomButton { +class VIEWS_EXPORT BaseScrollBarButton : public Button { public: explicit BaseScrollBarButton(ButtonListener* listener); ~BaseScrollBarButton() override; diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc index 2cf40e66d18..007df324cc4 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.cc @@ -20,8 +20,7 @@ BaseScrollBarThumb::BaseScrollBarThumb(BaseScrollBar* scroll_bar) : scroll_bar_(scroll_bar), drag_start_position_(-1), mouse_offset_(-1), - state_(CustomButton::STATE_NORMAL) { -} + state_(Button::STATE_NORMAL) {} BaseScrollBarThumb::~BaseScrollBarThumb() { } @@ -60,17 +59,17 @@ int BaseScrollBarThumb::GetPosition() const { } void BaseScrollBarThumb::OnMouseEntered(const ui::MouseEvent& event) { - SetState(CustomButton::STATE_HOVERED); + SetState(Button::STATE_HOVERED); } void BaseScrollBarThumb::OnMouseExited(const ui::MouseEvent& event) { - SetState(CustomButton::STATE_NORMAL); + SetState(Button::STATE_NORMAL); } bool BaseScrollBarThumb::OnMousePressed(const ui::MouseEvent& event) { mouse_offset_ = IsHorizontal() ? event.x() : event.y(); drag_start_position_ = GetPosition(); - SetState(CustomButton::STATE_PRESSED); + SetState(Button::STATE_PRESSED); return true; } @@ -104,19 +103,19 @@ bool BaseScrollBarThumb::OnMouseDragged(const ui::MouseEvent& event) { } void BaseScrollBarThumb::OnMouseReleased(const ui::MouseEvent& event) { - SetState(HitTestPoint(event.location()) ? - CustomButton::STATE_HOVERED : CustomButton::STATE_NORMAL); + SetState(HitTestPoint(event.location()) ? Button::STATE_HOVERED + : Button::STATE_NORMAL); } void BaseScrollBarThumb::OnMouseCaptureLost() { - SetState(CustomButton::STATE_HOVERED); + SetState(Button::STATE_HOVERED); } -CustomButton::ButtonState BaseScrollBarThumb::GetState() const { +Button::ButtonState BaseScrollBarThumb::GetState() const { return state_; } -void BaseScrollBarThumb::SetState(CustomButton::ButtonState state) { +void BaseScrollBarThumb::SetState(Button::ButtonState state) { if (state_ == state) return; diff --git a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h index 023b87ee4a8..bf4fccc9329 100644 --- a/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h +++ b/chromium/ui/views/controls/scrollbar/base_scroll_bar_thumb.h @@ -7,7 +7,7 @@ #include "base/macros.h" #include "ui/gfx/geometry/size.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/scrollbar/scroll_bar.h" #include "ui/views/view.h" @@ -57,9 +57,9 @@ class VIEWS_EXPORT BaseScrollBarThumb : public View { void OnMouseReleased(const ui::MouseEvent& event) override; void OnMouseCaptureLost() override; - CustomButton::ButtonState GetState() const; + Button::ButtonState GetState() const; // Update our state and schedule a repaint when the mouse moves over us. - void SetState(CustomButton::ButtonState state); + void SetState(Button::ButtonState state); virtual void OnStateChanged(); bool IsHorizontal() const; @@ -77,7 +77,7 @@ class VIEWS_EXPORT BaseScrollBarThumb : public View { int mouse_offset_; // The current state of the thumb button. - CustomButton::ButtonState state_; + Button::ButtonState state_; DISALLOW_COPY_AND_ASSIGN(BaseScrollBarThumb); }; diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm index c7e4cd4ac57..6cab3ad1f39 100644 --- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm +++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.mm @@ -103,11 +103,11 @@ CocoaScrollBarThumb::CocoaScrollBarThumb(CocoaScrollBar* scroll_bar) CocoaScrollBarThumb::~CocoaScrollBarThumb() {} bool CocoaScrollBarThumb::IsStateHovered() const { - return GetState() == CustomButton::STATE_HOVERED; + return GetState() == Button::STATE_HOVERED; } bool CocoaScrollBarThumb::IsStatePressed() const { - return GetState() == CustomButton::STATE_PRESSED; + return GetState() == Button::STATE_PRESSED; } gfx::Size CocoaScrollBarThumb::CalculatePreferredSize() const { @@ -154,8 +154,8 @@ void CocoaScrollBarThumb::OnMouseExited(const ui::MouseEvent& event) { // The thumb should remain pressed when dragged, even if the mouse leaves // the scrollview. The thumb will be set back to its hover or normal state // when the mouse is released. - if (GetState() != CustomButton::STATE_PRESSED) - SetState(CustomButton::STATE_NORMAL); + if (GetState() != Button::STATE_PRESSED) + SetState(Button::STATE_NORMAL); } ////////////////////////////////////////////////////////////////// diff --git a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc index 10d1d04e1d8..e62c0c84a1b 100644 --- a/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc +++ b/chromium/ui/views/controls/scrollbar/overlay_scroll_bar.cc @@ -101,12 +101,12 @@ void OverlayScrollBar::Thumb::OnBoundsChanged( const gfx::Rect& previous_bounds) { scroll_bar_->Show(); // Don't start the hide countdown if the thumb is still hovered or pressed. - if (GetState() == CustomButton::STATE_NORMAL) + if (GetState() == Button::STATE_NORMAL) scroll_bar_->StartHideCountdown(); } void OverlayScrollBar::Thumb::OnStateChanged() { - if (GetState() == CustomButton::STATE_NORMAL) { + if (GetState() == Button::STATE_NORMAL) { gfx::Transform translation; const int direction = base::i18n::IsRTL() ? -1 : 1; translation.Translate( diff --git a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc index 3dc0527f771..667eee0e1e1 100644 --- a/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc +++ b/chromium/ui/views/controls/scrollbar/scroll_bar_views.cc @@ -8,7 +8,7 @@ #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/path.h" -#include "ui/views/controls/button/custom_button.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/focusable_border.h" #include "ui/views/controls/scrollbar/base_scroll_bar_button.h" #include "ui/views/controls/scrollbar/base_scroll_bar_thumb.h" @@ -91,7 +91,7 @@ ui::NativeTheme::ExtraParams ScrollBarButton::GetNativeThemeParams() const { ui::NativeTheme::ExtraParams params; switch (state()) { - case CustomButton::STATE_HOVERED: + case Button::STATE_HOVERED: params.scrollbar_arrow.is_hovering = true; break; default: @@ -120,15 +120,15 @@ ui::NativeTheme::Part ScrollBarButton::GetNativeThemePart() const { ui::NativeTheme::State ScrollBarButton::GetNativeThemeState() const { switch (state()) { - case CustomButton::STATE_HOVERED: + case Button::STATE_HOVERED: return ui::NativeTheme::kHovered; - case CustomButton::STATE_PRESSED: + case Button::STATE_PRESSED: return ui::NativeTheme::kPressed; - case CustomButton::STATE_DISABLED: + case Button::STATE_DISABLED: return ui::NativeTheme::kDisabled; - case CustomButton::STATE_NORMAL: + case Button::STATE_NORMAL: return ui::NativeTheme::kNormal; - case CustomButton::STATE_COUNT: + case Button::STATE_COUNT: break; } @@ -165,8 +165,7 @@ void ScrollBarThumb::OnPaint(gfx::Canvas* canvas) { ui::NativeTheme::ExtraParams ScrollBarThumb::GetNativeThemeParams() const { // This gives the behavior we want. ui::NativeTheme::ExtraParams params; - params.scrollbar_thumb.is_hovering = - (GetState() != CustomButton::STATE_HOVERED); + params.scrollbar_thumb.is_hovering = (GetState() != Button::STATE_HOVERED); return params; } @@ -178,15 +177,15 @@ ui::NativeTheme::Part ScrollBarThumb::GetNativeThemePart() const { ui::NativeTheme::State ScrollBarThumb::GetNativeThemeState() const { switch (GetState()) { - case CustomButton::STATE_HOVERED: + case Button::STATE_HOVERED: return ui::NativeTheme::kHovered; - case CustomButton::STATE_PRESSED: + case Button::STATE_PRESSED: return ui::NativeTheme::kPressed; - case CustomButton::STATE_DISABLED: + case Button::STATE_DISABLED: return ui::NativeTheme::kDisabled; - case CustomButton::STATE_NORMAL: + case Button::STATE_NORMAL: return ui::NativeTheme::kNormal; - case CustomButton::STATE_COUNT: + case Button::STATE_COUNT: break; } diff --git a/chromium/ui/views/controls/slider.cc b/chromium/ui/views/controls/slider.cc index eaa903cb64c..8af93e64099 100644 --- a/chromium/ui/views/controls/slider.cc +++ b/chromium/ui/views/controls/slider.cc @@ -20,7 +20,6 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" -#include "ui/native_theme/native_theme.h" #include "ui/views/widget/widget.h" namespace { @@ -184,21 +183,6 @@ void Slider::MoveButtonTo(const gfx::Point& point) { VALUE_CHANGED_BY_USER); } -void Slider::OnPaintFocus(gfx::Canvas* canvas) { - if (!HasFocus()) - return; - - // TODO(estade): make this a glow effect instead: crbug.com/658783 - gfx::Rect focus_bounds = GetLocalBounds(); - focus_bounds.Inset(gfx::Insets(1)); - canvas->DrawSolidFocusRect( - gfx::RectF(focus_bounds), - SkColorSetA(GetNativeTheme()->GetSystemColor( - ui::NativeTheme::kColorId_FocusedBorderColor), - 0x99), - 2); -} - void Slider::OnSliderDragStarted() { SetHighlighted(true); if (listener_) @@ -321,8 +305,6 @@ void Slider::OnPaint(gfx::Canvas* canvas) { canvas->DrawCircle( thumb_center, is_active_ ? kThumbRadius : (kThumbRadius - kThumbStroke / 2), flags); - - OnPaintFocus(canvas); } void Slider::OnFocus() { diff --git a/chromium/ui/views/controls/slider.h b/chromium/ui/views/controls/slider.h index f2dace01954..096b1401836 100644 --- a/chromium/ui/views/controls/slider.h +++ b/chromium/ui/views/controls/slider.h @@ -87,8 +87,6 @@ class VIEWS_EXPORT Slider : public View, public gfx::AnimationDelegate { // Moves the button to the specified point and updates the value accordingly. void MoveButtonTo(const gfx::Point& point); - void OnPaintFocus(gfx::Canvas* canvas); - // Notify the listener_, if not NULL, that dragging started. void OnSliderDragStarted(); diff --git a/chromium/ui/views/controls/table/table_view.cc b/chromium/ui/views/controls/table/table_view.cc index 60657e6988e..e14dd9e9025 100644 --- a/chromium/ui/views/controls/table/table_view.cc +++ b/chromium/ui/views/controls/table/table_view.cc @@ -28,10 +28,8 @@ #include "ui/views/controls/table/table_header.h" #include "ui/views/controls/table/table_utils.h" #include "ui/views/controls/table/table_view_observer.h" - -// Padding around the text (on each side). -static const int kTextVerticalPadding = 3; -static const int kTextHorizontalPadding = 6; +#include "ui/views/layout/layout_provider.h" +#include "ui/views/style/typography.h" // Size of images. static const int kImageSize = 16; @@ -145,11 +143,16 @@ TableView::TableView(ui::TableModel* model, single_selection_(single_selection), select_on_remove_(true), observer_(NULL), - row_height_(font_list_.GetHeight() + kTextVerticalPadding * 2), last_parent_width_(0), layout_width_(0), grouper_(NULL), in_set_visible_column_width_(false) { + constexpr int kTextContext = style::CONTEXT_TABLE_ROW; + constexpr int kTextStyle = style::STYLE_PRIMARY; + font_list_ = style::GetFont(kTextContext, kTextStyle); + row_height_ = LayoutProvider::GetControlHeightForFont(kTextContext, + kTextStyle, font_list_); + for (size_t i = 0; i < columns.size(); ++i) { VisibleColumn visible_column; visible_column.column = columns[i]; @@ -551,6 +554,8 @@ void TableView::OnPaint(gfx::Canvas* canvas) { ui::NativeTheme::kColorId_TableText); const SkColor selected_fg_color = GetNativeTheme()->GetSystemColor( selected_text_color_id(HasFocus())); + const int cell_margin = GetCellMargin(); + const int cell_element_spacing = GetCellElementSpacing(); for (int i = region.min_row; i < region.max_row; ++i) { const int model_index = ViewToModel(i); const bool is_selected = selection_model_.IsSelected(model_index); @@ -558,11 +563,11 @@ void TableView::OnPaint(gfx::Canvas* canvas) { canvas->FillRect(GetRowBounds(i), selected_bg_color); for (int j = region.min_column; j < region.max_column; ++j) { const gfx::Rect cell_bounds(GetCellBounds(i, j)); - int text_x = kTextHorizontalPadding + cell_bounds.x(); + int text_x = cell_margin + cell_bounds.x(); // Provide space for the grouping indicator, but draw it separately. if (j == 0 && grouper_) - text_x += kGroupingIndicatorSize + kTextHorizontalPadding; + text_x += kGroupingIndicatorSize + cell_element_spacing; // Always paint the icon in the first visible column. if (j == 0 && table_type_ == ICON_AND_TEXT) { @@ -575,17 +580,16 @@ void TableView::OnPaint(gfx::Canvas* canvas) { cell_bounds.y() + (cell_bounds.height() - kImageSize) / 2, kImageSize, kImageSize, true); } - text_x += kImageSize + kTextHorizontalPadding; + text_x += kImageSize + cell_element_spacing; } - if (text_x < cell_bounds.right() - kTextHorizontalPadding) { + if (text_x < cell_bounds.right() - cell_margin) { canvas->DrawStringRectWithFlags( model_->GetText(model_index, visible_columns_[j].column.id), font_list_, is_selected ? selected_fg_color : fg_color, gfx::Rect(GetMirroredXWithWidthInView( - text_x, cell_bounds.right() - text_x - kTextHorizontalPadding), - cell_bounds.y() + kTextVerticalPadding, - cell_bounds.right() - text_x, - cell_bounds.height() - kTextVerticalPadding * 2), + text_x, cell_bounds.right() - text_x - cell_margin), + cell_bounds.y(), cell_bounds.right() - text_x, + row_height_), TableColumnAlignmentToCanvasAlignment( visible_columns_[j].column.alignment)); } @@ -601,8 +605,8 @@ void TableView::OnPaint(gfx::Canvas* canvas) { grouping_flags.setColor(grouping_color); grouping_flags.setStyle(cc::PaintFlags::kFill_Style); grouping_flags.setAntiAlias(true); - const int group_indicator_x = GetMirroredXInView(GetCellBounds(0, 0).x() + - kTextHorizontalPadding + kGroupingIndicatorSize / 2); + const int group_indicator_x = GetMirroredXInView( + GetCellBounds(0, 0).x() + cell_margin + kGroupingIndicatorSize / 2); for (int i = region.min_row; i < region.max_row; ) { const int model_index = ViewToModel(i); GroupRange range; @@ -648,6 +652,16 @@ void TableView::OnBlur() { SchedulePaintForSelection(); } +int TableView::GetCellMargin() const { + return LayoutProvider::Get()->GetDistanceMetric( + DISTANCE_TABLE_CELL_HORIZONTAL_MARGIN); +} + +int TableView::GetCellElementSpacing() const { + return LayoutProvider::Get()->GetDistanceMetric( + DISTANCE_RELATED_LABEL_HORIZONTAL); +} + void TableView::NumRowsChanged() { SortItemsAndUpdateMapping(); PreferredSizeChanged(); @@ -707,16 +721,17 @@ gfx::Rect TableView::GetCellBounds(int row, int visible_column_index) const { void TableView::AdjustCellBoundsForText(int visible_column_index, gfx::Rect* bounds) const { - int text_x = kTextHorizontalPadding + bounds->x(); + const int cell_margin = GetCellMargin(); + const int cell_element_spacing = GetCellElementSpacing(); + int text_x = cell_margin + bounds->x(); if (visible_column_index == 0) { if (grouper_) - text_x += kGroupingIndicatorSize + kTextHorizontalPadding; + text_x += kGroupingIndicatorSize + cell_element_spacing; if (table_type_ == ICON_AND_TEXT) - text_x += kImageSize + kTextHorizontalPadding; + text_x += kImageSize + cell_element_spacing; } bounds->set_x(text_x); - bounds->set_width( - std::max(0, bounds->right() - kTextHorizontalPadding - text_x)); + bounds->set_width(std::max(0, bounds->right() - cell_margin - text_x)); } void TableView::CreateHeaderIfNecessary() { @@ -736,15 +751,17 @@ void TableView::UpdateVisibleColumnSizes() { for (size_t i = 0; i < visible_columns_.size(); ++i) columns.push_back(visible_columns_[i].column); + const int cell_margin = GetCellMargin(); + const int cell_element_spacing = GetCellElementSpacing(); int first_column_padding = 0; if (table_type_ == ICON_AND_TEXT && header_) - first_column_padding += kImageSize + kTextHorizontalPadding; + first_column_padding += kImageSize + cell_element_spacing; if (grouper_) - first_column_padding += kGroupingIndicatorSize + kTextHorizontalPadding; + first_column_padding += kGroupingIndicatorSize + cell_element_spacing; std::vector<int> sizes = views::CalculateTableColumnSizes( layout_width_, first_column_padding, header_->font_list(), font_list_, - std::max(kTextHorizontalPadding, TableHeader::kHorizontalPadding) * 2, + std::max(cell_margin, TableHeader::kHorizontalPadding) * 2, TableHeader::kSortIndicatorWidth, columns, model_); DCHECK_EQ(visible_columns_.size(), sizes.size()); int x = 0; @@ -958,8 +975,9 @@ bool TableView::GetTooltipImpl(const gfx::Point& location, if (tooltip) *tooltip = text; if (tooltip_origin) { - tooltip_origin->SetPoint(cell_bounds.x(), - cell_bounds.y() + kTextVerticalPadding); + tooltip_origin->SetPoint( + cell_bounds.x(), + cell_bounds.y() + (row_height_ - font_list_.GetHeight()) / 2); } return true; } diff --git a/chromium/ui/views/controls/table/table_view.h b/chromium/ui/views/controls/table/table_view.h index 10d9bd8f11a..c8d3ad23f91 100644 --- a/chromium/ui/views/controls/table/table_view.h +++ b/chromium/ui/views/controls/table/table_view.h @@ -219,6 +219,14 @@ class VIEWS_EXPORT TableView ADVANCE_INCREMENT, }; + // Returns the horizontal margin between the bounds of a cell and its + // contents. + int GetCellMargin() const; + + // Returns the horizontal spacing between elements (grouper, icon, and text) + // in a cell. + int GetCellElementSpacing() const; + // Invoked when the number of rows changes in some way. void NumRowsChanged(); diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index 5c562f17a37..94be6ec5ba2 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -803,7 +803,7 @@ bool Textfield::CanHandleAccelerators() const { } void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { - SelectAll(false); + SelectAll(PlatformStyle::kTextfieldScrollsToStartOnFocusChange); } bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) { @@ -1036,6 +1036,11 @@ void Textfield::OnFocus() { void Textfield::OnBlur() { gfx::RenderText* render_text = GetRenderText(); render_text->set_focused(false); + + // If necessary, yank the cursor to the logical start of the textfield. + if (PlatformStyle::kTextfieldScrollsToStartOnFocusChange) + model_->MoveCursorTo(gfx::SelectionModel(0, gfx::CURSOR_FORWARD)); + if (GetInputMethod()) { GetInputMethod()->DetachTextInputClient(this); #if defined(OS_CHROMEOS) @@ -1115,8 +1120,11 @@ void Textfield::WriteDragDataForView(View* sender, if (!ui::XVisualManager::GetInstance()->ArgbVisualAvailable()) color = GetBackgroundColor(); #endif - label.Paint( - ui::CanvasPainter(&bitmap, label.size(), raster_scale, color).context()); + label.Paint(PaintInfo::CreateRootPaintInfo( + ui::CanvasPainter(&bitmap, label.size(), raster_scale, color, + GetWidget()->GetCompositor()->is_pixel_canvas()) + .context(), + label.size())); const gfx::Vector2d kOffset(-15, 0); gfx::ImageSkia image(gfx::ImageSkiaRep(bitmap, raster_scale)); data->provider().SetDragImage(image, kOffset); @@ -1974,6 +1982,9 @@ void Textfield::UpdateCursorVisibility() { void Textfield::UpdateCursorViewPosition() { gfx::Rect location(GetRenderText()->GetUpdatedCursorBounds()); location.set_x(GetMirroredXForRect(location)); + location.set_height( + std::min(location.height(), + GetContentsBounds().height() - location.y() - location.y())); cursor_view_.SetBoundsRect(location); } diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc index f4d2282c55a..39b56544d3c 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.cc +++ b/chromium/ui/views/controls/textfield/textfield_model.cc @@ -256,8 +256,8 @@ namespace { // representing the target clause (on Windows). Returns an invalid range if // there is no such a range. gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) { - for (size_t i = 0; i < composition.underlines.size(); ++i) { - const ui::CompositionUnderline& underline = composition.underlines[i]; + for (size_t i = 0; i < composition.ime_text_spans.size(); ++i) { + const ui::ImeTextSpan& underline = composition.ime_text_spans[i]; if (underline.thick) return gfx::Range(underline.start_offset, underline.end_offset); } @@ -659,8 +659,9 @@ 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 composition underlines. - if (composition.underlines.size() > 0 && composition.underlines[0].color != 0) + // Don't render transparent IME spans. + if (composition.ime_text_spans.size() > 0 && + composition.ime_text_spans[0].underline_color != 0) 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 d3722bb15e4..a28fb1e30a9 100644 --- a/chromium/ui/views/controls/textfield/textfield_model_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_model_unittest.cc @@ -867,7 +867,7 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { ui::CompositionText composition; composition.text = base::ASCIIToUTF16("678"); - composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false)); + composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 3, 0, false)); // Cursor should be at the end of composition when characters are just typed. composition.selection = gfx::Range(3, 3); @@ -881,15 +881,15 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { // Restart composition with targeting "67" in "678". composition.selection = gfx::Range(1, 3); - composition.underlines.clear(); - composition.underlines.push_back(ui::CompositionUnderline(0, 2, 0, true)); - composition.underlines.push_back(ui::CompositionUnderline(2, 3, 0, false)); + 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)); model.SetCompositionText(composition); EXPECT_TRUE(model.HasCompositionText()); EXPECT_TRUE(model.HasSelection()); #if !defined(OS_CHROMEOS) // |composition.selection| is ignored because SetCompositionText checks - // if a bold underline exists first. + // if a thick underline exists first. EXPECT_EQ(gfx::Range(5, 7), model.render_text()->selection()); EXPECT_EQ(7U, model.render_text()->cursor_position()); #else @@ -922,9 +922,9 @@ TEST_F(TextfieldModelTest, CompositionTextTest) { composition_text_confirmed_or_cleared_ = false; model.MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, gfx::SELECTION_NONE); - // Also test the case where a selection exists but a bold underline doesn't. + // Also test the case where a selection exists but a thick underline doesn't. composition.selection = gfx::Range(0, 1); - composition.underlines.clear(); + composition.ime_text_spans.clear(); model.SetCompositionText(composition); EXPECT_STR_EQ("1234567890678", model.text()); EXPECT_TRUE(model.HasSelection()); @@ -1475,7 +1475,7 @@ TEST_F(TextfieldModelTest, UndoRedo_CompositionText) { ui::CompositionText composition; composition.text = base::ASCIIToUTF16("abc"); - composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false)); + composition.ime_text_spans.push_back(ui::ImeTextSpan(0, 3, 0, false)); composition.selection = gfx::Range(2, 3); model.SetText(base::ASCIIToUTF16("ABCDE")); diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.cc b/chromium/ui/views/controls/textfield/textfield_test_api.cc index be45d24cc42..bef918aea45 100644 --- a/chromium/ui/views/controls/textfield/textfield_test_api.cc +++ b/chromium/ui/views/controls/textfield/textfield_test_api.cc @@ -4,6 +4,8 @@ #include "ui/views/controls/textfield/textfield_test_api.h" +#include "ui/gfx/geometry/rect.h" + namespace views { TextfieldTestApi::TextfieldTestApi(Textfield* textfield) @@ -27,4 +29,8 @@ void TextfieldTestApi::ResetTouchSelectionController() { textfield_->touch_selection_controller_.reset(); } +void TextfieldTestApi::SetCursorViewRect(gfx::Rect bounds) { + textfield_->cursor_view_.SetBoundsRect(bounds); +} + } // namespace views diff --git a/chromium/ui/views/controls/textfield/textfield_test_api.h b/chromium/ui/views/controls/textfield/textfield_test_api.h index 8fbc740e73a..8b898715afd 100644 --- a/chromium/ui/views/controls/textfield/textfield_test_api.h +++ b/chromium/ui/views/controls/textfield/textfield_test_api.h @@ -45,7 +45,8 @@ class TextfieldTestApi { return textfield_->cursor_blink_timer_.IsRunning(); } - gfx::Point GetCursorViewOrigin() { return textfield_->cursor_view_.origin(); } + gfx::Rect GetCursorViewRect() { return textfield_->cursor_view_.bounds(); } + void SetCursorViewRect(gfx::Rect bounds); bool IsCursorVisible() const { return textfield_->cursor_view_.visible(); } diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index 40eb7975d50..a58d14fec5f 100644 --- a/chromium/ui/views/controls/textfield/textfield_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -258,18 +258,14 @@ bool MockInputMethod::HasComposition() { } void MockInputMethod::ClearComposition() { - composition_.Clear(); + composition_ = ui::CompositionText(); result_text_.clear(); } // A Textfield wrapper to intercept OnKey[Pressed|Released]() results. class TestTextfield : public views::Textfield { public: - TestTextfield() - : Textfield(), - key_handled_(false), - key_received_(false), - weak_ptr_factory_(this) {} + TestTextfield() = default; // ui::TextInputClient overrides: void InsertChar(const ui::KeyEvent& e) override { @@ -283,13 +279,18 @@ class TestTextfield : public views::Textfield { bool key_handled() const { return key_handled_; } bool key_received() const { return key_received_; } + int event_flags() const { return event_flags_; } - void clear() { key_received_ = key_handled_ = false; } + void clear() { + key_received_ = key_handled_ = false; + event_flags_ = 0; + } private: // views::View override: void OnKeyEvent(ui::KeyEvent* event) override { key_received_ = true; + event_flags_ = event->flags(); // Since Textfield::OnKeyPressed() might destroy |this|, get a weak pointer // and verify it isn't null before writing the bool value to key_handled_. @@ -306,10 +307,11 @@ class TestTextfield : public views::Textfield { EXPECT_FALSE(key_handled_); } - bool key_handled_; - bool key_received_; + bool key_handled_ = false; + bool key_received_ = false; + int event_flags_ = 0; - base::WeakPtrFactory<TestTextfield> weak_ptr_factory_; + base::WeakPtrFactory<TestTextfield> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(TestTextfield); }; @@ -2430,11 +2432,11 @@ TEST_F(TextfieldTest, TextCursorPositionInRTLTest) { InitTextfield(); // LTR-RTL string in RTL context. - int text_cursor_position_prev = test_api_->GetCursorViewOrigin().x(); + int text_cursor_position_prev = test_api_->GetCursorViewRect().x(); SendKeyEvent('a'); SendKeyEvent('b'); EXPECT_STR_EQ("ab", textfield_->text()); - int text_cursor_position_new = test_api_->GetCursorViewOrigin().x(); + int text_cursor_position_new = test_api_->GetCursorViewRect().x(); // Text cursor stays at same place after inserting new charactors in RTL mode. EXPECT_EQ(text_cursor_position_prev, text_cursor_position_new); @@ -2446,11 +2448,11 @@ TEST_F(TextfieldTest, TextCursorPositionInLTRTest) { InitTextfield(); // LTR-RTL string in LTR context. - int text_cursor_position_prev = test_api_->GetCursorViewOrigin().x(); + int text_cursor_position_prev = test_api_->GetCursorViewRect().x(); SendKeyEvent('a'); SendKeyEvent('b'); EXPECT_STR_EQ("ab", textfield_->text()); - int text_cursor_position_new = test_api_->GetCursorViewOrigin().x(); + int text_cursor_position_new = test_api_->GetCursorViewRect().x(); // Text cursor moves to right after inserting new charactors in LTR mode. EXPECT_LT(text_cursor_position_prev, text_cursor_position_new); } @@ -3191,6 +3193,47 @@ TEST_F(TextfieldTest, CursorVisibility) { EXPECT_TRUE(test_api_->IsCursorVisible()); } +// Verify that cursor view height does not exceed the textfield height. +TEST_F(TextfieldTest, CursorViewHeight) { + InitTextfield(); + textfield_->SetBounds(0, 0, 100, 100); + textfield_->SetCursorEnabled(true); + SendKeyEvent('a'); + EXPECT_TRUE(test_api_->IsCursorVisible()); + EXPECT_GT(textfield_->GetVisibleBounds().height(), + test_api_->GetCursorViewRect().height()); + EXPECT_LE(test_api_->GetCursorViewRect().height(), + GetCursorBounds().height()); + + // set the cursor height to be higher than the textfield height, verify that + // UpdateCursorViewPosition update cursor view height currectly. + gfx::Rect cursor_bound(test_api_->GetCursorViewRect()); + cursor_bound.set_height(150); + test_api_->SetCursorViewRect(cursor_bound); + SendKeyEvent('b'); + EXPECT_GT(textfield_->GetVisibleBounds().height(), + test_api_->GetCursorViewRect().height()); + EXPECT_LE(test_api_->GetCursorViewRect().height(), + GetCursorBounds().height()); +} + +// Verify that cursor view height is independent of its parent view height. +TEST_F(TextfieldTest, CursorViewHeightAtDiffDSF) { + InitTextfield(); + textfield_->SetBounds(0, 0, 100, 100); + textfield_->SetCursorEnabled(true); + SendKeyEvent('a'); + EXPECT_TRUE(test_api_->IsCursorVisible()); + int height = test_api_->GetCursorViewRect().height(); + + // update the size of its parent view size and verify that the height of the + // cursor view stays the same. + View* parent = textfield_->parent(); + parent->SetBounds(0, 0, 50, height - 2); + SendKeyEvent('b'); + EXPECT_EQ(height, test_api_->GetCursorViewRect().height()); +} + // Check if the text cursor is always at the end of the textfield after the // text overflows from the textfield. If the textfield size changes, check if // the text cursor's location is updated accordingly. @@ -3270,4 +3313,40 @@ TEST_F(TextfieldTest, SwitchFocusInKeyDown) { EXPECT_EQ(base::ASCIIToUTF16(" "), textfield_->text()); } +TEST_F(TextfieldTest, FocusChangesScrollToStart) { + const std::string& kText = "abcdef"; + InitTextfield(); + textfield_->SetText(ASCIIToUTF16(kText)); + EXPECT_EQ(base::ASCIIToUTF16(std::string()), textfield_->GetSelectedText()); + textfield_->AboutToRequestFocusFromTabTraversal(false); + EXPECT_EQ(base::ASCIIToUTF16(kText), textfield_->GetSelectedText()); + if (PlatformStyle::kTextfieldScrollsToStartOnFocusChange) + EXPECT_EQ(0U, textfield_->GetCursorPosition()); + else + EXPECT_EQ(kText.size(), textfield_->GetCursorPosition()); + + // The OnBlur() behavior below is only meaningful on platforms where textfield + // focus moves on focus change. + if (!PlatformStyle::kTextfieldScrollsToStartOnFocusChange) + return; + + SendKeyEvent(ui::VKEY_RIGHT, true, false); + EXPECT_EQ(1U, textfield_->GetCursorPosition()); + textfield_->OnBlur(); + EXPECT_EQ(0U, textfield_->GetCursorPosition()); +} + +TEST_F(TextfieldTest, SendingDeletePreservesShiftFlag) { + InitTextfield(); + SendKeyPress(ui::VKEY_DELETE, 0); + EXPECT_EQ(0, textfield_->event_flags()); + textfield_->clear(); + + // Ensure the shift modifier propagates for keys that may be subject to native + // key mappings. E.g., on Mac, Delete and Shift+Delete are both + // deleteForward:, but the shift modifier should propagate. + SendKeyPress(ui::VKEY_DELETE, ui::EF_SHIFT_DOWN); + EXPECT_EQ(ui::EF_SHIFT_DOWN, textfield_->event_flags()); +} + } // namespace views diff --git a/chromium/ui/views/corewm/tooltip_controller_unittest.cc b/chromium/ui/views/corewm/tooltip_controller_unittest.cc index 7603750f987..357e7a3809a 100644 --- a/chromium/ui/views/corewm/tooltip_controller_unittest.cc +++ b/chromium/ui/views/corewm/tooltip_controller_unittest.cc @@ -580,7 +580,7 @@ TEST_F(TooltipControllerCaptureTest, DISABLED_CloseOnCaptureLost) { // Disabled on X11 as DesktopScreenX11::GetWindowAtScreenPoint() doesn't // consider z-order. // Disabled on Windows due to failing bots. http://crbug.com/604479 -#if (defined(USE_X11) && !defined(OS_CHROMEOS)) || defined(OS_WIN) +#if defined(USE_X11) || defined(OS_WIN) #define MAYBE_Capture DISABLED_Capture #else #define MAYBE_Capture Capture diff --git a/chromium/ui/views/examples/box_layout_example.h b/chromium/ui/views/examples/box_layout_example.h index 76950459ece..a11fc9e6a80 100644 --- a/chromium/ui/views/examples/box_layout_example.h +++ b/chromium/ui/views/examples/box_layout_example.h @@ -38,7 +38,7 @@ class VIEWS_EXAMPLES_EXPORT BoxLayoutExample : public ExampleBase, void CreateExampleView(View* container) override; private: - friend class ChildPanel; + friend views::examples::ChildPanel; // ButtonListener void ButtonPressed(Button* sender, const ui::Event& event) override; diff --git a/chromium/ui/views/examples/button_example.cc b/chromium/ui/views/examples/button_example.cc index a13bb25eb9e..5a0f96989ec 100644 --- a/chromium/ui/views/examples/button_example.cc +++ b/chromium/ui/views/examples/button_example.cc @@ -127,7 +127,7 @@ void ButtonExample::ButtonPressed(Button* sender, const ui::Event& event) { else if (sender == disabled_button_) LabelButtonPressed(disabled_button_, event); else if (sender == md_button_ || sender == md_default_button_) - static_cast<CustomButton*>(sender)->StartThrobbing(5); + static_cast<Button*>(sender)->StartThrobbing(5); else PrintStatus("Image Button Pressed! count: %d", ++count_); } diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index aca55f27e86..15765edf282 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -9,11 +9,11 @@ #include "base/files/file_path.h" #include "base/i18n/icu_util.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_device_source.h" #include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" #include "base/test/test_discardable_memory_allocator.h" #include "build/build_config.h" #include "components/viz/host/host_frame_sink_manager.h" @@ -73,7 +73,8 @@ int main(int argc, char** argv) { &host_frame_sink_manager_, &frame_sink_manager_); context_factory->set_use_test_surface(false); - base::MessageLoopForUI message_loop; + base::test::ScopedTaskEnvironment scoped_task_environment( + base::test::ScopedTaskEnvironment::MainThreadType::UI); base::i18n::InitializeICU(); diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc index f3b3d0a39fe..ae5e2ab998c 100644 --- a/chromium/ui/views/examples/examples_window.cc +++ b/chromium/ui/views/examples/examples_window.cc @@ -11,7 +11,7 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" -#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/combobox_model.h" @@ -190,7 +190,7 @@ class ExamplesWindowContents : public WidgetDelegateView, void WindowClosing() override { instance_ = NULL; if (operation_ == QUIT_ON_CLOSE) - base::MessageLoop::current()->QuitWhenIdle(); + base::RunLoop::QuitCurrentWhenIdleDeprecated(); } gfx::Size CalculatePreferredSize() const override { return gfx::Size(800, 300); diff --git a/chromium/ui/views/layout/layout_provider.cc b/chromium/ui/views/layout/layout_provider.cc index fd7c9c39604..b467de5fdf8 100644 --- a/chromium/ui/views/layout/layout_provider.cc +++ b/chromium/ui/views/layout/layout_provider.cc @@ -7,6 +7,8 @@ #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" #include "ui/views/views_delegate.h" namespace views { @@ -31,35 +33,39 @@ LayoutProvider* LayoutProvider::Get() { return g_layout_delegate; } +// static +int LayoutProvider::GetControlHeightForFont(int context, + int style, + const gfx::FontList& font) { + return std::max(views::style::GetLineHeight(context, style), + font.GetHeight()) + + Get()->GetDistanceMetric(DISTANCE_CONTROL_TOTAL_VERTICAL_TEXT_PADDING); +} + gfx::Insets LayoutProvider::GetInsetsMetric(int metric) const { DCHECK_LT(metric, VIEWS_INSETS_MAX); switch (metric) { - case InsetsMetric::INSETS_BUBBLE_CONTENTS: + case InsetsMetric::INSETS_DIALOG: return gfx::Insets(13, 13); - case InsetsMetric::INSETS_BUBBLE_TITLE: { - const gfx::Insets bubble_contents = - GetInsetsMetric(INSETS_BUBBLE_CONTENTS); - return gfx::Insets(bubble_contents.top(), bubble_contents.left(), 0, - bubble_contents.right()); - } - case InsetsMetric::INSETS_CHECKBOX_RADIO_BUTTON: - return gfx::Insets(5, 6); case InsetsMetric::INSETS_DIALOG_BUTTON_ROW: { - const gfx::Insets dialog_contents = - GetInsetsMetric(INSETS_DIALOG_CONTENTS); - return gfx::Insets( - 0, dialog_contents.left(), - GetDistanceMetric(DISTANCE_DIALOG_BUTTON_BOTTOM_MARGIN), - dialog_contents.right()); + const gfx::Insets dialog_insets = GetInsetsMetric(INSETS_DIALOG); + return gfx::Insets(0, dialog_insets.left(), dialog_insets.bottom(), + dialog_insets.right()); + } + case InsetsMetric::INSETS_DIALOG_CONTENTS: { + const gfx::Insets dialog_insets = GetInsetsMetric(INSETS_DIALOG); + return gfx::Insets(GetDistanceMetric(DISTANCE_DIALOG_TITLE_TO_CONTENT), + dialog_insets.left(), + GetDistanceMetric(DISTANCE_DIALOG_CONTENT_TO_BUTTONS), + dialog_insets.right()); } - case InsetsMetric::INSETS_DIALOG_CONTENTS: - return gfx::Insets(13, 20); case InsetsMetric::INSETS_DIALOG_TITLE: { - const gfx::Insets dialog_contents = - GetInsetsMetric(INSETS_DIALOG_CONTENTS); - return gfx::Insets(dialog_contents.top(), dialog_contents.left(), 0, - dialog_contents.right()); + const gfx::Insets dialog_insets = GetInsetsMetric(INSETS_DIALOG); + return gfx::Insets(dialog_insets.top(), dialog_insets.left(), 0, + dialog_insets.right()); } + case InsetsMetric::INSETS_CHECKBOX_RADIO_BUTTON: + return gfx::Insets(5, 6); case InsetsMetric::INSETS_VECTOR_IMAGE_BUTTON: return gfx::Insets(4); } @@ -76,18 +82,25 @@ int LayoutProvider::GetDistanceMetric(int metric) const { return 0; case DistanceMetric::DISTANCE_CLOSE_BUTTON_MARGIN: return 7; + case DistanceMetric::DISTANCE_CONTROL_TOTAL_VERTICAL_TEXT_PADDING: + return 6; + case DistanceMetric::DISTANCE_DIALOG_CONTENT_TO_BUTTONS: + return 13; + case DistanceMetric::DISTANCE_DIALOG_TITLE_TO_CONTENT: + return 13; case DistanceMetric::DISTANCE_RELATED_BUTTON_HORIZONTAL: return 6; case DistanceMetric::DISTANCE_RELATED_CONTROL_HORIZONTAL: return 8; - case DistanceMetric::DISTANCE_BUBBLE_BUTTON_TOP_MARGIN: case DistanceMetric::DISTANCE_RELATED_CONTROL_VERTICAL: return 8; - case DISTANCE_DIALOG_BUTTON_BOTTOM_MARGIN: - return 20; case DistanceMetric::DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH: return 75; - case DISTANCE_UNRELATED_CONTROL_VERTICAL: + case DistanceMetric::DISTANCE_RELATED_LABEL_HORIZONTAL: + return 10; + case DistanceMetric::DISTANCE_TABLE_CELL_HORIZONTAL_MARGIN: + return 10; + case DistanceMetric::DISTANCE_UNRELATED_CONTROL_VERTICAL: return 20; } NOTREACHED(); diff --git a/chromium/ui/views/layout/layout_provider.h b/chromium/ui/views/layout/layout_provider.h index a5d938668e0..5d76dfd32d5 100644 --- a/chromium/ui/views/layout/layout_provider.h +++ b/chromium/ui/views/layout/layout_provider.h @@ -19,20 +19,18 @@ enum InsetsMetric { // actual Insets: the rest are markers. VIEWS_INSETS_START = 0, - // The margins around the contents of a bubble (popover)-style dialog. - INSETS_BUBBLE_CONTENTS = VIEWS_INSETS_START, - // The margins around the title of a bubble (popover)-style dialog. The bottom - // margin is implied by the content insets. - INSETS_BUBBLE_TITLE, // Internal border around checkboxes and radio buttons. - INSETS_CHECKBOX_RADIO_BUTTON, + INSETS_CHECKBOX_RADIO_BUTTON = VIEWS_INSETS_START, + // The margins around the edges of the dialog. + INSETS_DIALOG, // The margins around the button row of a dialog. The top margin is implied - // by the content insets. + // by the content insets and the other margins overlap with INSETS_DIALOG. INSETS_DIALOG_BUTTON_ROW, - // The margins that should be applied around the contents of a dialog. + // The margins around the contents of a dialog. The left and right margins + // overlap with INSETS_DIALOG. INSETS_DIALOG_CONTENTS, // The margins around the icon/title of a dialog. The bottom margin is implied - // by the content insets. + // by the content insets and the other margins overlap with INSETS_DIALOG. INSETS_DIALOG_TITLE, // Padding to add to vector image buttons to increase their click and touch // target size. @@ -51,11 +49,8 @@ enum DistanceMetric { // two types have not been interchanged. VIEWS_DISTANCE_START = VIEWS_INSETS_MAX, - // If a bubble has buttons, this is the margin between them and the rest of - // the content. - DISTANCE_BUBBLE_BUTTON_TOP_MARGIN = VIEWS_DISTANCE_START, // The default padding to add on each side of a button's label. - DISTANCE_BUTTON_HORIZONTAL_PADDING, + DISTANCE_BUTTON_HORIZONTAL_PADDING = VIEWS_DISTANCE_START, // The maximum width a button can have and still influence the sizes of // other linked buttons. This allows short buttons to have linked widths // without long buttons making things overly wide. @@ -63,10 +58,16 @@ enum DistanceMetric { // The distance between a dialog's edge and the close button in the upper // trailing corner. DISTANCE_CLOSE_BUTTON_MARGIN, - // Margin between the bottom edge of a dialog and a contained button. - DISTANCE_DIALOG_BUTTON_BOTTOM_MARGIN, + // The combined vertical padding applied to text in a control. + DISTANCE_CONTROL_TOTAL_VERTICAL_TEXT_PADDING, + // The distance between the bottom of a dialog's content and the top of the + // dialog's button row. + DISTANCE_DIALOG_CONTENT_TO_BUTTONS, // The default minimum width of a dialog button. DISTANCE_DIALOG_BUTTON_MINIMUM_WIDTH, + // The distance between the bottom of a dialog's title and the top of the + // dialog's content. + DISTANCE_DIALOG_TITLE_TO_CONTENT, // The spacing between a pair of related horizontal buttons, used for // dialog layout. DISTANCE_RELATED_BUTTON_HORIZONTAL, @@ -75,6 +76,11 @@ enum DistanceMetric { // The spacing between a pair of related vertical controls, used for // dialog layout. DISTANCE_RELATED_CONTROL_VERTICAL, + // Horizontal spacing between an item such as an icon or checkbox and a + // label related to it. + DISTANCE_RELATED_LABEL_HORIZONTAL, + // Horizontal margin between a table cell and its contents. + DISTANCE_TABLE_CELL_HORIZONTAL_MARGIN, // Vertical spacing between controls that are logically unrelated. DISTANCE_UNRELATED_CONTROL_VERTICAL, @@ -90,6 +96,12 @@ class VIEWS_EXPORT LayoutProvider { // This should never return nullptr. static LayoutProvider* Get(); + // Calculates the control height based on the |font|'s reported glyph height, + // the default line spacing and DISTANCE_CONTROL_TOTAL_VERTICAL_TEXT_PADDING. + static int GetControlHeightForFont(int context, + int style, + const gfx::FontList& font); + // Returns the insets metric according to the given enumeration element. virtual gfx::Insets GetInsetsMetric(int metric) const; diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h index bc831d80f8f..844b8a7d62b 100644 --- a/chromium/ui/views/linux_ui/linux_ui.h +++ b/chromium/ui/views/linux_ui/linux_ui.h @@ -41,6 +41,7 @@ class Border; class DeviceScaleFactorObserver; class LabelButton; class LabelButtonBorder; +class NavButtonProvider; class WindowButtonOrderObserver; // Adapter class with targets to render like different toolkits. Set by any @@ -168,6 +169,10 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, // factor. virtual void RemoveDeviceScaleFactorObserver( DeviceScaleFactorObserver* observer) = 0; + + // Returns a new NavButtonProvider, or nullptr if the underlying + // toolkit does not support drawing client-side navigation buttons. + virtual std::unique_ptr<NavButtonProvider> CreateNavButtonProvider() = 0; }; } // namespace views diff --git a/chromium/ui/views/mus/BUILD.gn b/chromium/ui/views/mus/BUILD.gn index d9242d59cdc..8e836fda997 100644 --- a/chromium/ui/views/mus/BUILD.gn +++ b/chromium/ui/views/mus/BUILD.gn @@ -48,7 +48,6 @@ component("mus") { "//base:i18n", "//base/third_party/dynamic_annotations", "//cc", - "//cc/surfaces", "//mojo/common", "//mojo/public/cpp/bindings", "//net", diff --git a/chromium/ui/views/mus/OWNERS b/chromium/ui/views/mus/OWNERS new file mode 100644 index 00000000000..e2906750656 --- /dev/null +++ b/chromium/ui/views/mus/OWNERS @@ -0,0 +1,5 @@ +per-file interactive_ui_tests_manifest.json=set noparent +per-file interactive_ui_tests_manifest.json=file://ipc/SECURITY_OWNERS + +per-file unittests_manifest.json=set noparent +per-file unittests_manifest.json=file://ipc/SECURITY_OWNERS 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 e7d67121103..b1dd981c66d 100644 --- a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc +++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc @@ -321,8 +321,7 @@ void DesktopWindowTreeHostMus::Init(aura::Window* content_window, } if (!params.accept_events) { - aura::WindowPortMus::Get(window())->SetEventTargetingPolicy( - ui::mojom::EventTargetingPolicy::NONE); + window()->SetEventTargetingPolicy(ui::mojom::EventTargetingPolicy::NONE); } else { aura::WindowPortMus::Get(content_window)->SetCanAcceptDrops(true); } @@ -540,7 +539,7 @@ gfx::Rect DesktopWindowTreeHostMus::GetWorkAreaBoundsInScreen() const { } void DesktopWindowTreeHostMus::SetShape( - std::unique_ptr<SkRegion> native_region) { + std::unique_ptr<Widget::ShapeRects> native_shape) { NOTIMPLEMENTED(); } 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 d9c5620a0f2..149a66cf9c5 100644 --- a/chromium/ui/views/mus/desktop_window_tree_host_mus.h +++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.h @@ -84,7 +84,7 @@ class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus gfx::Rect GetRestoredBounds() const override; std::string GetWorkspace() const override; gfx::Rect GetWorkAreaBoundsInScreen() const override; - void SetShape(std::unique_ptr<SkRegion> native_region) override; + void SetShape(std::unique_ptr<Widget::ShapeRects> native_shape) override; void Activate() override; void Deactivate() override; bool IsActive() const override; 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 ad0db4ba5fe..a8e1fe126a5 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 @@ -5,6 +5,7 @@ #include "ui/views/mus/desktop_window_tree_host_mus.h" #include "base/debug/stack_trace.h" +#include "base/run_loop.h" #include "base/memory/ptr_util.h" #include "ui/aura/client/cursor_client.h" diff --git a/chromium/ui/views/mus/drag_interactive_uitest.cc b/chromium/ui/views/mus/drag_interactive_uitest.cc index a16c1d5b7d6..59a66d4ec21 100644 --- a/chromium/ui/views/mus/drag_interactive_uitest.cc +++ b/chromium/ui/views/mus/drag_interactive_uitest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "services/ui/public/interfaces/window_server_test.mojom.h" #include "services/ui/public/interfaces/window_tree_constants.mojom.h" @@ -153,7 +154,7 @@ TEST_F(DragTestInteractive, DragTest) { aura::test::ChangeCompletionWaiter source_waiter( MusClient::Get()->window_tree_client(), aura::ChangeType::BOUNDS, false); source_widget->SetBounds(gfx::Rect(0, 0, 20, 20)); - source_waiter.Wait(); + ASSERT_TRUE(source_waiter.Wait()); Widget* target_widget = CreateTopLevelFramelessPlatformWidget(); TargetView* target_view = new TargetView; @@ -163,7 +164,7 @@ TEST_F(DragTestInteractive, DragTest) { aura::test::ChangeCompletionWaiter target_waiter( MusClient::Get()->window_tree_client(), aura::ChangeType::BOUNDS, false); target_widget->SetBounds(gfx::Rect(20, 20, 20, 20)); - target_waiter.Wait(); + ASSERT_TRUE(target_waiter.Wait()); auto* dnwa = static_cast<DesktopNativeWidgetAura*>(source_widget->native_widget()); diff --git a/chromium/ui/views/mus/screen_mus.h b/chromium/ui/views/mus/screen_mus.h index a1eae31511d..4987b07edaa 100644 --- a/chromium/ui/views/mus/screen_mus.h +++ b/chromium/ui/views/mus/screen_mus.h @@ -19,9 +19,8 @@ namespace views { class ScreenMusDelegate; // Screen implementation backed by ui::mojom::DisplayManager. -class VIEWS_MUS_EXPORT ScreenMus - : public display::ScreenBase, - public NON_EXPORTED_BASE(ui::mojom::DisplayManagerObserver) { +class VIEWS_MUS_EXPORT ScreenMus : public display::ScreenBase, + public ui::mojom::DisplayManagerObserver { public: explicit ScreenMus(ScreenMusDelegate* delegate); ~ScreenMus() override; diff --git a/chromium/ui/views/paint_info.cc b/chromium/ui/views/paint_info.cc new file mode 100644 index 00000000000..fd749599f49 --- /dev/null +++ b/chromium/ui/views/paint_info.cc @@ -0,0 +1,127 @@ +// 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/paint_info.h" + +namespace views { + +// static +PaintInfo PaintInfo::CreateRootPaintInfo(const ui::PaintContext& root_context, + const gfx::Size& size) { + return PaintInfo(root_context, size); +} + +// static +PaintInfo PaintInfo::CreateChildPaintInfo(const PaintInfo& parent_paint_info, + const gfx::Rect& bounds, + const gfx::Size& parent_size, + ScaleType scale_type) { + return PaintInfo(parent_paint_info, bounds, parent_size, scale_type); +} + +// static +PaintInfo PaintInfo::ClonePaintInfo(const PaintInfo& parent_paint_info) { + return PaintInfo(parent_paint_info, + ui::PaintContext::CLONE_WITHOUT_INVALIDATION); +} + +PaintInfo::PaintInfo(const PaintInfo& other, + ui::PaintContext::CloneWithoutInvalidation c) + : paint_recording_scale_x_(other.paint_recording_scale_x_), + paint_recording_scale_y_(other.paint_recording_scale_y_), + paint_recording_bounds_(other.paint_recording_bounds_), + offset_from_parent_(other.offset_from_parent_), + context_(other.context(), c), + root_context_(nullptr) {} + +PaintInfo::~PaintInfo() {} + +bool PaintInfo::IsPixelCanvas() const { + return context().is_pixel_canvas(); +} + +PaintInfo::PaintInfo(const PaintInfo& other) + : paint_recording_scale_x_(other.paint_recording_scale_x_), + paint_recording_scale_y_(other.paint_recording_scale_y_), + paint_recording_bounds_(other.paint_recording_bounds_), + offset_from_parent_(other.offset_from_parent_), + context_(other.context(), gfx::Vector2d()), + root_context_(nullptr) {} + +PaintInfo::PaintInfo(const ui::PaintContext& root_context, + const gfx::Size& size) + : paint_recording_scale_x_(root_context.is_pixel_canvas() + ? root_context.device_scale_factor() + : 1.f), + paint_recording_scale_y_(paint_recording_scale_x_), + paint_recording_bounds_( + gfx::ScaleToRoundedRect(gfx::Rect(size), paint_recording_scale_x_)), + context_(root_context, gfx::Vector2d()), + root_context_(&root_context) {} + +PaintInfo::PaintInfo(const PaintInfo& parent_paint_info, + const gfx::Rect& bounds, + const gfx::Size& parent_size, + ScaleType scale_type) + : paint_recording_scale_x_(1.f), + paint_recording_scale_y_(1.f), + paint_recording_bounds_( + parent_paint_info.GetSnappedRecordingBounds(parent_size, bounds)), + offset_from_parent_( + paint_recording_bounds_.OffsetFromOrigin() - + parent_paint_info.paint_recording_bounds_.OffsetFromOrigin()), + context_(parent_paint_info.context(), offset_from_parent_), + root_context_(nullptr) { + if (IsPixelCanvas()) { + if (scale_type == ScaleType::kUniformScaling) { + paint_recording_scale_x_ = paint_recording_scale_y_ = + context().device_scale_factor(); + } else if (scale_type == ScaleType::kScaleWithEdgeSnapping) { + if (bounds.size().width() > 0) { + paint_recording_scale_x_ = + static_cast<float>(paint_recording_bounds_.width()) / + static_cast<float>(bounds.size().width()); + } + if (bounds.size().height() > 0) { + paint_recording_scale_y_ = + static_cast<float>(paint_recording_bounds_.height()) / + static_cast<float>(bounds.size().height()); + } + } + } +} + +gfx::Rect PaintInfo::GetSnappedRecordingBounds( + const gfx::Size& parent_size, + const gfx::Rect& child_bounds) const { + if (!IsPixelCanvas()) + return (child_bounds + paint_recording_bounds_.OffsetFromOrigin()); + + const gfx::Vector2d& child_origin = child_bounds.OffsetFromOrigin(); + + int right = child_origin.x() + child_bounds.width(); + int bottom = child_origin.y() + child_bounds.height(); + + int new_x = std::round(child_origin.x() * context().device_scale_factor()); + int new_y = std::round(child_origin.y() * context().device_scale_factor()); + + int new_right; + int new_bottom; + + if (right == parent_size.width()) + new_right = paint_recording_bounds_.width(); + else + new_right = std::round(right * context().device_scale_factor()); + + if (bottom == parent_size.height()) + new_bottom = paint_recording_bounds_.height(); + else + new_bottom = std::round(bottom * context().device_scale_factor()); + + return gfx::Rect(new_x + paint_recording_bounds_.x(), + new_y + paint_recording_bounds_.y(), new_right - new_x, + new_bottom - new_y); +} + +} // namespace views diff --git a/chromium/ui/views/paint_info.h b/chromium/ui/views/paint_info.h new file mode 100644 index 00000000000..7e38d374d5a --- /dev/null +++ b/chromium/ui/views/paint_info.h @@ -0,0 +1,122 @@ +// 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_VIEWS_PAINT_INFO_H_ +#define UI_VIEWS_PAINT_INFO_H_ + +#include "ui/compositor/paint_context.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/views/views_export.h" + +namespace views { + +// This class manages the context required during View::Paint(). It is +// responsible for setting the paint recording size and the paint recording +// scale factors for an individual View. +// Each PaintInfo instance has paint recording offset relative to a root +// PaintInfo. +// All coordinates are in paint recording space. If pixel canvas is enabled this +// essentially becomes pixel coordinate space. +class VIEWS_EXPORT PaintInfo { + public: + enum class ScaleType { + // Scale the recordings by the default device scale factor while maintaining + // the aspect ratio. Use this when a view contains an image or icon that + // should not get distorted due to scaling. + kUniformScaling = 0, + + // Scale the recordings based on the device scale factor but snap to the + // parent's bottom or right edge whenever possible. This may lead to minor + // distortion and is not recommended to be used with views that contain + // images. + kScaleWithEdgeSnapping + }; + + // Instantiates a root PaintInfo. This should only be initialized at the Paint + // root, ie., a layer or the root of a widget. + static PaintInfo CreateRootPaintInfo(const ui::PaintContext& root_context, + const gfx::Size& size); + + // Instantiate a child PaintInfo instance. All bounds for this object are + // relative to its root PaintInfo. + static PaintInfo CreateChildPaintInfo(const PaintInfo& parent_paint_info, + const gfx::Rect& bounds, + const gfx::Size& parent_size, + ScaleType scale_type); + + // Clones a given paint info, |other|, without the invalidation from its + // PaintContext. + static PaintInfo ClonePaintInfo(const PaintInfo& other); + + PaintInfo(const PaintInfo& other); + ~PaintInfo(); + + // Returns true if all paint commands are recorded at pixel size. + bool IsPixelCanvas() const; + + const ui::PaintContext& context() const { + return root_context_ ? *root_context_ : context_; + } + + gfx::Vector2d offset_from_root() const { + return paint_recording_bounds_.OffsetFromOrigin(); + } + + const gfx::Vector2d& offset_from_parent() const { + return offset_from_parent_; + } + + float paint_recording_scale_x() const { return paint_recording_scale_x_; } + + float paint_recording_scale_y() const { return paint_recording_scale_y_; } + + const gfx::Size& paint_recording_size() const { + return paint_recording_bounds_.size(); + } + + const gfx::Rect& paint_recording_bounds() const { + return paint_recording_bounds_; + } + + private: + friend class PaintInfoTest; + + PaintInfo(const ui::PaintContext& root_context, const gfx::Size& size); + PaintInfo(const PaintInfo& parent_paint_info, + const gfx::Rect& bounds, + const gfx::Size& parent_size, + ScaleType scale_type); + PaintInfo(const PaintInfo& other, + ui::PaintContext::CloneWithoutInvalidation c); + + // Scales the |child_bounds| to its recording bounds based on the + // |context.device_scale_factor()|. The recording bounds are snapped to the + // parent's right and/or bottom edge if required. + // If pixel canvas is disabled, this function returns |child_bounds| as is. + gfx::Rect GetSnappedRecordingBounds(const gfx::Size& parent_size, + const gfx::Rect& child_bounds) const; + + // The scale at which the paint commands are recorded at. Due to the decimal + // rounding and snapping to edges during the scale operation, the effective + // paint recording scale may end up being slightly different between the x and + // y axis. + float paint_recording_scale_x_; + float paint_recording_scale_y_; + + // Paint Recording bounds of the view. The offset is relative to the root + // PaintInfo. + const gfx::Rect paint_recording_bounds_; + + // Offset relative to the parent view's paint recording bounds. Returns 0 + // offset if this is the root. + gfx::Vector2d offset_from_parent_; + + // Compositor PaintContext associated with the view this object belongs to. + ui::PaintContext context_; + const ui::PaintContext* root_context_; +}; + +} // namespace views + +#endif // UI_VIEWS_PAINT_INFO_H_ diff --git a/chromium/ui/views/paint_info_unittest.cc b/chromium/ui/views/paint_info_unittest.cc new file mode 100644 index 00000000000..b0f75630885 --- /dev/null +++ b/chromium/ui/views/paint_info_unittest.cc @@ -0,0 +1,254 @@ +// 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/paint_info.h" + +#include <vector> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "cc/base/region.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/compositor/compositor_switches.h" +#include "ui/compositor/paint_context.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace views { +namespace { + +using PaintInfos = std::vector<std::unique_ptr<PaintInfo>>; + +// Device scale factors +constexpr float DSF100 = 1.f; +constexpr float DSF125 = 1.25f; +constexpr float DSF150 = 1.5f; +constexpr float DSF160 = 1.6f; +constexpr float DSF166 = 1.66f; + +const std::vector<float> kDsfList = {DSF100, DSF125, DSF150, DSF160, DSF166}; + +constexpr gfx::Size kLayerSize(123, 456); + +// ___________ +// | 1 | +// |___________| +// | 3 | 4 | 5 | <-- 2 (encapsulates 3, 4 and 5) +// |___|___|___| +// | 7 | 8 | <-- 6 (encapsulates 7 and 8) +// |_______|___| +// +// |r_0| encapsulates 1, 2 and 6. +const gfx::Rect r_0(kLayerSize); + +constexpr gfx::Rect r_1(0, 0, 123, 152); + +constexpr gfx::Rect r_2(0, 152, 123, 152); +constexpr gfx::Rect r_3(0, 0, 41, 152); +constexpr gfx::Rect r_4(41, 0, 41, 152); +constexpr gfx::Rect r_5(82, 0, 41, 152); + +constexpr gfx::Rect r_6(0, 304, 123, 152); +constexpr gfx::Rect r_7(0, 0, 82, 152); +constexpr gfx::Rect r_8(82, 0, 41, 152); + +// Verifies that the child recording bounds completely cover the parent +// recording bounds. +void VerifyChildBoundsCoversParent(const PaintInfo* parent_paint_info, + const std::vector<PaintInfo*>& info_list) { + cc::Region remaining(gfx::Rect(parent_paint_info->paint_recording_size())); + int times_empty = 0; + for (auto* const paint_info : info_list) { + const gfx::Rect& child_recording_bounds = + paint_info->paint_recording_bounds() - + parent_paint_info->paint_recording_bounds().OffsetFromOrigin(); + EXPECT_TRUE(remaining.Contains(child_recording_bounds)) + << "Remaining: " << remaining.ToString() + << " paint recording bounds: " << child_recording_bounds.ToString(); + remaining.Subtract(child_recording_bounds); + times_empty += remaining.IsEmpty(); + } + EXPECT_EQ(times_empty, 1); +} + +void VerifyPixelCanvasCornerScaling(const PaintInfos& info_list) { + // child 1, child 2 and child 6 should completely cover child 0. + std::vector<PaintInfo*> child_info_list; + child_info_list.push_back(info_list[1].get()); + child_info_list.push_back(info_list[2].get()); + child_info_list.push_back(info_list[6].get()); + VerifyChildBoundsCoversParent(info_list[0].get(), child_info_list); + child_info_list.clear(); + + // Child 3,4 and 5 should completely cover child 2. + child_info_list.push_back(info_list[3].get()); + child_info_list.push_back(info_list[4].get()); + child_info_list.push_back(info_list[5].get()); + VerifyChildBoundsCoversParent(info_list[2].get(), child_info_list); + child_info_list.clear(); + + // Child 7 and 8 should completely cover child 6. + child_info_list.push_back(info_list[7].get()); + child_info_list.push_back(info_list[8].get()); + VerifyChildBoundsCoversParent(info_list[6].get(), child_info_list); + child_info_list.clear(); +} + +void VerifyPixelSizesAreSameAsDIPSize(const PaintInfos& info_list) { + EXPECT_EQ(info_list[0]->paint_recording_size(), r_0.size()); + + EXPECT_EQ(info_list[1]->paint_recording_size(), r_1.size()); + + EXPECT_EQ(info_list[2]->paint_recording_size(), r_2.size()); + EXPECT_EQ(info_list[3]->paint_recording_size(), r_3.size()); + EXPECT_EQ(info_list[4]->paint_recording_size(), r_4.size()); + EXPECT_EQ(info_list[5]->paint_recording_size(), r_5.size()); + + EXPECT_EQ(info_list[6]->paint_recording_size(), r_6.size()); + EXPECT_EQ(info_list[7]->paint_recording_size(), r_7.size()); + EXPECT_EQ(info_list[8]->paint_recording_size(), r_8.size()); +} + +} // namespace + +class PaintInfoTest : public ::testing::Test { + public: + PaintInfoTest() {} + + ~PaintInfoTest() override {} + + // ___________ + // | 1 | + // |___________| + // | 3 | 4 | 5 | <-- 2 (encapsulates 3, 4 and 5) + // |___|___|___| + // | 7 | 8 | <-- 6 (encapsulates 7 and 8) + // |_______|___| + // + // |r_0| encapsulates 1, 2 and 6. + // + // Returns the following arrangement of paint recording bounds for the given + // |dsf| + PaintInfos GetPaintInfoSetup(const ui::PaintContext& context) { + PaintInfos info_list(9); + + info_list[0].reset(new PaintInfo(context, kLayerSize)); + + info_list[1].reset( + new PaintInfo(*info_list[0], r_1, r_0.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + + info_list[2].reset( + new PaintInfo(*info_list[0], r_2, r_0.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + info_list[3].reset( + new PaintInfo(*info_list[2], r_3, r_2.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + info_list[4].reset( + new PaintInfo(*info_list[2], r_4, r_2.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + info_list[5].reset( + new PaintInfo(*info_list[2], r_5, r_2.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + + info_list[6].reset( + new PaintInfo(*info_list[0], r_6, r_0.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + info_list[7].reset( + new PaintInfo(*info_list[6], r_7, r_6.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + info_list[8].reset( + new PaintInfo(*info_list[6], r_8, r_6.size(), + PaintInfo::ScaleType::kScaleWithEdgeSnapping)); + + return info_list; + } + + void VerifyInvalidationRects(float dsf, bool pixel_canvas_enabled) { + std::vector<gfx::Rect> invalidation_rects = { + gfx::Rect(0, 0, 123, 41), // Intersects with 0 & 1. + gfx::Rect(0, 76, 60, 152), // Intersects 0, 1, 2, 3 & 4. + gfx::Rect(41, 152, 41, 152), // Intersects with 0, 2 & 4. + gfx::Rect(80, 320, 4, 4), // Intersects with 0, 6, 7 & 8. + gfx::Rect(40, 151, 43, 154), // Intersects all + gfx::Rect(82, 304, 1, 1), // Intersects with 0, 6 & 8. + gfx::Rect(81, 303, 2, 2) // Intersects with 0, 2, 4, 5, 6, 7, 8 + }; + + std::vector<std::vector<int>> repaint_indices = { + std::vector<int>{0, 1}, + std::vector<int>{0, 1, 2, 3, 4}, + std::vector<int>{0, 2, 4}, + std::vector<int>{0, 6, 7, 8}, + std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8}, + std::vector<int>{0, 6, 8}, + std::vector<int>{0, 2, 4, 5, 6, 7, 8}}; + + PaintInfos info_list; + + EXPECT_EQ(repaint_indices.size(), invalidation_rects.size()); + for (size_t i = 0; i < invalidation_rects.size(); i++) { + ui::PaintContext context(nullptr, dsf, invalidation_rects[i], + pixel_canvas_enabled); + info_list = GetPaintInfoSetup(context); + for (size_t j = 0; j < repaint_indices[i].size(); j++) { + EXPECT_TRUE( + info_list[repaint_indices[i][j]]->context().IsRectInvalid(gfx::Rect( + info_list[repaint_indices[i][j]]->paint_recording_size()))); + } + info_list.clear(); + } + } +}; + +TEST_F(PaintInfoTest, CornerScalingPixelCanvasEnabled) { + PaintInfos info_list; + for (float dsf : kDsfList) { + ui::PaintContext context(nullptr, dsf, gfx::Rect(), true); + info_list = GetPaintInfoSetup(context); + VerifyPixelCanvasCornerScaling(info_list); + info_list.clear(); + } + + // More accurate testing for 1.25 dsf + ui::PaintContext context(nullptr, DSF125, gfx::Rect(), true); + info_list = GetPaintInfoSetup(context); + VerifyPixelCanvasCornerScaling(info_list); + EXPECT_EQ(info_list[0]->paint_recording_size(), gfx::Size(154, 570)); + + EXPECT_EQ(info_list[1]->paint_recording_size(), gfx::Size(154, 190)); + + EXPECT_EQ(info_list[2]->paint_recording_bounds(), + gfx::Rect(0, 190, 154, 190)); + EXPECT_EQ(info_list[3]->paint_recording_size(), gfx::Size(51, 190)); + EXPECT_EQ(info_list[4]->paint_recording_bounds(), + gfx::Rect(51, 190, 52, 190)); + EXPECT_EQ(info_list[5]->paint_recording_bounds(), + gfx::Rect(103, 190, 51, 190)); + + EXPECT_EQ(info_list[6]->paint_recording_bounds(), + gfx::Rect(0, 380, 154, 190)); + EXPECT_EQ(info_list[7]->paint_recording_size(), gfx::Size(103, 190)); + EXPECT_EQ(info_list[8]->paint_recording_bounds(), + gfx::Rect(103, 380, 51, 190)); +} + +TEST_F(PaintInfoTest, ScalingWithPixelCanvasDisabled) { + for (float dsf : kDsfList) { + ui::PaintContext context(nullptr, dsf, gfx::Rect(), false); + PaintInfos info_list = GetPaintInfoSetup(context); + VerifyPixelCanvasCornerScaling(info_list); + VerifyPixelSizesAreSameAsDIPSize(info_list); + info_list.clear(); + } +} + +TEST_F(PaintInfoTest, Invalidation) { + for (float dsf : kDsfList) { + VerifyInvalidationRects(dsf, false); + VerifyInvalidationRects(dsf, true); + } +} + +} // namespace views diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc index d9b9aee168f..d50ef9202bb 100644 --- a/chromium/ui/views/painter.cc +++ b/chromium/ui/views/painter.cc @@ -6,6 +6,10 @@ #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" +#include "ui/compositor/paint_recorder.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/insets_f.h" @@ -186,6 +190,40 @@ void ImagePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { nine_painter_->Paint(canvas, gfx::Rect(size)); } +class PaintedLayer : public ui::LayerOwner, public ui::LayerDelegate { + public: + explicit PaintedLayer(std::unique_ptr<Painter> painter); + ~PaintedLayer() override; + + // LayerDelegate: + void OnPaintLayer(const ui::PaintContext& context) override; + void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override; + void OnDeviceScaleFactorChanged(float device_scale_factor) override; + + private: + std::unique_ptr<Painter> painter_; + + DISALLOW_COPY_AND_ASSIGN(PaintedLayer); +}; + +PaintedLayer::PaintedLayer(std::unique_ptr<Painter> painter) + : painter_(std::move(painter)) { + SetLayer(base::MakeUnique<ui::Layer>(ui::LAYER_TEXTURED)); + layer()->set_delegate(this); +} + +PaintedLayer::~PaintedLayer() {} + +void PaintedLayer::OnPaintLayer(const ui::PaintContext& context) { + ui::PaintRecorder recorder(context, layer()->size()); + painter_->Paint(recorder.canvas(), layer()->size()); +} + +void PaintedLayer::OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) { +} + +void PaintedLayer::OnDeviceScaleFactorChanged(float device_scale_factor) {} + } // namespace @@ -277,4 +315,10 @@ std::unique_ptr<Painter> Painter::CreateSolidFocusPainter( return base::MakeUnique<SolidFocusPainter>(color, thickness, insets); } +// static +std::unique_ptr<ui::LayerOwner> Painter::CreatePaintedLayer( + std::unique_ptr<Painter> painter) { + return base::MakeUnique<PaintedLayer>(std::move(painter)); +} + } // namespace views diff --git a/chromium/ui/views/painter.h b/chromium/ui/views/painter.h index 57b4fb0a132..cd9672304d8 100644 --- a/chromium/ui/views/painter.h +++ b/chromium/ui/views/painter.h @@ -24,6 +24,10 @@ class Rect; class Size; } +namespace ui { +class LayerOwner; +} + namespace views { class View; @@ -89,6 +93,10 @@ class VIEWS_EXPORT Painter { int thickness, const gfx::InsetsF& insets); + // Creates and returns a texture layer that is painted by |painter|. + static std::unique_ptr<ui::LayerOwner> CreatePaintedLayer( + std::unique_ptr<Painter> painter); + // Returns the minimum size this painter can paint without obvious graphical // problems (e.g. overlapping images). virtual gfx::Size GetMinimumSize() const = 0; diff --git a/chromium/ui/views/style/platform_style.cc b/chromium/ui/views/style/platform_style.cc index 0b7ed2f41b5..3f7970d6bf1 100644 --- a/chromium/ui/views/style/platform_style.cc +++ b/chromium/ui/views/style/platform_style.cc @@ -41,15 +41,16 @@ const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = true; const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = true; const bool PlatformStyle::kSelectWordOnRightClick = false; const bool PlatformStyle::kSelectAllOnRightClickWhenUnfocused = false; -const CustomButton::NotifyAction PlatformStyle::kMenuNotifyActivationAction = - CustomButton::NOTIFY_ON_RELEASE; -const CustomButton::KeyClickAction PlatformStyle::kKeyClickActionOnSpace = - CustomButton::CLICK_ON_KEY_RELEASE; +const Button::NotifyAction PlatformStyle::kMenuNotifyActivationAction = + Button::NOTIFY_ON_RELEASE; +const Button::KeyClickAction PlatformStyle::kKeyClickActionOnSpace = + Button::CLICK_ON_KEY_RELEASE; const bool PlatformStyle::kReturnClicksFocusedControl = true; const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = false; const bool PlatformStyle::kTreeViewUsesOpenIcon = true; const bool PlatformStyle::kUseRipples = true; const bool PlatformStyle::kMirrorBubbleArrowInRTLByDefault = true; +const bool PlatformStyle::kTextfieldScrollsToStartOnFocusChange = false; // static std::unique_ptr<ScrollBar> PlatformStyle::CreateScrollBar(bool is_horizontal) { diff --git a/chromium/ui/views/style/platform_style.h b/chromium/ui/views/style/platform_style.h index ce396891725..025c6743782 100644 --- a/chromium/ui/views/style/platform_style.h +++ b/chromium/ui/views/style/platform_style.h @@ -9,7 +9,6 @@ #include "base/macros.h" #include "ui/views/controls/button/button.h" -#include "ui/views/controls/button/custom_button.h" #include "ui/views/views_export.h" namespace views { @@ -42,10 +41,10 @@ class VIEWS_EXPORT PlatformStyle { static const bool kSelectAllOnRightClickWhenUnfocused; // The menu button's action to show the menu. - static const CustomButton::NotifyAction kMenuNotifyActivationAction; + static const Button::NotifyAction kMenuNotifyActivationAction; // Whether the Space key clicks a button on key press or key release. - static const CustomButton::KeyClickAction kKeyClickActionOnSpace; + static const Button::KeyClickAction kKeyClickActionOnSpace; // Whether the Return key clicks the focused control (on key press). // Otherwise, Return does nothing unless it is handled by an accelerator. @@ -66,6 +65,10 @@ class VIEWS_EXPORT PlatformStyle { // opens in the opposite direction. static const bool kMirrorBubbleArrowInRTLByDefault; + // Whether to scroll text fields to the beginning when they gain or lose + // focus. + static const bool kTextfieldScrollsToStartOnFocusChange; + // Creates the default scrollbar for the given orientation. static std::unique_ptr<ScrollBar> CreateScrollBar(bool is_horizontal); diff --git a/chromium/ui/views/style/platform_style_mac.mm b/chromium/ui/views/style/platform_style_mac.mm index e18282bc17f..ab628d6b2c1 100644 --- a/chromium/ui/views/style/platform_style_mac.mm +++ b/chromium/ui/views/style/platform_style_mac.mm @@ -20,6 +20,7 @@ const bool PlatformStyle::kDefaultLabelButtonHasBoldFont = false; const bool PlatformStyle::kDialogDefaultButtonCanBeCancel = false; const bool PlatformStyle::kSelectWordOnRightClick = true; const bool PlatformStyle::kSelectAllOnRightClickWhenUnfocused = true; +const bool PlatformStyle::kTextfieldScrollsToStartOnFocusChange = true; const bool PlatformStyle::kTreeViewSelectionPaintsEntireRow = true; const bool PlatformStyle::kTreeViewUsesOpenIcon = false; const bool PlatformStyle::kUseRipples = false; @@ -30,11 +31,11 @@ const bool PlatformStyle::kUseRipples = false; const bool PlatformStyle::kMirrorBubbleArrowInRTLByDefault = BUILDFLAG(MAC_VIEWS_BROWSER); -const CustomButton::NotifyAction PlatformStyle::kMenuNotifyActivationAction = - CustomButton::NOTIFY_ON_PRESS; +const Button::NotifyAction PlatformStyle::kMenuNotifyActivationAction = + Button::NOTIFY_ON_PRESS; -const CustomButton::KeyClickAction PlatformStyle::kKeyClickActionOnSpace = - CustomButton::CLICK_ON_KEY_PRESS; +const Button::KeyClickAction PlatformStyle::kKeyClickActionOnSpace = + Button::CLICK_ON_KEY_PRESS; // On Mac, the Return key is used to perform the default action even when a // control is focused. diff --git a/chromium/ui/views/style/typography.h b/chromium/ui/views/style/typography.h index 29295a41583..36ee1230bfa 100644 --- a/chromium/ui/views/style/typography.h +++ b/chromium/ui/views/style/typography.h @@ -41,6 +41,9 @@ enum TextContext { // Text to label a control, usually next to it. "Body 2". Usually 12pt. CONTEXT_LABEL, + // Text in a table row. + CONTEXT_TABLE_ROW, + // An editable text field. Usually matches CONTROL_LABEL. CONTEXT_TEXTFIELD, diff --git a/chromium/ui/views/style/typography_provider.cc b/chromium/ui/views/style/typography_provider.cc index f8f7f3774c4..4fd2cd72b08 100644 --- a/chromium/ui/views/style/typography_provider.cc +++ b/chromium/ui/views/style/typography_provider.cc @@ -5,11 +5,16 @@ #include "ui/views/style/typography_provider.h" #include "base/logging.h" +#include "build/build_config.h" #include "ui/base/default_style.h" #include "ui/base/resource/resource_bundle.h" #include "ui/native_theme/native_theme.h" #include "ui/views/style/typography.h" +#if defined(OS_MACOSX) +#include "base/mac/mac_util.h" +#endif + using gfx::Font; namespace views { @@ -30,13 +35,27 @@ Font::Weight GetValueBolderThan(Font::Weight weight) { } // namespace // static -Font::Weight TypographyProvider::WeightNotLighterThanNormal( - Font::Weight weight) { +Font::Weight TypographyProvider::MediumWeightForUI() { +#if defined(OS_MACOSX) + // System fonts are not user-configurable on Mac, so there's a simpler check. + // However, 10.9 and 10.11 do not ship with a MEDIUM weight system font. In + // that case, trying to use MEDIUM there will give a bold font, which will + // look worse with the surrounding NORMAL text than just using NORMAL. + return (base::mac::IsOS10_9() || base::mac::IsOS10_11()) + ? Font::Weight::NORMAL + : Font::Weight::MEDIUM; +#else + // NORMAL may already have at least MEDIUM weight. Return NORMAL in that case + // since trying to return MEDIUM would actually make the font lighter-weight + // than the surrounding text. For example, Windows can be configured to use a + // BOLD font for dialog text; deriving MEDIUM from that would replace the BOLD + // attribute with something lighter. if (ResourceBundle::GetSharedInstance() .GetFontListWithDelta(0, Font::NORMAL, Font::Weight::NORMAL) - .GetFontWeight() < weight) - return weight; + .GetFontWeight() < Font::Weight::MEDIUM) + return Font::Weight::MEDIUM; return Font::Weight::NORMAL; +#endif } const gfx::FontList& DefaultTypographyProvider::GetFont(int context, @@ -90,7 +109,7 @@ void DefaultTypographyProvider::GetDefaultFont(int context, switch (context) { case style::CONTEXT_BUTTON_MD: *size_delta = ui::kLabelFontSizeDelta; - *font_weight = WeightNotLighterThanNormal(Font::Weight::MEDIUM); + *font_weight = MediumWeightForUI(); break; case style::CONTEXT_DIALOG_TITLE: *size_delta = ui::kTitleFontSizeDelta; diff --git a/chromium/ui/views/style/typography_provider.h b/chromium/ui/views/style/typography_provider.h index 978df456f44..ac67e9890bc 100644 --- a/chromium/ui/views/style/typography_provider.h +++ b/chromium/ui/views/style/typography_provider.h @@ -37,13 +37,12 @@ class VIEWS_EXPORT TypographyProvider { // Gets the line spacing, or 0 if it should be provided by gfx::FontList. virtual int GetLineHeight(int context, int style) const = 0; - // The system may indicate a "bold" UI font is preferred (e.g. by selecting - // the "Bold" checkbox in Windows under "Change only the text size" in - // Control Panel). In this case, a user's gfx::Weight::NORMAL font will - // already be bold, and requesting a MEDIUM font will result in a font that is - // less bold. So this method returns NORMAL, if the NORMAL font is at least as - // bold as |weight|. - static gfx::Font::Weight WeightNotLighterThanNormal(gfx::Font::Weight weight); + // Returns the weight that will result in the ResourceBundle returning an + // appropriate "medium" weight for UI. This caters for systems that are known + // to be unable to provide a system font with weight other than NORMAL or BOLD + // and for user configurations where the NORMAL font is already BOLD. In both + // of these cases, NORMAL is returned instead. + static gfx::Font::Weight MediumWeightForUI(); protected: TypographyProvider() = default; diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.cc b/chromium/ui/views/touchui/touch_selection_controller_impl.cc index 0846f6a0c50..4fa1a8a98e8 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.cc @@ -10,6 +10,7 @@ #include "ui/aura/client/cursor_client.h" #include "ui/aura/env.h" #include "ui/aura/window.h" +#include "ui/aura/window_targeter.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/rect.h" @@ -20,7 +21,6 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/wm/core/coordinate_conversion.h" -#include "ui/wm/core/masked_window_targeter.h" namespace { @@ -206,19 +206,19 @@ namespace views { typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView; -class TouchHandleWindowTargeter : public wm::MaskedWindowTargeter { +// A WindowTargeter that shifts the hit-test target down - away from the text +// cursor and expanding the hit-test area just below the visible drag handle. +class TouchHandleWindowTargeter : public aura::WindowTargeter { public: - TouchHandleWindowTargeter(aura::Window* window, - EditingHandleView* handle_view); + TouchHandleWindowTargeter() = default; + ~TouchHandleWindowTargeter() override = default; - ~TouchHandleWindowTargeter() override {} + void SetHitTestOffset(int offset) { + const gfx::Insets insets(offset, 0, -offset, 0); + SetInsets(insets, insets); + } private: - // wm::MaskedWindowTargeter: - bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const override; - - EditingHandleView* handle_view_; - DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter); }; @@ -237,8 +237,8 @@ class TouchSelectionControllerImpl::EditingHandleView widget_.reset(CreateTouchSelectionPopupWidget(context, this)); aura::Window* window = widget_->GetNativeWindow(); - window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>( - new TouchHandleWindowTargeter(window, this))); + targeter_ = new TouchHandleWindowTargeter(); + window->SetEventTargeter(std::unique_ptr<ui::EventTargeter>(targeter_)); // We are owned by the TouchSelectionControllerImpl. set_owned_by_client(); @@ -251,20 +251,6 @@ class TouchSelectionControllerImpl::EditingHandleView } // Overridden from views::WidgetDelegateView: - bool WidgetHasHitTestMask() const override { return true; } - - void GetWidgetHitTestMask(gfx::Path* mask) const override { - gfx::Size image_size = image_->Size(); - mask->addRect( - SkIntToScalar(0), - SkIntToScalar(selection_bound_.GetHeight() + - kSelectionHandleVerticalVisualOffset), - SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding, - SkIntToScalar(selection_bound_.GetHeight() + - kSelectionHandleVerticalVisualOffset + - image_size.height() + kSelectionHandleVertPadding)); - } - void DeleteDelegate() override { // We are owned and deleted by TouchSelectionControllerImpl. } @@ -366,19 +352,20 @@ class TouchSelectionControllerImpl::EditingHandleView SchedulePaint(); } - if (!is_visible) - return; - - selection_bound_.SetEdge(bound.edge_top(), bound.edge_bottom()); + if (is_visible) { + selection_bound_.SetEdge(bound.edge_top(), bound.edge_bottom()); - widget_->SetBounds(GetSelectionWidgetBounds(selection_bound_)); + widget_->SetBounds(GetSelectionWidgetBounds(selection_bound_)); - aura::Window* window = widget_->GetNativeView(); - gfx::Point edge_top = selection_bound_.edge_top_rounded(); - gfx::Point edge_bottom = selection_bound_.edge_bottom_rounded(); - wm::ConvertPointFromScreen(window, &edge_top); - wm::ConvertPointFromScreen(window, &edge_bottom); - selection_bound_.SetEdge(gfx::PointF(edge_top), gfx::PointF(edge_bottom)); + aura::Window* window = widget_->GetNativeView(); + gfx::Point edge_top = selection_bound_.edge_top_rounded(); + gfx::Point edge_bottom = selection_bound_.edge_bottom_rounded(); + wm::ConvertPointFromScreen(window, &edge_top); + wm::ConvertPointFromScreen(window, &edge_bottom); + selection_bound_.SetEdge(gfx::PointF(edge_top), gfx::PointF(edge_bottom)); + } + targeter_->SetHitTestOffset(selection_bound_.GetHeight() + + kSelectionHandleVerticalVisualOffset); } void SetDrawInvisible(bool draw_invisible) { @@ -392,6 +379,12 @@ class TouchSelectionControllerImpl::EditingHandleView std::unique_ptr<Widget> widget_; TouchSelectionControllerImpl* controller_; + // A custom targeter that shifts the hit-test target below the apparent bounds + // to make dragging easier. The |widget_|'s NativeWindow takes ownership over + // the |targeter_| but since the |widget_|'s lifetime is known to this class, + // it can safely access the |targeter_|. + TouchHandleWindowTargeter* targeter_; + // In local coordinates gfx::SelectionBound selection_bound_; gfx::Image* image_; @@ -416,19 +409,6 @@ class TouchSelectionControllerImpl::EditingHandleView DISALLOW_COPY_AND_ASSIGN(EditingHandleView); }; -TouchHandleWindowTargeter::TouchHandleWindowTargeter( - aura::Window* window, - EditingHandleView* handle_view) - : wm::MaskedWindowTargeter(window), - handle_view_(handle_view) { -} - -bool TouchHandleWindowTargeter::GetHitTestMask(aura::Window* window, - gfx::Path* mask) const { - handle_view_->GetWidgetHitTestMask(mask); - return true; -} - TouchSelectionControllerImpl::TouchSelectionControllerImpl( ui::TouchEditable* client_view) : client_view_(client_view), diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index 19b9a6d98a5..db50dc0720c 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -642,19 +642,6 @@ void View::SetLayoutManager(LayoutManager* layout_manager) { layout_manager_->Installed(this); } -void View::SnapLayerToPixelBoundary() { - if (!layer()) - return; - - if (snap_layer_to_pixel_boundary_ && layer()->parent() && - layer()->GetCompositor()) { - ui::SnapLayerToPhysicalPixelBoundary(layer()->parent(), layer()); - } else { - // Reset the offset. - layer()->SetSubpixelPositionOffset(gfx::Vector2dF()); - } -} - // Attributes ------------------------------------------------------------------ const char* View::GetClassName() const { @@ -845,36 +832,46 @@ void View::SchedulePaintInRect(const gfx::Rect& rect) { } } -void View::Paint(const ui::PaintContext& parent_context) { +void View::Paint(const PaintInfo& parent_paint_info) { if (!ShouldPaint()) return; - ui::PaintContext context(parent_context, GetPaintContextOffset()); + const gfx::Rect& parent_bounds = !parent() + ? GetPaintRecordingBounds() + : parent()->GetPaintRecordingBounds(); + + PaintInfo paint_info = PaintInfo::CreateChildPaintInfo( + parent_paint_info, GetPaintRecordingBounds(), parent_bounds.size(), + GetPaintScaleType()); + const ui::PaintContext& context = paint_info.context(); bool is_invalidated = true; - if (context.CanCheckInvalid()) { + if (paint_info.context().CanCheckInvalid()) { #if DCHECK_IS_ON() - gfx::Vector2d offset; - context.Visited(this); - View* view = this; - while (view->parent() && !view->layer()) { - DCHECK(view->GetTransform().IsIdentity()); - offset += view->GetMirroredPosition().OffsetFromOrigin(); - view = view->parent(); + if (!context.is_pixel_canvas()) { + gfx::Vector2d offset; + context.Visited(this); + View* view = this; + while (view->parent() && !view->layer()) { + DCHECK(view->GetTransform().IsIdentity()); + offset += view->GetMirroredPosition().OffsetFromOrigin(); + view = view->parent(); + } + // The offset in the PaintContext should be the offset up to the paint + // root, which we compute and verify here. + DCHECK_EQ(context.PaintOffset().x(), offset.x()); + DCHECK_EQ(context.PaintOffset().y(), offset.y()); + // The above loop will stop when |view| is the paint root, which should be + // the root of the current paint walk, as verified by storing the root in + // the PaintContext. + DCHECK_EQ(context.RootVisited(), view); } - // The offset in the PaintContext should be the offset up to the paint root, - // which we compute and verify here. - DCHECK_EQ(context.PaintOffset().x(), offset.x()); - DCHECK_EQ(context.PaintOffset().y(), offset.y()); - // The above loop will stop when |view| is the paint root, which should be - // the root of the current paint walk, as verified by storing the root in - // the PaintContext. - DCHECK_EQ(context.RootVisited(), view); #endif // If the View wasn't invalidated, don't waste time painting it, the output // would be culled. - is_invalidated = context.IsRectInvalid(GetLocalBounds()); + is_invalidated = + context.IsRectInvalid(gfx::Rect(paint_info.paint_recording_size())); } TRACE_EVENT1("views", "View::Paint", "class", GetClassName()); @@ -883,29 +880,44 @@ void View::Paint(const ui::PaintContext& parent_context) { // rather than relative to its parent. // TODO(danakj): Rework clip and transform recorder usage here to use // std::optional once we can do so. - ui::ClipRecorder clip_recorder(parent_context); + ui::ClipRecorder clip_recorder(parent_paint_info.context()); if (!layer()) { // Set the clip rect to the bounds of this View, or |clip_path_| if it's // been set. Note that the X (or left) position we pass to ClipRect takes // into consideration whether or not the View uses a right-to-left layout so // that we paint the View in its mirrored position if need be. if (clip_path_.isEmpty()) { - clip_recorder.ClipRect(GetMirroredBounds()); + clip_recorder.ClipRect(gfx::Rect(paint_info.paint_recording_size()) + + paint_info.offset_from_parent()); } else { gfx::Path clip_path_in_parent = clip_path_; - clip_path_in_parent.offset(GetMirroredX(), y()); + + // Transform |clip_path_| from local space to parent recording space. + gfx::Transform to_parent_recording_space; + + to_parent_recording_space.Translate(paint_info.offset_from_parent()); + to_parent_recording_space.Scale( + SkFloatToScalar(paint_info.paint_recording_scale_x()), + SkFloatToScalar(paint_info.paint_recording_scale_y())); + + clip_path_in_parent.transform(to_parent_recording_space.matrix()); clip_recorder.ClipPathWithAntiAliasing(clip_path_in_parent); } } ui::TransformRecorder transform_recorder(context); - SetupTransformRecorderForPainting(&transform_recorder); + SetupTransformRecorderForPainting(paint_info.offset_from_parent(), + &transform_recorder); // Note that the cache is not aware of the offset of the view // relative to the parent since painting is always done relative to // the top left of the individual view. - if (is_invalidated || !paint_cache_.UseCache(context, size())) { - ui::PaintRecorder recorder(context, size(), &paint_cache_); + if (is_invalidated || + !paint_cache_.UseCache(context, paint_info.paint_recording_size())) { + ui::PaintRecorder recorder(context, paint_info.paint_recording_size(), + paint_info.paint_recording_scale_x(), + paint_info.paint_recording_scale_y(), + &paint_cache_); gfx::Canvas* canvas = recorder.canvas(); gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, width(), flip_canvas_on_paint_for_rtl_ui_); @@ -915,7 +927,7 @@ void View::Paint(const ui::PaintContext& parent_context) { } // View::Paint() recursion over the subtree. - PaintChildren(context); + PaintChildren(paint_info); } void View::SetBackground(std::unique_ptr<Background> b) { @@ -1543,9 +1555,9 @@ void View::RemovedFromWidget() {} // Painting -------------------------------------------------------------------- -void View::PaintChildren(const ui::PaintContext& context) { +void View::PaintChildren(const PaintInfo& paint_info) { TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName()); - RecursivePaintHelper(&View::Paint, context); + RecursivePaintHelper(&View::Paint, paint_info); } void View::OnPaint(gfx::Canvas* canvas) { @@ -1570,18 +1582,20 @@ void View::OnPaintBorder(gfx::Canvas* canvas) { // Accelerated Painting -------------------------------------------------------- -gfx::Vector2d View::CalculateOffsetToAncestorWithLayer( +View::LayerOffsetData View::CalculateOffsetToAncestorWithLayer( ui::Layer** layer_parent) { if (layer()) { if (layer_parent) *layer_parent = layer(); - return gfx::Vector2d(); + return LayerOffsetData(layer()->device_scale_factor()); } if (!parent_) - return gfx::Vector2d(); + return LayerOffsetData(); - return gfx::Vector2d(GetMirroredX(), y()) + + LayerOffsetData offset_data = parent_->CalculateOffsetToAncestorWithLayer(layer_parent); + + return offset_data + GetMirroredPosition().OffsetFromOrigin(); } void View::UpdateParentLayer() { @@ -1591,25 +1605,27 @@ void View::UpdateParentLayer() { ui::Layer* parent_layer = NULL; gfx::Vector2d offset(GetMirroredX(), y()); - if (parent_) - offset += parent_->CalculateOffsetToAncestorWithLayer(&parent_layer); + if (parent_) { + offset += + parent_->CalculateOffsetToAncestorWithLayer(&parent_layer).offset(); + } ReparentLayer(offset, parent_layer); } void View::MoveLayerToParent(ui::Layer* parent_layer, - const gfx::Point& point) { - gfx::Point local_point(point); + const LayerOffsetData& offset_data) { + LayerOffsetData local_offset_data(offset_data); if (parent_layer != layer()) - local_point.Offset(GetMirroredX(), y()); + local_offset_data += GetMirroredPosition().OffsetFromOrigin(); + if (layer() && parent_layer != layer()) { parent_layer->Add(layer()); - SetLayerBounds(gfx::Rect(local_point.x(), local_point.y(), - width(), height())); + SetLayerBounds(size(), local_offset_data); } else { internal::ScopedChildrenLock lock(this); for (auto* child : children_) - child->MoveLayerToParent(parent_layer, local_point); + child->MoveLayerToParent(parent_layer, local_offset_data); } } @@ -1674,14 +1690,14 @@ void View::NotifyParentsOfLayerChange() { } } -void View::UpdateChildLayerBounds(const gfx::Vector2d& offset) { +void View::UpdateChildLayerBounds(const LayerOffsetData& offset_data) { if (layer()) { - SetLayerBounds(GetLocalBounds() + offset); + SetLayerBounds(size(), offset_data); } else { internal::ScopedChildrenLock lock(this); for (auto* child : children_) { child->UpdateChildLayerBounds( - offset + gfx::Vector2d(child->GetMirroredX(), child->y())); + offset_data + child->GetMirroredPosition().OffsetFromOrigin()); } } } @@ -1697,8 +1713,23 @@ void View::OnDelegatedFrameDamage( void View::OnDeviceScaleFactorChanged(float device_scale_factor) { snap_layer_to_pixel_boundary_ = (device_scale_factor - std::floor(device_scale_factor)) != 0.0f; - SnapLayerToPixelBoundary(); - // Repainting with new scale factor will paint the content at the right scale. + + if (!layer()) + return; + + // There can be no subpixel offset if the layer has no parent. + if (!parent() || !layer()->parent()) + return; + + if (layer()->parent() && layer()->GetCompositor() && + layer()->GetCompositor()->is_pixel_canvas()) { + LayerOffsetData offset_data( + parent()->CalculateOffsetToAncestorWithLayer(nullptr)); + offset_data += GetMirroredPosition().OffsetFromOrigin(); + SnapLayerToPixelBoundary(offset_data); + } else { + SnapLayerToPixelBoundary(LayerOffsetData()); + } } void View::ReorderLayers() { @@ -1817,6 +1848,10 @@ int View::GetVerticalDragThreshold() { return kDefaultVerticalDragThreshold; } +PaintInfo::ScaleType View::GetPaintScaleType() const { + return PaintInfo::ScaleType::kScaleWithEdgeSnapping; +} + // Debugging ------------------------------------------------------------------- #if !defined(NDEBUG) @@ -1969,14 +2004,16 @@ bool View::ShouldPaint() const { return visible_ && !size().IsEmpty(); } -gfx::Vector2d View::GetPaintContextOffset() const { - // If the View has a layer() then it is a paint root. Otherwise, we need to - // add the offset from the parent into the total offset from the paint root. +gfx::Rect View::GetPaintRecordingBounds() const { + // If the View has a layer() then it is a paint root and no offset information + // is needed. Otherwise, we need bounds that includes an offset from the + // parent to add to the total offset from the paint root. DCHECK(layer() || parent() || origin() == gfx::Point()); - return layer() ? gfx::Vector2d() : GetMirroredPosition().OffsetFromOrigin(); + return layer() ? GetLocalBounds() : GetMirroredBounds(); } void View::SetupTransformRecorderForPainting( + const gfx::Vector2d& offset_from_parent, ui::TransformRecorder* recorder) const { // If the view is backed by a layer, it should paint with itself as the origin // rather than relative to its parent. @@ -1986,42 +2023,54 @@ void View::SetupTransformRecorderForPainting( // Translate the graphics such that 0,0 corresponds to where this View is // located relative to its parent. gfx::Transform transform_from_parent; - gfx::Vector2d offset_from_parent = GetMirroredPosition().OffsetFromOrigin(); transform_from_parent.Translate(offset_from_parent.x(), offset_from_parent.y()); - transform_from_parent.PreconcatTransform(GetTransform()); recorder->Transform(transform_from_parent); } -void View::RecursivePaintHelper(void (View::*func)(const ui::PaintContext&), - const ui::PaintContext& context) { +void View::RecursivePaintHelper(void (View::*func)(const PaintInfo&), + const PaintInfo& info) { View::Views children = GetChildrenInZOrder(); DCHECK_EQ(child_count(), static_cast<int>(children.size())); for (auto* child : children) { if (!child->layer()) - (child->*func)(context); + (child->*func)(info); } } void View::PaintFromPaintRoot(const ui::PaintContext& parent_context) { - Paint(parent_context); + PaintInfo paint_info = PaintInfo::CreateRootPaintInfo( + parent_context, layer() ? layer()->size() : size()); + Paint(paint_info); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDrawViewBoundsRects)) - PaintDebugRects(parent_context); + PaintDebugRects(paint_info); } -void View::PaintDebugRects(const ui::PaintContext& parent_context) { +void View::PaintDebugRects(const PaintInfo& parent_paint_info) { if (!ShouldPaint()) return; - ui::PaintContext context(parent_context, GetPaintContextOffset()); + const gfx::Rect& parent_bounds = (layer() || !parent()) + ? GetPaintRecordingBounds() + : parent()->GetPaintRecordingBounds(); + PaintInfo paint_info = PaintInfo::CreateChildPaintInfo( + parent_paint_info, GetPaintRecordingBounds(), parent_bounds.size(), + GetPaintScaleType()); + + const ui::PaintContext& context = paint_info.context(); + ui::TransformRecorder transform_recorder(context); - SetupTransformRecorderForPainting(&transform_recorder); + SetupTransformRecorderForPainting(paint_info.offset_from_parent(), + &transform_recorder); - RecursivePaintHelper(&View::PaintDebugRects, context); + RecursivePaintHelper(&View::PaintDebugRects, paint_info); // Draw outline rects for debugging. - ui::PaintRecorder recorder(context, size()); + ui::PaintRecorder recorder(context, paint_info.paint_recording_size(), + paint_info.paint_recording_scale_x(), + paint_info.paint_recording_scale_y(), + &paint_cache_); gfx::Canvas* canvas = recorder.canvas(); const float scale = canvas->UndoDeviceScaleFactor(); gfx::RectF outline_rect(ScaleToEnclosedRect(GetLocalBounds(), scale)); @@ -2180,6 +2229,23 @@ void View::VisibilityChangedImpl(View* starting_from, bool is_visible) { VisibilityChanged(starting_from, is_visible); } +void View::SnapLayerToPixelBoundary(const LayerOffsetData& offset_data) { + if (!layer()) + return; + + if (snap_layer_to_pixel_boundary_ && layer()->parent() && + layer()->GetCompositor()) { + if (layer()->GetCompositor()->is_pixel_canvas()) { + layer()->SetSubpixelPositionOffset(offset_data.GetSubpixelOffset()); + } else { + ui::SnapLayerToPhysicalPixelBoundary(layer()->parent(), layer()); + } + } else { + // Reset the offset. + layer()->SetSubpixelPositionOffset(gfx::Vector2dF()); + } +} + void View::BoundsChanged(const gfx::Rect& previous_bounds) { if (visible_) { // Paint the new bounds. @@ -2190,11 +2256,13 @@ void View::BoundsChanged(const gfx::Rect& previous_bounds) { if (layer()) { if (parent_) { - SetLayerBounds(GetLocalBounds() + - gfx::Vector2d(GetMirroredX(), y()) + - parent_->CalculateOffsetToAncestorWithLayer(NULL)); + LayerOffsetData offset_data( + parent_->CalculateOffsetToAncestorWithLayer(nullptr)); + offset_data += GetMirroredPosition().OffsetFromOrigin(); + SetLayerBounds(size(), offset_data); } else { - SetLayerBounds(bounds_); + SetLayerBounds(bounds_.size(), + LayerOffsetData() + bounds_.OffsetFromOrigin()); } // In RTL mode, if our width has changed, our children's mirrored bounds @@ -2204,7 +2272,8 @@ void View::BoundsChanged(const gfx::Rect& previous_bounds) { for (int i = 0; i < child_count(); ++i) { View* child = child_at(i); child->UpdateChildLayerBounds( - gfx::Vector2d(child->GetMirroredX(), child->y())); + LayerOffsetData(layer()->device_scale_factor(), + child->GetMirroredPosition().OffsetFromOrigin())); } } } else { @@ -2284,9 +2353,10 @@ void View::RemoveDescendantToNotify(View* view) { descendants_to_notify_.reset(); } -void View::SetLayerBounds(const gfx::Rect& bounds) { - layer()->SetBounds(bounds); - SnapLayerToPixelBoundary(); +void View::SetLayerBounds(const gfx::Size& size, + const LayerOffsetData& offset_data) { + layer()->SetBounds(gfx::Rect(size) + offset_data.offset()); + SnapLayerToPixelBoundary(offset_data); } // Transformations ------------------------------------------------------------- @@ -2423,7 +2493,7 @@ void View::ReparentLayer(const gfx::Vector2d& offset, ui::Layer* parent_layer) { if (parent_layer) parent_layer->Add(layer()); layer()->SchedulePaint(GetLocalBounds()); - MoveLayerToParent(layer(), gfx::Point()); + MoveLayerToParent(layer(), LayerOffsetData(layer()->device_scale_factor())); } // Input ----------------------------------------------------------------------- @@ -2620,15 +2690,6 @@ void View::PropagateThemeChanged() { OnThemeChanged(); } -void View::PropagateLocaleChanged() { - { - internal::ScopedChildrenLock lock(this); - for (auto* child : base::Reversed(children_)) - child->PropagateLocaleChanged(); - } - OnLocaleChanged(); -} - void View::PropagateDeviceScaleFactorChanged(float device_scale_factor) { { internal::ScopedChildrenLock lock(this); diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h index 7a8a7b327eb..33f393275cb 100644 --- a/chromium/ui/views/view.h +++ b/chromium/ui/views/view.h @@ -37,6 +37,7 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/path.h" +#include "ui/views/paint_info.h" #include "ui/views/view_targeter.h" #include "ui/views/views_export.h" @@ -165,6 +166,96 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, View* move_view; }; + // During paint, the origin of each view in physical pixel is calculated by + // view_origin_pixel = ROUND(view.origin() * device_scale_factor) + // + // Thus in a view hierarchy, the offset between two views, view_i and view_j, + // is calculated by: + // view_offset_ij_pixel = SUM [view_origin_pixel.OffsetFromOrigin()] + // {For all views along the path from view_i to view_j} + // + // But the offset between the two layers, the layer in view_i and the layer in + // view_j, is computed by + // view_offset_ij_dip = SUM [view.origin().OffsetFromOrigin()] + // {For all views along the path from view_i to view_j} + // + // layer_offset_ij_pixel = ROUND (view_offset_ij_dip * device_scale_factor) + // + // Due to this difference in the logic for computation of offset, the values + // view_offset_ij_pixel and layer_offset_ij_pixel may not always be equal. + // They will differ by some subpixel_offset. This leads to bugs like + // crbug.com/734787. + // The subpixel offset needs to be applied to the layer to get the correct + // output during paint. + // + // This class manages the computation of subpixel offset internally when + // working with offsets. + class LayerOffsetData { + public: + LayerOffsetData(float device_scale_factor = 1.f, + const gfx::Vector2d& offset = gfx::Vector2d()) + : device_scale_factor_(device_scale_factor) { + AddOffset(offset); + } + + const gfx::Vector2d& offset() const { return offset_; } + + const gfx::Vector2dF GetSubpixelOffset() const { + // |rounded_pixel_offset_| is stored in physical pixel space. Convert it + // into DIP space before returning. + gfx::Vector2dF subpixel_offset(rounded_pixel_offset_); + subpixel_offset.Scale(1.f / device_scale_factor_); + return subpixel_offset; + } + + LayerOffsetData& operator+=(const gfx::Vector2d& offset) { + AddOffset(offset); + return *this; + } + + LayerOffsetData operator+(const gfx::Vector2d& offset) const { + LayerOffsetData offset_data(*this); + offset_data.AddOffset(offset); + return offset_data; + } + + private: + // Adds the |offset_to_parent| to the total |offset_| and updates the + // |rounded_pixel_offset_| value. + void AddOffset(const gfx::Vector2d& offset_to_parent) { + // Add the DIP |offset_to_parent| amount to the total offset. + offset_ += offset_to_parent; + + // Convert |offset_to_parent| to physical pixel coordinates. + gfx::Vector2dF fractional_pixel_offset( + offset_to_parent.x() * device_scale_factor_, + offset_to_parent.y() * device_scale_factor_); + + // Since pixels cannot be fractional, we need to round the offset to get + // the correct physical pixel coordinate. + gfx::Vector2dF integral_pixel_offset( + gfx::ToRoundedInt(fractional_pixel_offset.x()), + gfx::ToRoundedInt(fractional_pixel_offset.y())); + + // |integral_pixel_offset - fractional_pixel_offset| gives the subpixel + // offset amount for |offset_to_parent|. This is added to + // |rounded_pixel_offset_| to update the total subpixel offset. + rounded_pixel_offset_ += integral_pixel_offset - fractional_pixel_offset; + } + + // Total offset so far. This stores the offset between two nodes in the view + // hierarchy. + gfx::Vector2d offset_; + + // This stores the value such that if added to + // |offset_ * device_scale_factor| will give the correct aligned offset in + // physical pixels. + gfx::Vector2dF rounded_pixel_offset_; + + // The device scale factor at which the subpixel offset is being computed. + float device_scale_factor_; + }; + // Creation and lifetime ----------------------------------------------------- View(); @@ -424,10 +515,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, LayoutManager* GetLayoutManager() const; void SetLayoutManager(LayoutManager* layout); - // Adjust the layer's offset so that it snaps to the physical pixel boundary. - // This has no effect if the view does not have an associated layer. - void SnapLayerToPixelBoundary(); - // Attributes ---------------------------------------------------------------- // The view class name. @@ -543,7 +630,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // for View coordinates and language direction as required, allows the View // to paint itself via the various OnPaint*() event handlers and then paints // the hierarchy beneath it. - void Paint(const ui::PaintContext& parent_context); + void Paint(const PaintInfo& parent_paint_info); // The background object may be null. void SetBackground(std::unique_ptr<Background> b); @@ -1152,7 +1239,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Responsible for calling Paint() on child Views. Override to control the // order child Views are painted. - virtual void PaintChildren(const ui::PaintContext& context); + virtual void PaintChildren(const PaintInfo& info); // Override to provide rendering in any part of the View's bounds. Typically // this is the "contents" of the view. If you override this method you will @@ -1167,11 +1254,18 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Override to paint a border not specified by SetBorder(). virtual void OnPaintBorder(gfx::Canvas* canvas); + // Returns the type of scaling to be done for this View. Override this to + // change the default scaling type from |kScaleToFit|. You would want to + // override this for a view and return |kScaleToScaleFactor| in cases where + // scaling should cause no distortion. Such as in the case of an image or + // an icon. + virtual PaintInfo::ScaleType GetPaintScaleType() const; + // Accelerated painting ------------------------------------------------------ // Returns the offset from this view to the nearest ancestor with a layer. If // |layer_parent| is non-NULL it is set to the nearest ancestor with a layer. - virtual gfx::Vector2d CalculateOffsetToAncestorWithLayer( + virtual LayerOffsetData CalculateOffsetToAncestorWithLayer( ui::Layer** layer_parent); // Updates the view's layer's parent. Called when a view is added to a view @@ -1184,11 +1278,12 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // recurses through all children. This is used when adding a layer to an // existing view to make sure all descendants that have layers are parented to // the right layer. - void MoveLayerToParent(ui::Layer* parent_layer, const gfx::Point& point); + void MoveLayerToParent(ui::Layer* parent_layer, + const LayerOffsetData& offset_data); // Called to update the bounds of any child layers within this View's // hierarchy when something happens to the hierarchy. - void UpdateChildLayerBounds(const gfx::Vector2d& offset); + void UpdateChildLayerBounds(const LayerOffsetData& offset_data); // Overridden from ui::LayerDelegate: void OnPaintLayer(const ui::PaintContext& context) override; @@ -1235,11 +1330,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Widget::ThemeChanged(). virtual void OnThemeChanged() {} - // Called when the locale has changed, overriding allows individual Views to - // update locale-dependent strings. - // To dispatch a locale changed notification, call Widget::LocaleChanged(). - virtual void OnLocaleChanged() {} - // Tooltips ------------------------------------------------------------------ // Views must invoke this when the tooltip text they are to display changes. @@ -1296,6 +1386,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, friend class ViewLayerTest; friend class Widget; + FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithMovedViewUsesCache); + FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithMovedViewUsesCacheInRTL); + FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithUnknownInvalidation); + // Painting ----------------------------------------------------------------- enum SchedulePaintType { @@ -1319,17 +1413,19 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // during painting. bool ShouldPaint() const; - // Returns the offset that should be used when constructing the paint context + // Returns the bounds that should be used when constructing the |PaintInfo| // for this view. - gfx::Vector2d GetPaintContextOffset() const; + gfx::Rect GetPaintRecordingBounds() const; // Adjusts the transform of |recorder| in advance of painting. - void SetupTransformRecorderForPainting(ui::TransformRecorder* recorder) const; + void SetupTransformRecorderForPainting( + const gfx::Vector2d& offset_from_parent, + ui::TransformRecorder* recorder) const; // Recursively calls the painting method |func| on all non-layered children, // in Z order. - void RecursivePaintHelper(void (View::*func)(const ui::PaintContext&), - const ui::PaintContext& context); + void RecursivePaintHelper(void (View::*func)(const PaintInfo&), + const PaintInfo& info); // Invokes Paint() and, if necessary, PaintDebugRects(). Should be called // only on the root of a widget/layer. PaintDebugRects() is invoked as a @@ -1340,7 +1436,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // Draws a semitransparent rect to indicate the bounds of this view. // Recursively does the same for all children. Invoked only with // --draw-view-bounds-rects. - void PaintDebugRects(const ui::PaintContext& parent_context); + void PaintDebugRects(const PaintInfo& paint_info); // Tree operations ----------------------------------------------------------- @@ -1412,9 +1508,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, void AddDescendantToNotify(View* view); void RemoveDescendantToNotify(View* view); - // Sets the layer's bounds given in DIP coordinates. - void SetLayerBounds(const gfx::Rect& bounds_in_dip); - // Transformations ----------------------------------------------------------- // Returns in |transform| the transform to get from coordinates of |ancestor| @@ -1492,6 +1585,14 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // this subtree. void OrphanLayers(); + // Adjust the layer's offset so that it snaps to the physical pixel boundary. + // This has no effect if the view does not have an associated layer. + void SnapLayerToPixelBoundary(const LayerOffsetData& offset_data); + + // Sets the layer's bounds given in DIP coordinates. + void SetLayerBounds(const gfx::Size& size_in_dip, + const LayerOffsetData& layer_offset_data); + // Input --------------------------------------------------------------------- bool ProcessMousePressed(const ui::MouseEvent& event); @@ -1525,10 +1626,6 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // views in the hierarchy. void PropagateThemeChanged(); - // Used to propagate locale changed notifications from the root view to all - // views in the hierarchy. - void PropagateLocaleChanged(); - // Used to propagate device scale factor changed notifications from the root // view to all views in the hierarchy. void PropagateDeviceScaleFactorChanged(float device_scale_factor); diff --git a/chromium/ui/views/view_properties.cc b/chromium/ui/views/view_properties.cc index 3f5d1c3539a..7fe9d82e504 100644 --- a/chromium/ui/views/view_properties.cc +++ b/chromium/ui/views/view_properties.cc @@ -6,6 +6,11 @@ #include "ui/gfx/geometry/insets.h" +#if !defined(USE_AURA) +// aura_constants.cc also declared the bool ClassProperty type. +DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, bool); +#endif + DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Insets*); namespace views { diff --git a/chromium/ui/views/view_tracker.cc b/chromium/ui/views/view_tracker.cc index c1391ab2936..4d2032a8eb1 100644 --- a/chromium/ui/views/view_tracker.cc +++ b/chromium/ui/views/view_tracker.cc @@ -8,7 +8,7 @@ namespace views { -ViewTracker::ViewTracker(View* view) : view_(view) { +ViewTracker::ViewTracker(View* view) : view_(nullptr) { SetView(view); } diff --git a/chromium/ui/views/view_tracker_unittest.cc b/chromium/ui/views/view_tracker_unittest.cc index 34f48cf0ac9..998d234e5b9 100644 --- a/chromium/ui/views/view_tracker_unittest.cc +++ b/chromium/ui/views/view_tracker_unittest.cc @@ -4,6 +4,7 @@ #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" @@ -21,4 +22,14 @@ TEST_F(ViewTrackerTest, RemovedOnDelete) { EXPECT_EQ(nullptr, tracker.view()); } +TEST_F(ViewTrackerTest, ObservedAtConstruction) { + std::unique_ptr<ViewTracker> tracker; + { + View view; + tracker = base::MakeUnique<ViewTracker>(&view); + EXPECT_EQ(&view, tracker->view()); + } + EXPECT_EQ(nullptr, tracker->view()); +} + } // namespace views diff --git a/chromium/ui/views/view_unittest.cc b/chromium/ui/views/view_unittest.cc index 1dbb59e20cc..3dcb6b01b5f 100644 --- a/chromium/ui/views/view_unittest.cc +++ b/chromium/ui/views/view_unittest.cc @@ -9,10 +9,12 @@ #include <map> #include <memory> +#include "base/command_line.h" #include "base/i18n/rtl.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/rand_util.h" +#include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" @@ -22,6 +24,7 @@ #include "ui/base/clipboard/clipboard.h" #include "ui/base/l10n/l10n_util.h" #include "ui/compositor/compositor.h" +#include "ui/compositor/compositor_switches.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animator.h" #include "ui/compositor/paint_context.h" @@ -40,6 +43,7 @@ #include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/scroll_view.h" #include "ui/views/controls/textfield/textfield.h" +#include "ui/views/paint_info.h" #include "ui/views/test/views_test_base.h" #include "ui/views/view_observer.h" #include "ui/views/widget/native_widget.h" @@ -527,7 +531,9 @@ TEST_F(ViewTest, PaintEmptyView) { // Paint "everything". gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + first_paint.size())); // The empty view has nothing to paint so it doesn't try build a cache, nor do // its children which would be clipped by its (empty) self. @@ -548,8 +554,8 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { gfx::Rect pixel_rect = gfx::Rect(1, 1); float device_scale_factor = 1.f; auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false)); EXPECT_TRUE(v1->did_paint_); v1->Reset(); // The visual rects for (clip, drawing, transform) should be in layer space. @@ -564,15 +570,15 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { // If invalidation doesn't intersect v1, we paint with the cache. list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false)); EXPECT_FALSE(v1->did_paint_); v1->Reset(); // If invalidation does intersect v1, we don't paint with the cache. list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, v1->bounds())); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, v1->bounds(), false)); EXPECT_TRUE(v1->did_paint_); v1->Reset(); @@ -580,8 +586,8 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { // intersect v1. list = base::MakeRefCounted<cc::DisplayItemList>(); v1->SetX(9); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false)); EXPECT_FALSE(v1->did_paint_); v1->Reset(); item_index = 3; @@ -597,8 +603,8 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCache) { // invalidation. list = base::MakeRefCounted<cc::DisplayItemList>(); v1->SetX(8); - root_view->Paint(ui::PaintContext( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect), + root_view->PaintFromPaintRoot(ui::PaintContext( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false), ui::PaintContext::CLONE_WITHOUT_INVALIDATION)); EXPECT_TRUE(v1->did_paint_); v1->Reset(); @@ -625,8 +631,8 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { gfx::Rect pixel_rect = gfx::Rect(1, 1); float device_scale_factor = 1.f; auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false)); EXPECT_TRUE(v1->did_paint_); v1->Reset(); // The visual rects for (clip, drawing, transform) should be in layer space. @@ -642,15 +648,15 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { // If invalidation doesn't intersect v1, we paint with the cache. list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false)); EXPECT_FALSE(v1->did_paint_); v1->Reset(); // If invalidation does intersect v1, we don't paint with the cache. list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, v1->bounds())); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, v1->bounds(), false)); EXPECT_TRUE(v1->did_paint_); v1->Reset(); @@ -658,8 +664,8 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { // intersect v1. list = base::MakeRefCounted<cc::DisplayItemList>(); v1->SetX(9); - root_view->Paint( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false)); EXPECT_FALSE(v1->did_paint_); v1->Reset(); item_index = 3; @@ -676,8 +682,8 @@ TEST_F(ViewTest, PaintWithMovedViewUsesCacheInRTL) { // invalidation. list = base::MakeRefCounted<cc::DisplayItemList>(); v1->SetX(8); - root_view->Paint(ui::PaintContext( - ui::PaintContext(list.get(), device_scale_factor, pixel_rect), + root_view->PaintFromPaintRoot(ui::PaintContext( + ui::PaintContext(list.get(), device_scale_factor, pixel_rect, false), ui::PaintContext::CLONE_WITHOUT_INVALIDATION)); EXPECT_TRUE(v1->did_paint_); v1->Reset(); @@ -708,7 +714,8 @@ TEST_F(ViewTest, PaintWithUnknownInvalidation) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), 1.f, first_paint, false)); v1->Reset(); v2->Reset(); @@ -719,13 +726,14 @@ TEST_F(ViewTest, PaintWithUnknownInvalidation) { // With a known invalidation, v1 and v2 are not painted. EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->PaintFromPaintRoot( + ui::PaintContext(list.get(), 1.f, paint_area, false)); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); // With unknown invalidation, v1 and v2 are painted. - root_view->Paint( - ui::PaintContext(ui::PaintContext(list.get(), 1.f, paint_area), + root_view->PaintFromPaintRoot( + ui::PaintContext(ui::PaintContext(list.get(), 1.f, paint_area, false), ui::PaintContext::CLONE_WITHOUT_INVALIDATION)); EXPECT_TRUE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); @@ -747,7 +755,9 @@ TEST_F(ViewTest, PaintContainsChildren) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -757,7 +767,8 @@ TEST_F(ViewTest, PaintContainsChildren) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); } @@ -790,7 +801,9 @@ TEST_F(ViewTest, PaintContainsChildrenInRTL) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -800,7 +813,8 @@ TEST_F(ViewTest, PaintContainsChildrenInRTL) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); } @@ -821,7 +835,9 @@ TEST_F(ViewTest, PaintIntersectsChildren) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -831,7 +847,8 @@ TEST_F(ViewTest, PaintIntersectsChildren) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); } @@ -864,7 +881,9 @@ TEST_F(ViewTest, PaintIntersectsChildrenInRTL) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -874,7 +893,8 @@ TEST_F(ViewTest, PaintIntersectsChildrenInRTL) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); } @@ -895,7 +915,9 @@ TEST_F(ViewTest, PaintIntersectsChildButNotGrandChild) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -905,7 +927,8 @@ TEST_F(ViewTest, PaintIntersectsChildButNotGrandChild) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); } @@ -938,7 +961,9 @@ TEST_F(ViewTest, PaintIntersectsChildButNotGrandChildInRTL) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -948,7 +973,8 @@ TEST_F(ViewTest, PaintIntersectsChildButNotGrandChildInRTL) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); } @@ -969,7 +995,9 @@ TEST_F(ViewTest, PaintIntersectsNoChildren) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -979,7 +1007,8 @@ TEST_F(ViewTest, PaintIntersectsNoChildren) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); } @@ -1012,7 +1041,9 @@ TEST_F(ViewTest, PaintIntersectsNoChildrenInRTL) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -1022,7 +1053,8 @@ TEST_F(ViewTest, PaintIntersectsNoChildrenInRTL) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); } @@ -1043,7 +1075,9 @@ TEST_F(ViewTest, PaintIntersectsOneChild) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -1054,7 +1088,8 @@ TEST_F(ViewTest, PaintIntersectsOneChild) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_FALSE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); @@ -1065,7 +1100,8 @@ TEST_F(ViewTest, PaintIntersectsOneChild) { v2->Reset(); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); } @@ -1098,7 +1134,9 @@ TEST_F(ViewTest, PaintIntersectsOneChildInRTL) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - root_view->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), + root_view->size())); v1->Reset(); v2->Reset(); @@ -1109,7 +1147,8 @@ TEST_F(ViewTest, PaintIntersectsOneChildInRTL) { EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); @@ -1120,7 +1159,8 @@ TEST_F(ViewTest, PaintIntersectsOneChildInRTL) { v2->Reset(); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), root_view->size())); EXPECT_FALSE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); } @@ -1142,7 +1182,8 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { // invalidation. gfx::Rect first_paint(1, 1); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - v1->Paint(ui::PaintContext(list.get(), 1.f, first_paint)); + v1->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, first_paint, false), v1->size())); v1->Reset(); v2->Reset(); @@ -1152,7 +1193,9 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { auto list = base::MakeRefCounted<cc::DisplayItemList>(); // The promoted views are not painted as they are separate paint roots. - root_view->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + root_view->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), + root_view->size())); EXPECT_FALSE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); } @@ -1164,7 +1207,8 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { // The |v1| view is painted. If it used its offset incorrect, it would think // its at (10,11) instead of at (0,0) since it is the paint root. - v1->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + v1->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), v1->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_FALSE(v2->did_paint_); } @@ -1178,7 +1222,8 @@ TEST_F(ViewTest, PaintInPromotedToLayer) { // The |v2| view is painted also. If it used its offset incorrect, it would // think its at (13,15) instead of at (3,4) since |v1| is the paint root. - v1->Paint(ui::PaintContext(list.get(), 1.f, paint_area)); + v1->Paint(PaintInfo::CreateRootPaintInfo( + ui::PaintContext(list.get(), 1.f, paint_area, false), v1->size())); EXPECT_TRUE(v1->did_paint_); EXPECT_TRUE(v2->did_paint_); } @@ -1221,9 +1266,9 @@ TEST_F(ViewTest, PaintLocalBounds) { EXPECT_EQ(gfx::Rect(0, 1000, 100, 100), v1->GetVisibleBounds()); auto list = base::MakeRefCounted<cc::DisplayItemList>(); - ui::PaintContext context(list.get(), 1.f, gfx::Rect()); + ui::PaintContext context(list.get(), 1.f, gfx::Rect(), false); - v1->Paint(context); + v1->Paint(PaintInfo::CreateRootPaintInfo(context, gfx::Size())); EXPECT_TRUE(v1->did_paint_); // Check that the canvas produced by |v1| for paint contains all of |v1|'s @@ -2353,16 +2398,9 @@ class ToplevelWidgetObserverView : public View { DISALLOW_COPY_AND_ASSIGN(ToplevelWidgetObserverView); }; -// No ReparentNativeView on Mac. See http://crbug.com/514920. -#if defined(OS_MACOSX) && !defined(USE_AURA) -#define MAYBE_NativeViewHierarchyChanged DISABLED_NativeViewHierarchyChanged -#else -#define MAYBE_NativeViewHierarchyChanged NativeViewHierarchyChanged -#endif - // Test that a view can track the current top level widget by overriding // View::ViewHierarchyChanged() and View::NativeViewHierarchyChanged(). -TEST_F(ViewTest, MAYBE_NativeViewHierarchyChanged) { +TEST_F(ViewTest, NativeViewHierarchyChanged) { std::unique_ptr<Widget> toplevel1(new Widget); Widget::InitParams toplevel1_params = CreateParams(Widget::InitParams::TYPE_POPUP); @@ -4440,6 +4478,67 @@ TEST_F(ViewLayerTest, SnapLayerToPixel) { EXPECT_EQ("0.00 0.00", ToString(v11->layer()->subpixel_position_offset())); } +class ViewLayerPixelCanvasTest : public ViewLayerTest { + public: + ViewLayerPixelCanvasTest() {} + + ~ViewLayerPixelCanvasTest() override {} + + void SetUp() override { + // Enable pixel canvas + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + cmd_line->AppendSwitch(switches::kEnablePixelCanvasRecording); + + ViewLayerTest::SetUp(); + } +}; + +TEST_F(ViewLayerPixelCanvasTest, SnapLayerToPixel) { + View* v1 = new View; + View* v2 = new View; + View* v3 = new View; + v1->AddChildView(v2); + v2->AddChildView(v3); + + widget()->SetContentsView(v1); + + const gfx::Size& size = GetRootLayer()->GetCompositor()->size(); + GetRootLayer()->GetCompositor()->SetScaleAndSize(1.6f, size); + + v3->SetBoundsRect(gfx::Rect(4, 4, 20, 20)); + v2->SetBoundsRect(gfx::Rect(7, 7, 50, 50)); + v1->SetBoundsRect(gfx::Rect(9, 9, 100, 100)); + v3->SetPaintToLayer(); + + EXPECT_EQ("-0.63 -0.63", ToString(v3->layer()->subpixel_position_offset())); + + // Creating a layer in parent should update the child view's layer offset. + v1->SetPaintToLayer(); + EXPECT_EQ("-0.25 -0.25", ToString(v1->layer()->subpixel_position_offset())); + EXPECT_EQ("-0.37 -0.37", ToString(v3->layer()->subpixel_position_offset())); + + // DSF change should get propagated and update offsets. + GetRootLayer()->GetCompositor()->SetScaleAndSize(1.5f, size); + EXPECT_EQ("0.33 0.33", ToString(v1->layer()->subpixel_position_offset())); + EXPECT_EQ("0.33 0.33", ToString(v3->layer()->subpixel_position_offset())); + + GetRootLayer()->GetCompositor()->SetScaleAndSize(1.33f, size); + EXPECT_EQ("0.02 0.02", ToString(v1->layer()->subpixel_position_offset())); + EXPECT_EQ("-0.47 -0.47", ToString(v3->layer()->subpixel_position_offset())); + + // Deleting parent's layer should update the child view's layer's offset. + v1->DestroyLayer(); + EXPECT_EQ("-0.45 -0.45", ToString(v3->layer()->subpixel_position_offset())); + + // Setting parent view should update the child view's layer's offset. + v1->SetBoundsRect(gfx::Rect(3, 3, 10, 10)); + EXPECT_EQ("-0.47 -0.47", ToString(v3->layer()->subpixel_position_offset())); + + // Setting integral DSF should reset the offset. + GetRootLayer()->GetCompositor()->SetScaleAndSize(2.0f, size); + EXPECT_EQ("0.00 0.00", ToString(v3->layer()->subpixel_position_offset())); +} + TEST_F(ViewTest, FocusableAssertions) { // View subclasses may change insets based on whether they are focusable, // which effects the preferred size. To avoid preferred size changing around diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc index 862d3c3eb1c..cd3ffb17d51 100644 --- a/chromium/ui/views/views_delegate.cc +++ b/chromium/ui/views/views_delegate.cc @@ -136,8 +136,4 @@ int ViewsDelegate::GetAppbarAutohideEdges(HMONITOR monitor, } #endif -scoped_refptr<base::TaskRunner> ViewsDelegate::GetBlockingPoolTaskRunner() { - return nullptr; -} - } // namespace views diff --git a/chromium/ui/views/views_delegate.h b/chromium/ui/views/views_delegate.h index b869e7b3fb3..ec2f61cb056 100644 --- a/chromium/ui/views/views_delegate.h +++ b/chromium/ui/views/views_delegate.h @@ -24,7 +24,6 @@ #include "ui/views/widget/widget.h" namespace base { -class TaskRunner; class TimeDelta; } @@ -213,9 +212,6 @@ class VIEWS_EXPORT ViewsDelegate { const base::Closure& callback); #endif - // Returns a blocking pool task runner given a TaskRunnerType. - virtual scoped_refptr<base::TaskRunner> GetBlockingPoolTaskRunner(); - protected: ViewsDelegate(); 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 d53d9c72cc6..5df66d73019 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 @@ -836,6 +836,16 @@ bool DesktopDragDropClientAuraX11::IsDragDropInProgress() { return !!g_current_drag_drop_client; } +void DesktopDragDropClientAuraX11::AddObserver( + aura::client::DragDropClientObserver* observer) { + NOTIMPLEMENTED(); +} + +void DesktopDragDropClientAuraX11::RemoveObserver( + aura::client::DragDropClientObserver* observer) { + NOTIMPLEMENTED(); +} + void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { DCHECK_EQ(target_window_, window); target_window_ = NULL; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h index b8792c2d855..53a8f237e0f 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h @@ -26,6 +26,7 @@ namespace aura { namespace client { +class DragDropClientObserver; class DragDropDelegate; } } @@ -89,6 +90,8 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 ui::DragDropTypes::DragEventSource source) override; void DragCancel() override; bool IsDragDropInProgress() override; + void AddObserver(aura::client::DragDropClientObserver* observer) override; + void RemoveObserver(aura::client::DragDropClientObserver* observer) override; // Overridden from aura::WindowObserver: void OnWindowDestroyed(aura::Window* window) override; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc index 67fd2faa13f..1267b568a30 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc @@ -84,6 +84,16 @@ bool DesktopDragDropClientWin::IsDragDropInProgress() { return drag_drop_in_progress_; } +void DesktopDragDropClientWin::AddObserver( + aura::client::DragDropClientObserver* observer) { + NOTIMPLEMENTED(); +} + +void DesktopDragDropClientWin::RemoveObserver( + aura::client::DragDropClientObserver* observer) { + NOTIMPLEMENTED(); +} + void DesktopDragDropClientWin::OnNativeWidgetDestroying(HWND window) { if (drop_target_.get()) { RevokeDragDrop(window); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h index 44c28416eea..25845e08f22 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h @@ -14,6 +14,12 @@ #include "ui/aura/client/drag_drop_client.h" #include "ui/views/views_export.h" +namespace aura { +namespace client { +class DragDropClientObserver; +} +} // namespace aura + namespace ui { class DragSourceWin; } @@ -36,6 +42,8 @@ class VIEWS_EXPORT DesktopDragDropClientWin ui::DragDropTypes::DragEventSource source) override; void DragCancel() override; bool IsDragDropInProgress() override; + void AddObserver(aura::client::DragDropClientObserver* observer) override; + void RemoveObserver(aura::client::DragDropClientObserver* observer) override; void OnNativeWidgetDestroying(HWND window); 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 a9fdec6c525..aed089087f9 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 @@ -710,7 +710,8 @@ void DesktopNativeWidgetAura::StackAtTop() { desktop_window_tree_host_->StackAtTop(); } -void DesktopNativeWidgetAura::SetShape(std::unique_ptr<SkRegion> shape) { +void DesktopNativeWidgetAura::SetShape( + std::unique_ptr<Widget::ShapeRects> shape) { if (content_window_) desktop_window_tree_host_->SetShape(std::move(shape)); } @@ -1150,12 +1151,11 @@ int DesktopNativeWidgetAura::OnPerformDrop(const ui::DropTargetEvent& event) { //////////////////////////////////////////////////////////////////////////////// // DesktopNativeWidgetAura, aura::WindowTreeHostObserver implementation: -void DesktopNativeWidgetAura::OnHostCloseRequested( - const aura::WindowTreeHost* host) { +void DesktopNativeWidgetAura::OnHostCloseRequested(aura::WindowTreeHost* host) { GetWidget()->Close(); } -void DesktopNativeWidgetAura::OnHostResized(const aura::WindowTreeHost* host) { +void DesktopNativeWidgetAura::OnHostResized(aura::WindowTreeHost* host) { // Don't update the bounds of the child layers when animating closed. If we // did it would force a paint, which we don't want. We don't want the paint // as we can't assume any of the children are valid. @@ -1168,12 +1168,12 @@ void DesktopNativeWidgetAura::OnHostResized(const aura::WindowTreeHost* host) { } void DesktopNativeWidgetAura::OnHostWorkspaceChanged( - const aura::WindowTreeHost* host) { + aura::WindowTreeHost* host) { native_widget_delegate_->OnNativeWidgetWorkspaceChanged(); } void DesktopNativeWidgetAura::OnHostMovedInPixels( - const aura::WindowTreeHost* host, + aura::WindowTreeHost* host, const gfx::Point& new_origin_in_pixels) { TRACE_EVENT1("views", "DesktopNativeWidgetAura::OnHostMovedInPixels", "new_origin_in_pixels", new_origin_in_pixels.ToString()); 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 01faa59fc6b..cdb96b1b2d3 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 @@ -138,7 +138,7 @@ class VIEWS_EXPORT DesktopNativeWidgetAura void SetSize(const gfx::Size& size) override; void StackAbove(gfx::NativeView native_view) override; void StackAtTop() override; - void SetShape(std::unique_ptr<SkRegion> shape) override; + void SetShape(std::unique_ptr<Widget::ShapeRects> shape) override; void Close() override; void CloseNow() override; void Show() override; @@ -231,10 +231,10 @@ class VIEWS_EXPORT DesktopNativeWidgetAura int OnPerformDrop(const ui::DropTargetEvent& event) override; // Overridden from aura::WindowTreeHostObserver: - void OnHostCloseRequested(const aura::WindowTreeHost* host) override; - void OnHostResized(const aura::WindowTreeHost* host) override; - void OnHostWorkspaceChanged(const aura::WindowTreeHost* host) override; - void OnHostMovedInPixels(const aura::WindowTreeHost* host, + void OnHostCloseRequested(aura::WindowTreeHost* host) override; + void OnHostResized(aura::WindowTreeHost* host) override; + void OnHostWorkspaceChanged(aura::WindowTreeHost* host) override; + void OnHostMovedInPixels(aura::WindowTreeHost* host, const gfx::Point& new_origin_in_pixels) override; private: 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 c31728ca1b4..df8884e7de4 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -377,8 +377,9 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { // TODO(ccameron): Populate this based on this specific display. // http://crbug.com/735613 if (!display::Display::HasForceColorProfile()) { - display.set_color_space( - gfx::ICCProfile::FromBestMonitor().GetColorSpace()); + gfx::ICCProfile icc_profile = gfx::ICCProfile::FromBestMonitor(); + icc_profile.HistogramDisplay(display.id()); + display.set_color_space(icc_profile.GetColorSpace()); } displays.push_back(display); 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 c8633b68fad..a1cf57dc46e 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 @@ -94,9 +94,9 @@ class VIEWS_EXPORT DesktopWindowTreeHost { virtual gfx::Rect GetWorkAreaBoundsInScreen() const = 0; - // Sets the shape of the root window. If |native_region| is NULL then the + // Sets the shape of the root window. If |native_shape| is nullptr then the // window reverts to rectangular. - virtual void SetShape(std::unique_ptr<SkRegion> native_region) = 0; + virtual void SetShape(std::unique_ptr<Widget::ShapeRects> native_shape) = 0; virtual void Activate() = 0; virtual void Deactivate() = 0; 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 4d0dfbe80e5..e5b007cd0a4 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 @@ -15,7 +15,6 @@ #include "ui/base/cursor/cursor_loader_win.h" #include "ui/base/ime/input_method.h" #include "ui/base/win/shell.h" -#include "ui/compositor/compositor_constants.h" #include "ui/compositor/paint_context.h" #include "ui/display/win/dpi.h" #include "ui/display/win/screen_win.h" @@ -133,12 +132,7 @@ void DesktopWindowTreeHostWin::Init(aura::Window* content_window, gfx::Rect pixel_bounds = display::win::ScreenWin::DIPToScreenRect(nullptr, params.bounds); message_handler_->Init(parent_hwnd, pixel_bounds); - if (params.force_software_compositing) { - ::SetProp(GetAcceleratedWidget(), - kForceSoftwareCompositor, - reinterpret_cast<HANDLE>(true)); - } - CreateCompositor(); + CreateCompositor(viz::FrameSinkId(), params.force_software_compositing); OnAcceleratedWidgetAvailable(); InitHost(); window()->Show(); @@ -298,34 +292,34 @@ gfx::Rect DesktopWindowTreeHostWin::GetWorkAreaBoundsInScreen() const { } void DesktopWindowTreeHostWin::SetShape( - std::unique_ptr<SkRegion> native_region) { - if (!native_region) { + std::unique_ptr<Widget::ShapeRects> native_shape) { + if (!native_shape || native_shape->empty()) { message_handler_->SetRegion(nullptr); return; } // TODO(wez): This would be a lot simpler if we were passed an SkPath. // See crbug.com/410593. - SkRegion* shape = native_region.get(); - SkRegion device_region; + SkRegion shape; const float scale = display::win::ScreenWin::GetScaleFactorForHWND(GetHWND()); if (scale > 1.0) { - shape = &device_region; - std::vector<SkIRect> rects; - for (SkRegion::Iterator it(*native_region); !it.done(); it.next()) { - const SkIRect& rect = it.rect(); + std::vector<SkIRect> sk_rects; + for (const gfx::Rect& rect : *native_shape) { + const SkIRect sk_rect = gfx::RectToSkIRect(rect); SkRect scaled_rect = - SkRect::MakeLTRB(rect.left() * scale, rect.top() * scale, - rect.right() * scale, rect.bottom() * scale); + SkRect::MakeLTRB(sk_rect.left() * scale, sk_rect.top() * scale, + sk_rect.right() * scale, sk_rect.bottom() * scale); SkIRect rounded_scaled_rect; scaled_rect.roundOut(&rounded_scaled_rect); - rects.push_back(rounded_scaled_rect); + sk_rects.push_back(rounded_scaled_rect); } - if (!rects.empty()) - device_region.setRects(&rects[0], rects.size()); + shape.setRects(&sk_rects[0], sk_rects.size()); + } else { + for (const gfx::Rect& rect : *native_shape) + shape.op(gfx::RectToSkIRect(rect), SkRegion::kUnion_Op); } - message_handler_->SetRegion(gfx::CreateHRGNFromSkRegion(*shape)); + message_handler_->SetRegion(gfx::CreateHRGNFromSkRegion(shape)); } void DesktopWindowTreeHostWin::Activate() { @@ -802,11 +796,13 @@ void DesktopWindowTreeHostWin::HandleEndWMSizeMove() { } void DesktopWindowTreeHostWin::HandleMove() { + CheckForMonitorChange(); native_widget_delegate_->OnNativeWidgetMove(); OnHostMovedInPixels(GetBoundsInPixels().origin()); } void DesktopWindowTreeHostWin::HandleWorkAreaChanged() { + CheckForMonitorChange(); GetWidget()->widget_delegate()->OnWorkAreaChanged(); } @@ -820,11 +816,13 @@ void DesktopWindowTreeHostWin::HandleVisibilityChanged(bool visible) { void DesktopWindowTreeHostWin::HandleClientSizeChanged( const gfx::Size& new_size) { + CheckForMonitorChange(); if (dispatcher()) OnHostResizedInPixels(new_size); } void DesktopWindowTreeHostWin::HandleFrameChanged() { + CheckForMonitorChange(); SetWindowTransparency(); // Replace the frame and layout the contents. GetWidget()->non_client_view()->UpdateFrame(); @@ -843,6 +841,11 @@ bool DesktopWindowTreeHostWin::HandleMouseEvent(const ui::MouseEvent& event) { return event.handled(); } +bool DesktopWindowTreeHostWin::HandlePointerEvent(ui::PointerEvent* event) { + SendEventToSink(event); + return event->handled(); +} + void DesktopWindowTreeHostWin::HandleKeyEvent(ui::KeyEvent* event) { SendEventToSink(event); } @@ -997,6 +1000,15 @@ bool DesktopWindowTreeHostWin::IsModalWindowActive() const { return false; } +void DesktopWindowTreeHostWin::CheckForMonitorChange() { + HMONITOR monitor_from_window = + ::MonitorFromWindow(GetHWND(), MONITOR_DEFAULTTOPRIMARY); + if (monitor_from_window == last_monitor_from_window_) + return; + last_monitor_from_window_ = monitor_from_window; + OnHostDisplayChanged(); +} + //////////////////////////////////////////////////////////////////////////////// // 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 3f1c7bffb61..eb1205ee6a5 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 @@ -77,7 +77,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin gfx::Rect GetRestoredBounds() const override; std::string GetWorkspace() const override; gfx::Rect GetWorkAreaBoundsInScreen() const override; - void SetShape(std::unique_ptr<SkRegion> native_region) override; + void SetShape(std::unique_ptr<Widget::ShapeRects> native_shape) override; void Activate() override; void Deactivate() override; bool IsActive() const override; @@ -188,6 +188,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin void HandleNativeFocus(HWND last_focused_window) override; void HandleNativeBlur(HWND focused_window) override; bool HandleMouseEvent(const ui::MouseEvent& event) override; + bool HandlePointerEvent(ui::PointerEvent* event) override; void HandleKeyEvent(ui::KeyEvent* event) override; void HandleTouchEvent(const ui::TouchEvent& event) override; bool HandleIMEMessage(UINT message, @@ -221,6 +222,12 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin // Returns true if a modal window is active in the current root window chain. bool IsModalWindowActive() const; + // Called whenever the HWND resizes or moves, to see if the nearest HMONITOR + // has changed, and, if so, inform the aura::WindowTreeHost. + void CheckForMonitorChange(); + + HMONITOR last_monitor_from_window_ = nullptr; + std::unique_ptr<HWNDMessageHandler> message_handler_; std::unique_ptr<aura::client::FocusClient> focus_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 e8a7a79c534..316cf855f7c 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 @@ -166,8 +166,7 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( has_pointer_focus_(false), modal_dialog_counter_(0), close_widget_factory_(this), - weak_factory_(this) { -} + weak_factory_(this) {} DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() { window()->ClearProperty(kHostForRootWindow); @@ -701,15 +700,18 @@ gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const { } void DesktopWindowTreeHostX11::SetShape( - std::unique_ptr<SkRegion> native_region) { + std::unique_ptr<Widget::ShapeRects> native_shape) { custom_window_shape_ = false; window_shape_.reset(); - if (native_region) { + if (native_shape) { + SkRegion native_region; + for (const gfx::Rect& rect : *native_shape) + native_region.op(gfx::RectToSkIRect(rect), SkRegion::kUnion_Op); gfx::Transform transform = GetRootTransform(); - if (!transform.IsIdentity() && !native_region->isEmpty()) { + if (!transform.IsIdentity() && !native_region.isEmpty()) { SkPath path_in_dip; - if (native_region->getBoundaryPath(&path_in_dip)) { + if (native_region.getBoundaryPath(&path_in_dip)) { SkPath path_in_pixels; path_in_dip.transform(transform.matrix(), &path_in_pixels); window_shape_.reset(gfx::CreateRegionFromSkPath(path_in_pixels)); @@ -717,7 +719,7 @@ void DesktopWindowTreeHostX11::SetShape( window_shape_.reset(XCreateRegion()); } } else { - window_shape_.reset(gfx::CreateRegionFromSkRegion(*native_region)); + window_shape_.reset(gfx::CreateRegionFromSkRegion(native_region)); } custom_window_shape_ = true; @@ -1321,6 +1323,8 @@ void DesktopWindowTreeHostX11::OnDisplayMetricsChanged( } } +void DesktopWindowTreeHostX11::OnMaximizedStateChanged() {} + //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostX11, private: @@ -1530,7 +1534,10 @@ void DesktopWindowTreeHostX11::InitX11Window( if (window_icon) { SetWindowIcons(gfx::ImageSkia(), *window_icon); } - CreateCompositor(); + // Disable compositing on tooltips as a workaround for + // https://crbug.com/442111. + CreateCompositor(viz::FrameSinkId(), + params.type == Widget::InitParams::TYPE_TOOLTIP); OnAcceleratedWidgetAvailable(); } @@ -1560,11 +1567,15 @@ void DesktopWindowTreeHostX11::OnWMStateUpdated() { ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list); bool was_minimized = IsMinimized(); + bool was_maximized = IsMaximized(); window_properties_.clear(); std::copy(atom_list.begin(), atom_list.end(), inserter(window_properties_, window_properties_.begin())); + bool is_minimized = IsMinimized(); + bool is_maximized = IsMaximized(); + // Propagate the window minimization information to the content window, so // the render side can update its visibility properly. OnWMStateUpdated() is // called by PropertyNofify event from DispatchEvent() when the browser is @@ -1577,7 +1588,6 @@ void DesktopWindowTreeHostX11::OnWMStateUpdated() { // don't draw any 'blank' frames that could be noticed in applications such as // window manager previews, which show content even when a window is // minimized. - bool is_minimized = IsMinimized(); if (is_minimized != was_minimized) { if (is_minimized) { compositor()->SetVisible(false); @@ -1611,6 +1621,9 @@ void DesktopWindowTreeHostX11::OnWMStateUpdated() { is_always_on_top_ = HasWMSpecProperty("_NET_WM_STATE_ABOVE"); + if (was_maximized != is_maximized) + OnMaximizedStateChanged(); + // Now that we have different window properties, we may need to relayout the // window. (The windows code doesn't need this because their window change is // synchronous.) 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 cb189675734..b72ec26d3ed 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 @@ -113,7 +113,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 gfx::Rect GetRestoredBounds() const override; std::string GetWorkspace() const override; gfx::Rect GetWorkAreaBoundsInScreen() const override; - void SetShape(std::unique_ptr<SkRegion> native_region) override; + void SetShape(std::unique_ptr<Widget::ShapeRects> native_shape) override; void Activate() override; void Deactivate() override; bool IsActive() const override; @@ -173,6 +173,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 void OnDisplayMetricsChanged(const display::Display& display, uint32_t changed_metrics) override; + // Called after the window is maximized or restored. + virtual void OnMaximizedStateChanged(); + private: friend class DesktopWindowTreeHostX11HighDPITest; // Initializes our X11 surface to draw on. This method performs all 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 612b41f5939..32bcd039e1c 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 @@ -267,17 +267,10 @@ TEST_F(DesktopWindowTreeHostX11Test, Shape) { } // 2) Test setting the window shape via Widget::SetShape(). - gfx::Path shape2; - shape2.moveTo(10, 0); - shape2.lineTo(10, 10); - shape2.lineTo(0, 10); - shape2.lineTo(0, 100); - shape2.lineTo(100, 100); - shape2.lineTo(100, 0); - shape2.close(); - - auto shape_region = base::MakeUnique<SkRegion>(); - shape_region->setPath(shape2, SkRegion(shape2.getBounds().round())); + auto shape_region = base::MakeUnique<Widget::ShapeRects>(); + shape_region->emplace_back(10, 0, 90, 10); + shape_region->emplace_back(0, 10, 10, 90); + shape_region->emplace_back(10, 10, 90, 90); std::unique_ptr<Widget> widget2(CreateWidget(nullptr)); widget2->Show(); 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 d94a8789cbf..18fac097875 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 @@ -20,7 +20,6 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "third_party/skia/include/core/SkRect.h" -#include "third_party/skia/include/core/SkRegion.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/events/platform/x11/x11_event_source.h" @@ -302,10 +301,10 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangular) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - auto skregion1 = base::MakeUnique<SkRegion>(); - skregion1->op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op); - skregion1->op(SkIRect::MakeXYWH(10, 0, 90, 100), SkRegion::kUnion_Op); - widget1->SetShape(std::move(skregion1)); + auto shape1 = base::MakeUnique<Widget::ShapeRects>(); + shape1->emplace_back(0, 10, 10, 90); + shape1->emplace_back(10, 0, 90, 100); + widget1->SetShape(std::move(shape1)); SkRegion skregion2; skregion2.op(SkIRect::MakeXYWH(0, 10, 10, 90), SkRegion::kUnion_Op); @@ -341,10 +340,10 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularEmptyShape) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - auto skregion1 = base::MakeUnique<SkRegion>(); - skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op); - // Widget takes ownership of |skregion1|. - widget1->SetShape(std::move(skregion1)); + auto shape1 = base::MakeUnique<Widget::ShapeRects>(); + shape1->emplace_back(); + // Widget takes ownership of |shape1|. + widget1->SetShape(std::move(shape1)); XID xids[] = { xid1 }; StackingClientListWaiter stack_waiter(xids, arraysize(xids)); @@ -362,9 +361,9 @@ TEST_F(X11TopmostWindowFinderTest, NonRectangularNullShape) { std::unique_ptr<Widget> widget1( CreateAndShowWidget(gfx::Rect(100, 100, 100, 100))); XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget(); - auto skregion1 = base::MakeUnique<SkRegion>(); - skregion1->op(SkIRect::MakeXYWH(0, 0, 0, 0), SkRegion::kUnion_Op); - widget1->SetShape(std::move(skregion1)); + auto shape1 = base::MakeUnique<Widget::ShapeRects>(); + shape1->emplace_back(); + widget1->SetShape(std::move(shape1)); // Remove the shape - this is now just a normal window. widget1->SetShape(nullptr); diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index 90cd8ab604d..af6022f14ec 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -13,7 +13,7 @@ #include "build/build_config.h" #include "services/ui/public/interfaces/window_manager.mojom.h" #include "services/ui/public/interfaces/window_manager_constants.mojom.h" -#include "third_party/skia/include/core/SkRegion.h" +#include "services/ui/public/interfaces/window_tree_constants.mojom.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/capture_client.h" #include "ui/aura/client/cursor_client.h" @@ -62,7 +62,7 @@ #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" #endif -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) #include "ui/views/linux_ui/linux_ui.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" #endif @@ -226,7 +226,10 @@ void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) { SetRestoreBounds(window_, window_bounds); else SetBounds(window_bounds); - window_->set_ignore_events(!params.accept_events); + window_->SetEventTargetingPolicy( + params.accept_events + ? ui::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS + : ui::mojom::EventTargetingPolicy::NONE); DCHECK(GetWidget()->GetRootView()); if (params.type != Widget::InitParams::TYPE_TOOLTIP) tooltip_manager_.reset(new views::TooltipManagerAura(GetWidget())); @@ -490,9 +493,9 @@ void NativeWidgetAura::StackAtTop() { window_->parent()->StackChildAtTop(window_); } -void NativeWidgetAura::SetShape(std::unique_ptr<SkRegion> region) { +void NativeWidgetAura::SetShape(std::unique_ptr<Widget::ShapeRects> shape) { if (window_) - window_->layer()->SetAlphaShape(std::move(region)); + window_->layer()->SetAlphaShape(std::move(shape)); } void NativeWidgetAura::Close() { @@ -1000,7 +1003,7 @@ void NativeWidgetAura::SetInitialFocus(ui::WindowShowState show_state) { // Widget, public: namespace { -#if defined(OS_WIN) || (defined(USE_X11) && !defined(OS_CHROMEOS)) +#if defined(OS_WIN) || defined(USE_X11) void CloseWindow(aura::Window* window) { if (window) { Widget* widget = Widget::GetWidgetForNativeView(window); @@ -1030,7 +1033,7 @@ void Widget::CloseAllSecondaryWidgets() { EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0); #endif -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) DesktopWindowTreeHostX11::CleanUpWindowList(CloseWindow); #endif } @@ -1042,7 +1045,7 @@ bool Widget::ConvertRect(const Widget* source, } const ui::NativeTheme* Widget::GetNativeTheme() const { -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) const LinuxUI* linux_ui = LinuxUI::instance(); if (linux_ui) { ui::NativeTheme* native_theme = diff --git a/chromium/ui/views/widget/native_widget_aura.h b/chromium/ui/views/widget/native_widget_aura.h index f556bcc14d1..87fc9372272 100644 --- a/chromium/ui/views/widget/native_widget_aura.h +++ b/chromium/ui/views/widget/native_widget_aura.h @@ -99,7 +99,7 @@ class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate, void SetSize(const gfx::Size& size) override; void StackAbove(gfx::NativeView native_view) override; void StackAtTop() override; - void SetShape(std::unique_ptr<SkRegion> shape) override; + void SetShape(std::unique_ptr<Widget::ShapeRects> shape) override; void Close() override; void CloseNow() override; void Show() override; diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h index 25e52d78ab3..74e3f333e57 100644 --- a/chromium/ui/views/widget/native_widget_mac.h +++ b/chromium/ui/views/widget/native_widget_mac.h @@ -84,7 +84,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { void SetSize(const gfx::Size& size) override; void StackAbove(gfx::NativeView native_view) override; void StackAtTop() override; - void SetShape(std::unique_ptr<SkRegion> shape) override; + void SetShape(std::unique_ptr<Widget::ShapeRects> shape) override; void Close() override; void CloseNow() override; void Show() override; diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm index 398ea45f1f2..284c7fb7e1e 100644 --- a/chromium/ui/views/widget/native_widget_mac.mm +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -268,7 +268,7 @@ void NativeWidgetMac::GetWindowPlacement( bool NativeWidgetMac::SetWindowTitle(const base::string16& title) { NSWindow* window = GetNativeWindow(); NSString* current_title = [window title]; - NSString* new_title = SysUTF16ToNSString(title); + NSString* new_title = base::SysUTF16ToNSString(title); if ([current_title isEqualToString:new_title]) return false; @@ -333,7 +333,7 @@ void NativeWidgetMac::StackAtTop() { NOTIMPLEMENTED(); } -void NativeWidgetMac::SetShape(std::unique_ptr<SkRegion> shape) { +void NativeWidgetMac::SetShape(std::unique_ptr<Widget::ShapeRects> shape) { NOTIMPLEMENTED(); } @@ -742,14 +742,34 @@ void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, // static void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, gfx::NativeView new_parent) { + DCHECK_NE(native_view, new_parent); + if (!new_parent || [native_view superview] == new_parent) { + NOTREACHED(); + return; + } + BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow([native_view window]); - if (bridge && bridge->parent() && - bridge->parent()->GetNSWindow() == [new_parent window]) - return; // Nothing to do. + BridgedNativeWidget* parent_bridge = + NativeWidgetMac::GetBridgeForNativeWindow([new_parent window]); + DCHECK(bridge); + if (Widget::GetWidgetForNativeView(native_view)->is_top_level() && + bridge->parent() == parent_bridge) + return; + + Widget::Widgets widgets; + GetAllChildWidgets(native_view, &widgets); + + // First notify all the widgets that they are being disassociated + // from their previous parent. + for (auto* child : widgets) + child->NotifyNativeViewHierarchyWillChange(); + + bridge->ReparentNativeView(native_view, new_parent); - // Not supported. See http://crbug.com/514920. - NOTREACHED(); + // And now, notify them that they have a brand new parent. + for (auto* child : widgets) + child->NotifyNativeViewHierarchyChanged(); } // static diff --git a/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm b/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm index 357897b4fc7..a9debe71bcc 100644 --- a/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_accessibility_unittest.mm @@ -24,19 +24,6 @@ #include "ui/views/test/widget_test.h" #include "ui/views/widget/widget.h" -// Expose some methods from AXPlatformNodeCocoa for testing purposes only. -@interface AXPlatformNodeCocoa (Testing) -- (NSString*)AXRole; -- (id)AXValue; - -// Text attributes. -- (NSString*)AXSelectedText; -- (NSValue*)AXSelectedTextRange; -- (NSNumber*)AXNumberOfCharacters; -- (NSValue*)AXVisibleCharacterRange; -- (NSNumber*)AXInsertionPointLineNumber; -@end - namespace views { namespace { @@ -92,28 +79,44 @@ class TestLabelButton : public LabelButton { DISALLOW_COPY_AND_ASSIGN(TestLabelButton); }; +class TestWidgetDelegate : public test::TestDesktopWidgetDelegate { + public: + TestWidgetDelegate() = default; + + static constexpr char kAccessibleWindowTitle[] = "My Accessible Window"; + + // WidgetDelegate: + base::string16 GetAccessibleWindowTitle() const override { + return base::ASCIIToUTF16(kAccessibleWindowTitle); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); +}; + +constexpr char TestWidgetDelegate::kAccessibleWindowTitle[]; + class NativeWidgetMacAccessibilityTest : public test::WidgetTest { public: NativeWidgetMacAccessibilityTest() {} void SetUp() override { test::WidgetTest::SetUp(); - widget_ = CreateTopLevelPlatformWidget(); - widget_->SetBounds(gfx::Rect(50, 50, 100, 100)); + widget_delegate_.InitWidget(CreateParams(Widget::InitParams::TYPE_WINDOW)); widget()->Show(); } void TearDown() override { - widget_->CloseNow(); + widget()->CloseNow(); test::WidgetTest::TearDown(); } id A11yElementAtMidpoint() { // Accessibility hit tests come in Cocoa screen coordinates. NSPoint midpoint_in_screen_ = gfx::ScreenPointToNSPoint( - widget_->GetWindowBoundsInScreen().CenterPoint()); + widget()->GetWindowBoundsInScreen().CenterPoint()); return - [widget_->GetNativeWindow() accessibilityHitTest:midpoint_in_screen_]; + [widget()->GetNativeWindow() accessibilityHitTest:midpoint_in_screen_]; } id AttributeValueAtMidpoint(NSString* attribute) { @@ -129,11 +132,71 @@ class NativeWidgetMacAccessibilityTest : public test::WidgetTest { return textfield; } - Widget* widget() { return widget_; } - gfx::Rect GetWidgetBounds() { return widget_->GetClientAreaBoundsInScreen(); } + // Shorthand helpers to get a11y properties from A11yElementAtMidpoint(). + NSString* AXRole() { + return AttributeValueAtMidpoint(NSAccessibilityRoleAttribute); + } + id AXParent() { + return AttributeValueAtMidpoint(NSAccessibilityParentAttribute); + } + id AXValue() { + return AttributeValueAtMidpoint(NSAccessibilityValueAttribute); + } + NSString* AXTitle() { + return AttributeValueAtMidpoint(NSAccessibilityTitleAttribute); + } + NSString* AXDescription() { + return AttributeValueAtMidpoint(NSAccessibilityDescriptionAttribute); + } + NSString* AXSelectedText() { + return AttributeValueAtMidpoint(NSAccessibilitySelectedTextAttribute); + } + NSValue* AXSelectedTextRange() { + return AttributeValueAtMidpoint(NSAccessibilitySelectedTextRangeAttribute); + } + NSNumber* AXNumberOfCharacters() { + return AttributeValueAtMidpoint(NSAccessibilityNumberOfCharactersAttribute); + } + NSValue* AXVisibleCharacterRange() { + return AttributeValueAtMidpoint( + NSAccessibilityVisibleCharacterRangeAttribute); + } + NSNumber* AXInsertionPointLineNumber() { + return AttributeValueAtMidpoint( + NSAccessibilityInsertionPointLineNumberAttribute); + } + NSNumber* AXLineForIndex(id parameter) { + return [A11yElementAtMidpoint() + accessibilityAttributeValue: + NSAccessibilityLineForIndexParameterizedAttribute + forParameter:parameter]; + } + NSValue* AXRangeForLine(id parameter) { + return [A11yElementAtMidpoint() + accessibilityAttributeValue: + NSAccessibilityRangeForLineParameterizedAttribute + forParameter:parameter]; + } + NSString* AXStringForRange(id parameter) { + return [A11yElementAtMidpoint() + accessibilityAttributeValue: + NSAccessibilityStringForRangeParameterizedAttribute + forParameter:parameter]; + } + NSAttributedString* AXAttributedStringForRange(id parameter) { + return [A11yElementAtMidpoint() + accessibilityAttributeValue: + NSAccessibilityAttributedStringForRangeParameterizedAttribute + forParameter:parameter]; + } + + Widget* widget() { return widget_delegate_.GetWidget(); } + gfx::Rect GetWidgetBounds() { + return widget()->GetClientAreaBoundsInScreen(); + } private: - Widget* widget_ = nullptr; + TestWidgetDelegate widget_delegate_; DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacAccessibilityTest); }; @@ -229,8 +292,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, FocusableElementsAreLeafNodes) { TestLabelButton* button = new TestLabelButton(); button->SetSize(widget()->GetContentsView()->size()); widget()->GetContentsView()->AddChildView(button); - EXPECT_NSEQ(NSAccessibilityButtonRole, - AttributeValueAtMidpoint(NSAccessibilityRoleAttribute)); + EXPECT_NSEQ(NSAccessibilityButtonRole, AXRole()); EXPECT_EQ( 0u, [[button->GetNativeViewAccessible() @@ -290,7 +352,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, ParentAttribute) { // Views with Widget parents will have a NSWindow parent. EXPECT_NSEQ( NSAccessibilityWindowRole, - [AttributeValueAtMidpoint(NSAccessibilityParentAttribute) AXRole]); + [AXParent() accessibilityAttributeValue:NSAccessibilityRoleAttribute]); // Views with non-Widget parents will have the role of the parent view. widget()->GetContentsView()->RemoveChildView(child); @@ -299,13 +361,13 @@ TEST_F(NativeWidgetMacAccessibilityTest, ParentAttribute) { widget()->GetContentsView()->AddChildView(parent); EXPECT_NSEQ( NSAccessibilityGroupRole, - [AttributeValueAtMidpoint(NSAccessibilityParentAttribute) AXRole]); + [AXParent() accessibilityAttributeValue:NSAccessibilityRoleAttribute]); // Test an ignored role parent is skipped in favor of the grandparent. parent->set_role(ui::AX_ROLE_IGNORED); EXPECT_NSEQ( NSAccessibilityWindowRole, - [AttributeValueAtMidpoint(NSAccessibilityParentAttribute) AXRole]); + [AXParent() accessibilityAttributeValue:NSAccessibilityRoleAttribute]); } // Test for NSAccessibilityPositionAttribute, including on Widget movement @@ -337,20 +399,22 @@ TEST_F(NativeWidgetMacAccessibilityTest, HelpAttribute) { AttributeValueAtMidpoint(NSAccessibilityHelpAttribute)); } -// Test for NSAccessibilityWindowAttribute and -// NSAccessibilityTopLevelUIElementAttribute. -TEST_F(NativeWidgetMacAccessibilityTest, WindowAndTopLevelUIElementAttributes) { +// Test view properties that should report the native NSWindow, and test +// specific properties on that NSWindow. +TEST_F(NativeWidgetMacAccessibilityTest, NativeWindowProperties) { FlexibleRoleTestView* view = new FlexibleRoleTestView(ui::AX_ROLE_GROUP); view->SetSize(GetWidgetBounds().size()); widget()->GetContentsView()->AddChildView(view); // Make sure it's |view| in the hit test by checking its accessibility role. - EXPECT_EQ(NSAccessibilityGroupRole, - AttributeValueAtMidpoint(NSAccessibilityRoleAttribute)); - EXPECT_NSEQ(widget()->GetNativeWindow(), - AttributeValueAtMidpoint(NSAccessibilityWindowAttribute)); + EXPECT_EQ(NSAccessibilityGroupRole, AXRole()); + + NSWindow* window = widget()->GetNativeWindow(); + EXPECT_NSEQ(window, AttributeValueAtMidpoint(NSAccessibilityWindowAttribute)); + EXPECT_NSEQ(window, AttributeValueAtMidpoint( + NSAccessibilityTopLevelUIElementAttribute)); EXPECT_NSEQ( - widget()->GetNativeWindow(), - AttributeValueAtMidpoint(NSAccessibilityTopLevelUIElementAttribute)); + base::SysUTF8ToNSString(TestWidgetDelegate::kAccessibleWindowTitle), + [window accessibilityAttributeValue:NSAccessibilityTitleAttribute]); } // Tests for accessibility attributes on a views::Textfield. @@ -375,16 +439,9 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldGenericAttributes) { boolValue]); // NSAccessibilityTitleAttribute. - EXPECT_NSEQ(kTestTitle, - AttributeValueAtMidpoint(NSAccessibilityTitleAttribute)); - - // NSAccessibilityValueAttribute. - EXPECT_NSEQ(kTestStringValue, - AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); - - // NSAccessibilityRoleAttribute. - EXPECT_NSEQ(NSAccessibilityTextFieldRole, - AttributeValueAtMidpoint(NSAccessibilityRoleAttribute)); + EXPECT_NSEQ(NSAccessibilityTextFieldRole, AXRole()); + EXPECT_NSEQ(kTestTitle, AXTitle()); + EXPECT_NSEQ(kTestStringValue, AXValue()); // NSAccessibilitySubroleAttribute and // NSAccessibilityRoleDescriptionAttribute. @@ -431,10 +488,8 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldEditableAttributes) { NSAccessibilityInsertionPointLineNumberAttribute) intValue]); // NSAccessibilityNumberOfCharactersAttribute. - EXPECT_EQ( - kTestStringValue.length, - [AttributeValueAtMidpoint(NSAccessibilityNumberOfCharactersAttribute) - unsignedIntegerValue]); + EXPECT_EQ(kTestStringValue.length, + [AXNumberOfCharacters() unsignedIntegerValue]); // NSAccessibilityPlaceholderAttribute. EXPECT_NSEQ( @@ -443,54 +498,41 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldEditableAttributes) { // NSAccessibilitySelectedTextAttribute and // NSAccessibilitySelectedTextRangeAttribute. - EXPECT_NSEQ(@"", - AttributeValueAtMidpoint(NSAccessibilitySelectedTextAttribute)); + EXPECT_NSEQ(@"", AXSelectedText()); // The cursor will be at the end of the textfield, so the selection range will // span 0 characters and be located at the index after the last character. EXPECT_EQ(gfx::Range(kTestStringValue.length, kTestStringValue.length), - gfx::Range([AttributeValueAtMidpoint( - NSAccessibilitySelectedTextRangeAttribute) rangeValue])); + gfx::Range([AXSelectedTextRange() rangeValue])); // Select some text in the middle of the textfield. const gfx::Range forward_range(2, 6); const NSRange ns_range = forward_range.ToNSRange(); textfield->SelectRange(forward_range); - EXPECT_NSEQ([kTestStringValue substringWithRange:ns_range], - AttributeValueAtMidpoint(NSAccessibilitySelectedTextAttribute)); + EXPECT_NSEQ([kTestStringValue substringWithRange:ns_range], AXSelectedText()); EXPECT_EQ(textfield->GetSelectedText(), - base::SysNSStringToUTF16(AttributeValueAtMidpoint( - NSAccessibilitySelectedTextAttribute))); - EXPECT_EQ(forward_range, - gfx::Range([AttributeValueAtMidpoint( - NSAccessibilitySelectedTextRangeAttribute) rangeValue])); + base::SysNSStringToUTF16(AXSelectedText())); + EXPECT_EQ(forward_range, gfx::Range([AXSelectedTextRange() rangeValue])); const gfx::Range reversed_range(6, 2); textfield->SelectRange(reversed_range); // NSRange has no direction, so these are unchanged from the forward range. - EXPECT_NSEQ([kTestStringValue substringWithRange:ns_range], - AttributeValueAtMidpoint(NSAccessibilitySelectedTextAttribute)); + EXPECT_NSEQ([kTestStringValue substringWithRange:ns_range], AXSelectedText()); EXPECT_EQ(textfield->GetSelectedText(), - base::SysNSStringToUTF16(AttributeValueAtMidpoint( - NSAccessibilitySelectedTextAttribute))); - EXPECT_EQ(forward_range, - gfx::Range([AttributeValueAtMidpoint( - NSAccessibilitySelectedTextRangeAttribute) rangeValue])); + base::SysNSStringToUTF16(AXSelectedText())); + EXPECT_EQ(forward_range, gfx::Range([AXSelectedTextRange() rangeValue])); // NSAccessibilityVisibleCharacterRangeAttribute. EXPECT_EQ(gfx::Range(0, kTestStringValue.length), - gfx::Range([AttributeValueAtMidpoint( - NSAccessibilityVisibleCharacterRangeAttribute) rangeValue])); + gfx::Range([AXVisibleCharacterRange() rangeValue])); // Test an RTL string. textfield->SetText(base::SysNSStringToUTF16(kTestRTLStringValue)); textfield->SelectRange(forward_range); EXPECT_EQ(textfield->GetSelectedText(), - base::SysNSStringToUTF16(AttributeValueAtMidpoint( - NSAccessibilitySelectedTextAttribute))); + base::SysNSStringToUTF16(AXSelectedText())); textfield->SelectRange(reversed_range); EXPECT_EQ(textfield->GetSelectedText(), - base::SysNSStringToUTF16(AttributeValueAtMidpoint( - NSAccessibilitySelectedTextAttribute))); + base::SysNSStringToUTF16(AXSelectedText())); } // Test writing accessibility attributes via an accessibility client for normal @@ -503,8 +545,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, ViewWritableAttributes) { // Make sure the accessibility object tested is the correct one. id ax_node = A11yElementAtMidpoint(); EXPECT_TRUE(ax_node); - EXPECT_NSEQ(NSAccessibilityGroupRole, - AttributeValueAtMidpoint(NSAccessibilityRoleAttribute)); + EXPECT_NSEQ(NSAccessibilityGroupRole, AXRole()); // Make sure |view| is focusable, then focus/unfocus it. view->SetFocusBehavior(View::FocusBehavior::ALWAYS); @@ -530,17 +571,14 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) { EXPECT_TRUE(ax_node); // Make sure it's the correct accessibility object. - id value = - [ax_node accessibilityAttributeValue:NSAccessibilityValueAttribute]; - EXPECT_NSEQ(kTestStringValue, value); + EXPECT_NSEQ(kTestStringValue, AXValue()); // Write a new NSAccessibilityValueAttribute. EXPECT_TRUE( [ax_node accessibilityIsAttributeSettable:NSAccessibilityValueAttribute]); [ax_node accessibilitySetValue:kTestPlaceholderText forAttribute:NSAccessibilityValueAttribute]; - EXPECT_NSEQ(kTestPlaceholderText, - AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); + EXPECT_NSEQ(kTestPlaceholderText, AXValue()); EXPECT_EQ(base::SysNSStringToUTF16(kTestPlaceholderText), textfield->text()); // Test a read-only textfield. @@ -549,8 +587,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) { [ax_node accessibilityIsAttributeSettable:NSAccessibilityValueAttribute]); [ax_node accessibilitySetValue:kTestStringValue forAttribute:NSAccessibilityValueAttribute]; - EXPECT_NSEQ(kTestPlaceholderText, - AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); + EXPECT_NSEQ(kTestPlaceholderText, AXValue()); EXPECT_EQ(base::SysNSStringToUTF16(kTestPlaceholderText), textfield->text()); textfield->SetReadOnly(false); @@ -563,8 +600,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) { [kTestStringValue stringByAppendingString:kTestPlaceholderText]; [ax_node accessibilitySetValue:kTestStringValue forAttribute:NSAccessibilitySelectedTextAttribute]; - EXPECT_NSEQ(new_string, - AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); + EXPECT_NSEQ(new_string, AXValue()); EXPECT_EQ(base::SysNSStringToUTF16(new_string), textfield->text()); // Replace entire selection. @@ -572,8 +608,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) { textfield->SelectRange(test_range); [ax_node accessibilitySetValue:kTestStringValue forAttribute:NSAccessibilitySelectedTextAttribute]; - EXPECT_NSEQ(kTestStringValue, - AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); + EXPECT_NSEQ(kTestStringValue, AXValue()); EXPECT_EQ(base::SysNSStringToUTF16(kTestStringValue), textfield->text()); // Make sure the cursor is at the end of the Textfield. EXPECT_EQ(gfx::Range([kTestStringValue length]), @@ -590,8 +625,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextfieldWritableAttributes) { textfield->SelectRange(test_range); [ax_node accessibilitySetValue:base::SysUTF16ToNSString(replacement) forAttribute:NSAccessibilitySelectedTextAttribute]; - EXPECT_NSEQ(new_string, - AttributeValueAtMidpoint(NSAccessibilityValueAttribute)); + EXPECT_NSEQ(new_string, AXValue()); EXPECT_EQ(base::SysNSStringToUTF16(new_string), textfield->text()); // Make sure the cursor is at the end of the replacement. EXPECT_EQ(gfx::Range(front.length() + replacement.length()), @@ -645,30 +679,17 @@ TEST_F(NativeWidgetMacAccessibilityTest, TextParameterizedAttributes) { EXPECT_TRUE([ax_node respondsToSelector:sel]); } - NSNumber* line = - [ax_node accessibilityAttributeValue: - NSAccessibilityLineForIndexParameterizedAttribute - forParameter:@5]; + NSNumber* line = AXLineForIndex(@5); EXPECT_TRUE(line); EXPECT_EQ(0, [line intValue]); EXPECT_NSEQ([NSValue valueWithRange:NSMakeRange(0, kTestStringLength)], - [ax_node accessibilityAttributeValue: - NSAccessibilityRangeForLineParameterizedAttribute - forParameter:line]); + AXRangeForLine(line)); // The substring "est st" of kTestStringValue. NSValue* test_range = [NSValue valueWithRange:NSMakeRange(1, 6)]; - EXPECT_NSEQ(@"est st", - [ax_node accessibilityAttributeValue: - NSAccessibilityStringForRangeParameterizedAttribute - forParameter:test_range]); - EXPECT_NSEQ( - @"est st", - [[ax_node - accessibilityAttributeValue: - NSAccessibilityAttributedStringForRangeParameterizedAttribute - forParameter:test_range] string]); + EXPECT_NSEQ(@"est st", AXStringForRange(test_range)); + EXPECT_NSEQ(@"est st", [AXAttributedStringForRange(test_range) string]); // Not implemented yet. Update these tests when they are. EXPECT_NSEQ(nil, @@ -705,8 +726,7 @@ TEST_F(NativeWidgetMacAccessibilityTest, PressAction) { view->SetSize(GetWidgetBounds().size()); id ax_node = A11yElementAtMidpoint(); - EXPECT_NSEQ(NSAccessibilityButtonRole, - AttributeValueAtMidpoint(NSAccessibilityRoleAttribute)); + EXPECT_NSEQ(NSAccessibilityButtonRole, AXRole()); EXPECT_TRUE([[ax_node accessibilityActionNames] containsObject:NSAccessibilityPressAction]); @@ -769,35 +789,35 @@ TEST_F(NativeWidgetMacAccessibilityTest, ProtectedTextfields) { // Explicit checks done without comparing to NSTextField. EXPECT_TRUE( [ax_node accessibilityIsAttributeSettable:NSAccessibilityValueAttribute]); - EXPECT_NSEQ(NSAccessibilityTextFieldRole, [ax_node AXRole]); + EXPECT_NSEQ(NSAccessibilityTextFieldRole, AXRole()); NSString* kShownValue = @"•" @"••••••••••••••••"; // Sanity check. EXPECT_EQ(kTestStringLength, static_cast<int>([kShownValue length])); - EXPECT_NSEQ(kShownValue, [ax_node AXValue]); + EXPECT_NSEQ(kShownValue, AXValue()); // Cursor currently at the end of input. - EXPECT_NSEQ(@"", [ax_node AXSelectedText]); + EXPECT_NSEQ(@"", AXSelectedText()); EXPECT_NSEQ([NSValue valueWithRange:NSMakeRange(kTestStringLength, 0)], - [ax_node AXSelectedTextRange]); + AXSelectedTextRange()); - EXPECT_EQ(kTestStringLength, [[ax_node AXNumberOfCharacters] intValue]); + EXPECT_EQ(kTestStringLength, [AXNumberOfCharacters() intValue]); EXPECT_NSEQ(([NSValue valueWithRange:{0, kTestStringLength}]), - [ax_node AXVisibleCharacterRange]); - EXPECT_EQ(0, [[ax_node AXInsertionPointLineNumber] intValue]); + AXVisibleCharacterRange()); + EXPECT_EQ(0, [AXInsertionPointLineNumber() intValue]); // Test replacing text. textfield->SetText(base::ASCIIToUTF16("123")); - EXPECT_NSEQ(@"•••", [ax_node AXValue]); - EXPECT_EQ(3, [[ax_node AXNumberOfCharacters] intValue]); + EXPECT_NSEQ(@"•••", AXValue()); + EXPECT_EQ(3, [AXNumberOfCharacters() intValue]); textfield->SelectRange(gfx::Range(2, 3)); // Selects "3". [ax_node accessibilitySetValue:@"ab" forAttribute:NSAccessibilitySelectedTextAttribute]; EXPECT_EQ(base::ASCIIToUTF16("12ab"), textfield->text()); - EXPECT_NSEQ(@"••••", [ax_node AXValue]); - EXPECT_EQ(4, [[ax_node AXNumberOfCharacters] intValue]); + EXPECT_NSEQ(@"••••", AXValue()); + EXPECT_EQ(4, [AXNumberOfCharacters() intValue]); } // Test text-specific attributes of Labels. @@ -811,20 +831,40 @@ TEST_F(NativeWidgetMacAccessibilityTest, Label) { id ax_node = A11yElementAtMidpoint(); EXPECT_TRUE(ax_node); - EXPECT_NSEQ(NSAccessibilityStaticTextRole, [ax_node AXRole]); - EXPECT_NSEQ(kTestStringValue, [ax_node AXValue]); + EXPECT_NSEQ(NSAccessibilityStaticTextRole, AXRole()); + EXPECT_NSEQ(kTestStringValue, AXValue()); + + // Title and description for StaticTextRole should always be empty. + EXPECT_NSEQ(@"", AXTitle()); + + // The description is "The purpose of the element, not including the role.". + // BrowserAccessibility returns an empty string instead of nil. Either should + // be OK. + EXPECT_EQ(nil, AXDescription()); // No selection by default. TODO(tapted): Test selection when views::Label // uses RenderTextHarfBuzz on Mac. See http://crbug.com/454835. // For now, this tests that the codepaths are valid for views::Label. - EXPECT_NSEQ(@"", [ax_node AXSelectedText]); + EXPECT_NSEQ(@"", AXSelectedText()); EXPECT_NSEQ([NSValue valueWithRange:NSMakeRange(0, 0)], - [ax_node AXSelectedTextRange]); + AXSelectedTextRange()); - EXPECT_EQ(kTestStringLength, [[ax_node AXNumberOfCharacters] intValue]); + EXPECT_EQ(kTestStringLength, [AXNumberOfCharacters() intValue]); EXPECT_NSEQ(([NSValue valueWithRange:{0, kTestStringLength}]), - [ax_node AXVisibleCharacterRange]); - EXPECT_EQ(0, [[ax_node AXInsertionPointLineNumber] intValue]); + AXVisibleCharacterRange()); + EXPECT_EQ(0, [AXInsertionPointLineNumber() intValue]); + + // Test parameterized attributes for Static Text. + NSNumber* line = AXLineForIndex(@5); + EXPECT_TRUE(line); + EXPECT_EQ(0, [line intValue]); + EXPECT_NSEQ([NSValue valueWithRange:NSMakeRange(0, kTestStringLength)], + AXRangeForLine(line)); + NSValue* test_range = [NSValue valueWithRange:NSMakeRange(1, 6)]; + EXPECT_NSEQ(@"est st", AXStringForRange(test_range)); + EXPECT_NSEQ(@"est st", [AXAttributedStringForRange(test_range) string]); + + // TODO(tapted): Add a test for multiline Labels (currently not supported). } class TestComboboxModel : public ui::ComboboxModel { @@ -851,12 +891,12 @@ TEST_F(NativeWidgetMacAccessibilityTest, Combobox) { id ax_node = A11yElementAtMidpoint(); EXPECT_TRUE(ax_node); - EXPECT_NSEQ(NSAccessibilityPopUpButtonRole, [ax_node AXRole]); + EXPECT_NSEQ(NSAccessibilityPopUpButtonRole, AXRole()); // The initial value should be the first item in the menu. - EXPECT_NSEQ(kTestStringValue, [ax_node AXValue]); + EXPECT_NSEQ(kTestStringValue, AXValue()); combobox->SetSelectedIndex(1); - EXPECT_NSEQ(@"Second Item", [ax_node AXValue]); + EXPECT_NSEQ(@"Second Item", AXValue()); // Expect to see both a press action and a show menu action. This matches // Cocoa behavior. diff --git a/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm b/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm index c29fe0464ff..4e8cf2b6c67 100644 --- a/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm +++ b/chromium/ui/views/widget/native_widget_mac_interactive_uitest.mm @@ -11,6 +11,7 @@ #include "base/macros.h" #include "ui/base/test/ui_controls.h" #import "ui/base/test/windowed_nsnotification_observer.h" +#import "ui/events/test/cocoa_test_event_utils.h" #include "ui/views/bubble/bubble_dialog_delegate.h" #include "ui/views/test/test_widget_observer.h" #include "ui/views/test/widget_test.h" @@ -221,6 +222,57 @@ TEST_F(NativeWidgetMacInteractiveUITest, ParentWindowTrafficLights) { parent_widget->CloseNow(); } +// Test that bubble widgets are dismissed on right mouse down. +TEST_F(NativeWidgetMacInteractiveUITest, BubbleDismiss) { + Widget* parent_widget = CreateTopLevelPlatformWidget(); + parent_widget->SetBounds(gfx::Rect(100, 100, 100, 100)); + ShowKeyWindow(parent_widget); + + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(new TestBubbleView(parent_widget)); + ShowKeyWindow(bubble_widget); + + // First, test with LeftMouseDown in the parent window. + NSEvent* mouse_down = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( + NSMakePoint(50, 50), parent_widget->GetNativeWindow()); + [NSApp sendEvent:mouse_down]; + EXPECT_TRUE(bubble_widget->IsClosed()); + + bubble_widget = + BubbleDialogDelegateView::CreateBubble(new TestBubbleView(parent_widget)); + ShowKeyWindow(bubble_widget); + + // Test with RightMouseDown in the parent window. + mouse_down = cocoa_test_event_utils::RightMouseDownAtPointInWindow( + NSMakePoint(50, 50), parent_widget->GetNativeWindow()); + [NSApp sendEvent:mouse_down]; + EXPECT_TRUE(bubble_widget->IsClosed()); + + bubble_widget = + BubbleDialogDelegateView::CreateBubble(new TestBubbleView(parent_widget)); + ShowKeyWindow(bubble_widget); + + // Test with RightMouseDown in the bubble (bubble should stay open). + mouse_down = cocoa_test_event_utils::RightMouseDownAtPointInWindow( + NSMakePoint(50, 50), bubble_widget->GetNativeWindow()); + [NSApp sendEvent:mouse_down]; + EXPECT_FALSE(bubble_widget->IsClosed()); + bubble_widget->CloseNow(); + + // Test with RightMouseDown when set_close_on_deactivate(false). + TestBubbleView* bubble_view = new TestBubbleView(parent_widget); + bubble_view->set_close_on_deactivate(false); + bubble_widget = BubbleDialogDelegateView::CreateBubble(bubble_view); + ShowKeyWindow(bubble_widget); + + mouse_down = cocoa_test_event_utils::RightMouseDownAtPointInWindow( + NSMakePoint(50, 50), parent_widget->GetNativeWindow()); + [NSApp sendEvent:mouse_down]; + EXPECT_FALSE(bubble_widget->IsClosed()); + + parent_widget->CloseNow(); +} + INSTANTIATE_TEST_CASE_P(NativeWidgetMacInteractiveUITestInstance, NativeWidgetMacInteractiveUITest, ::testing::Bool()); diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm index edd51fb6a44..5a2188cb882 100644 --- a/chromium/ui/views/widget/native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm @@ -664,7 +664,7 @@ TEST_F(NativeWidgetMacTest, AccessibilityIntegration) { NSPoint midpoint = NSMakePoint(NSMidX(nsrect), NSMidY(nsrect)); id hit = [widget->GetNativeWindow() accessibilityHitTest:midpoint]; - id title = [hit accessibilityAttributeValue:NSAccessibilityTitleAttribute]; + id title = [hit accessibilityAttributeValue:NSAccessibilityValueAttribute]; EXPECT_NSEQ(title, @"Green"); widget->CloseNow(); @@ -915,11 +915,17 @@ class ModalDialogDelegate : public DialogDelegateView { explicit ModalDialogDelegate(ui::ModalType modal_type) : modal_type_(modal_type) {} + void set_can_close(bool value) { can_close_ = value; } + // WidgetDelegate: ui::ModalType GetModalType() const override { return modal_type_; } + // DialogDelegate: + bool Close() override { return can_close_; } + private: const ui::ModalType modal_type_; + bool can_close_ = true; DISALLOW_COPY_AND_ASSIGN(ModalDialogDelegate); }; @@ -1697,6 +1703,80 @@ TEST_F(NativeWidgetMacTest, ChangeFocusOnChangeFirstResponder) { widget->CloseNow(); } +// Ensure reparented native view has correct bounds. +TEST_F(NativeWidgetMacTest, ReparentNativeViewBounds) { + Widget* parent = CreateTopLevelFramelessPlatformWidget(); + gfx::Rect parent_rect(100, 100, 300, 200); + parent->SetBounds(parent_rect); + + Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); + params.parent = parent->GetNativeView(); + Widget* widget = new Widget; + widget->Init(params); + widget->SetContentsView(new View); + + NSView* child_view = widget->GetNativeView(); + Widget::ReparentNativeView(child_view, parent->GetNativeView()); + + // Reparented content view has the size of the Widget that created it. + gfx::Rect widget_rect(0, 0, 200, 100); + widget->SetBounds(widget_rect); + EXPECT_EQ(200, NSWidth([child_view frame])); + EXPECT_EQ(100, NSHeight([child_view frame])); + + // Reparented widget has bounds relative to the native parent + NSRect native_parent_rect = NSMakeRect(50, 100, 200, 70); + base::scoped_nsobject<NSView> native_parent( + [[NSView alloc] initWithFrame:native_parent_rect]); + [parent->GetNativeView() addSubview:native_parent]; + + gfx::Rect screen_rect = widget->GetWindowBoundsInScreen(); + EXPECT_EQ(100, screen_rect.x()); + EXPECT_EQ(100, screen_rect.y()); + + Widget::ReparentNativeView(child_view, native_parent); + widget->SetBounds(widget_rect); + screen_rect = widget->GetWindowBoundsInScreen(); + EXPECT_EQ(150, screen_rect.x()); + EXPECT_EQ(130, screen_rect.y()); + + parent->CloseNow(); +} + +// Test two kinds of widgets to re-parent. +TEST_F(NativeWidgetMacTest, ReparentNativeViewTypes) { + std::unique_ptr<Widget> toplevel1(new Widget); + Widget::InitParams toplevel_params = + CreateParams(Widget::InitParams::TYPE_POPUP); + toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + toplevel1->Init(toplevel_params); + + std::unique_ptr<Widget> toplevel2(new Widget); + toplevel2->Init(toplevel_params); + + Widget* child = new Widget; + Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL); + child->Init(child_params); + + Widget::ReparentNativeView(child->GetNativeView(), + toplevel1->GetNativeView()); + EXPECT_EQ([child->GetNativeView() window], + [toplevel1->GetNativeView() window]); + EXPECT_EQ(0, [child->GetNativeWindow() alphaValue]); + + Widget::ReparentNativeView(child->GetNativeView(), + toplevel2->GetNativeView()); + EXPECT_EQ([child->GetNativeView() window], + [toplevel2->GetNativeView() window]); + EXPECT_EQ(0, [child->GetNativeWindow() alphaValue]); + EXPECT_NE(0, [toplevel1->GetNativeWindow() alphaValue]); + + Widget::ReparentNativeView(toplevel2->GetNativeView(), + toplevel1->GetNativeView()); + EXPECT_EQ([toplevel2->GetNativeWindow() parentWindow], + [toplevel1->GetNativeView() window]); +} + // Test class for Full Keyboard Access related tests. class NativeWidgetMacFullKeyboardAccessTest : public NativeWidgetMacTest { public: @@ -1877,6 +1957,19 @@ TEST_F(NativeWidgetMacViewsOrderTest, UnassociatedViewsIsAbove) { ]])); } +// Test -[NSWindowDelegate windowShouldClose:]. +TEST_F(NativeWidgetMacTest, CanClose) { + ModalDialogDelegate* delegate = new ModalDialogDelegate(ui::MODAL_TYPE_NONE); + Widget* widget = + views::DialogDelegate::CreateDialogWidget(delegate, nullptr, nullptr); + NSWindow* window = widget->GetNativeWindow(); + delegate->set_can_close(false); + EXPECT_FALSE([[window delegate] windowShouldClose:window]); + delegate->set_can_close(true); + EXPECT_TRUE([[window delegate] windowShouldClose:window]); + widget->CloseNow(); +} + } // namespace test } // namespace views diff --git a/chromium/ui/views/widget/native_widget_private.h b/chromium/ui/views/widget/native_widget_private.h index dfe37828b68..ebeb4e377b9 100644 --- a/chromium/ui/views/widget/native_widget_private.h +++ b/chromium/ui/views/widget/native_widget_private.h @@ -176,7 +176,7 @@ class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget { virtual void SetSize(const gfx::Size& size) = 0; virtual void StackAbove(gfx::NativeView native_view) = 0; virtual void StackAtTop() = 0; - virtual void SetShape(std::unique_ptr<SkRegion> shape) = 0; + virtual void SetShape(std::unique_ptr<Widget::ShapeRects> shape) = 0; virtual void Close() = 0; virtual void CloseNow() = 0; virtual void Show() = 0; diff --git a/chromium/ui/views/widget/root_view.cc b/chromium/ui/views/widget/root_view.cc index 37e54bdf2d7..acc2aaf61d0 100644 --- a/chromium/ui/views/widget/root_view.cc +++ b/chromium/ui/views/widget/root_view.cc @@ -219,10 +219,6 @@ void RootView::ThemeChanged() { View::PropagateThemeChanged(); } -void RootView::LocaleChanged() { - View::PropagateLocaleChanged(); -} - void RootView::DeviceScaleFactorChanged(float device_scale_factor) { View::PropagateDeviceScaleFactorChanged(device_scale_factor); } @@ -644,12 +640,13 @@ void RootView::OnPaint(gfx::Canvas* canvas) { View::OnPaint(canvas); } -gfx::Vector2d RootView::CalculateOffsetToAncestorWithLayer( +View::LayerOffsetData RootView::CalculateOffsetToAncestorWithLayer( ui::Layer** layer_parent) { - gfx::Vector2d offset(View::CalculateOffsetToAncestorWithLayer(layer_parent)); - if (!layer() && layer_parent) + if (layer() || !widget_->GetLayer()) + return View::CalculateOffsetToAncestorWithLayer(layer_parent); + if (layer_parent) *layer_parent = widget_->GetLayer(); - return offset; + return LayerOffsetData(widget_->GetLayer()->device_scale_factor()); } View::DragInfo* RootView::GetDragInfo() { diff --git a/chromium/ui/views/widget/root_view.h b/chromium/ui/views/widget/root_view.h index d8212a24d78..17ae8112718 100644 --- a/chromium/ui/views/widget/root_view.h +++ b/chromium/ui/views/widget/root_view.h @@ -83,10 +83,6 @@ class VIEWS_EXPORT RootView : public View, // hierarchy. void ThemeChanged(); - // Public API for broadcasting locale change notifications to this View - // hierarchy. - void LocaleChanged(); - // Public API for broadcasting device scale factor change notifications to // this View hierarchy. void DeviceScaleFactorChanged(float device_scale_factor); @@ -125,7 +121,7 @@ class VIEWS_EXPORT RootView : public View, const ViewHierarchyChangedDetails& details) override; void VisibilityChanged(View* starting_from, bool is_visible) override; void OnPaint(gfx::Canvas* canvas) override; - gfx::Vector2d CalculateOffsetToAncestorWithLayer( + View::LayerOffsetData CalculateOffsetToAncestorWithLayer( ui::Layer** layer_parent) override; View::DragInfo* GetDragInfo() override; diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 32a50e8860a..d37585c6fa3 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -567,7 +567,7 @@ void Widget::StackAtTop() { native_widget_->StackAtTop(); } -void Widget::SetShape(std::unique_ptr<SkRegion> shape) { +void Widget::SetShape(std::unique_ptr<ShapeRects> shape) { native_widget_->SetShape(std::move(shape)); } @@ -857,10 +857,6 @@ void Widget::ThemeChanged() { root_view_->ThemeChanged(); } -void Widget::LocaleChanged() { - root_view_->LocaleChanged(); -} - void Widget::DeviceScaleFactorChanged(float device_scale_factor) { root_view_->DeviceScaleFactorChanged(device_scale_factor); } diff --git a/chromium/ui/views/widget/widget.h b/chromium/ui/views/widget/widget.h index 63fcb9d611e..6590ea90b80 100644 --- a/chromium/ui/views/widget/widget.h +++ b/chromium/ui/views/widget/widget.h @@ -109,7 +109,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, public FocusTraversable, public ui::NativeThemeObserver { public: - typedef std::set<Widget*> Widgets; + using Widgets = std::set<Widget*>; + using ShapeRects = std::vector<gfx::Rect>; enum FrameType { FRAME_TYPE_DEFAULT, // Use whatever the default would be. @@ -484,7 +485,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // Sets a shape on the widget. Passing a NULL |shape| reverts the widget to // be rectangular. - void SetShape(std::unique_ptr<SkRegion> shape); + void SetShape(std::unique_ptr<ShapeRects> shape); // Hides the widget then closes it after a return to the message loop. virtual void Close(); @@ -641,10 +642,6 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, // changed. void ThemeChanged(); - // Notifies the view hierarchy contained in this widget that locale resources - // changed. - void LocaleChanged(); - // Notifies the view hierarchy contained in this widget that the device scale // factor changed. void DeviceScaleFactorChanged(float device_scale_factor); @@ -859,7 +856,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, private: friend class ComboboxTest; - friend class CustomButtonTest; + friend class ButtonTest; friend class TextfieldTest; friend class ViewAuraTest; diff --git a/chromium/ui/views/widget/widget_interactive_uitest.cc b/chromium/ui/views/widget/widget_interactive_uitest.cc index 75aba4f70ef..fd641827713 100644 --- a/chromium/ui/views/widget/widget_interactive_uitest.cc +++ b/chromium/ui/views/widget/widget_interactive_uitest.cc @@ -62,7 +62,7 @@ class ExitLoopOnRelease : public View { // View: void OnMouseReleased(const ui::MouseEvent& event) override { GetWidget()->Close(); - base::MessageLoop::current()->QuitNow(); + base::RunLoop::QuitCurrentDeprecated(); } DISALLOW_COPY_AND_ASSIGN(ExitLoopOnRelease); diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index e321d91b9a0..9cddacbcda9 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -8,7 +8,6 @@ #include "base/bind.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -1059,7 +1058,7 @@ TEST_F(WidgetTest, GetWindowPlacement) { #endif WidgetAutoclosePtr widget; -#if defined(USE_X11) && !defined(OS_CHROMEOS) +#if defined(USE_X11) // On desktop-Linux cheat and use non-desktop widgets. On X11, minimize is // asynchronous. Also (harder) showing a window doesn't activate it without // user interaction (or extra steps only done for interactive ui tests). @@ -2096,7 +2095,7 @@ class CloseDestroysWidget : public Widget { ~CloseDestroysWidget() override { if (destroyed_) { *destroyed_ = true; - base::MessageLoop::current()->QuitNow(); + base::RunLoop::QuitCurrentDeprecated(); } } diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index d8086d862d5..9664c746279 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -29,6 +29,7 @@ #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/text_input_type.h" +#include "ui/base/ui_base_switches.h" #include "ui/base/view_prop.h" #include "ui/base/win/internal_constants.h" #include "ui/base/win/lock_state.h" @@ -241,12 +242,12 @@ ui::EventType GetTouchEventType(POINTER_FLAGS pointer_flags) { const int kTouchDownContextResetTimeout = 500; -// Windows does not flag synthesized mouse messages from touch in all cases. -// This causes us grief as we don't want to process touch and mouse messages -// concurrently. Hack as per msdn is to check if the time difference between -// the touch message and the mouse move is within 500 ms and at the same -// location as the cursor. -const int kSynthesizedMouseTouchMessagesTimeDifference = 500; +// Windows does not flag synthesized mouse messages from touch or pen in all +// cases. This causes us grief as we don't want to process touch and mouse +// messages concurrently. Hack as per msdn is to check if the time difference +// between the touch/pen message and the mouse move is within 500 ms and at the +// same location as the cursor. +const int kSynthesizedMouseMessagesTimeDifference = 500; // Currently this flag is always false - see http://crbug.com/763223 const bool kUsePointerEventsForTouch = false; @@ -339,7 +340,7 @@ base::LazyInstance<HWNDMessageHandler::FullscreenWindowMonitorMap>:: //////////////////////////////////////////////////////////////////////////////// // HWNDMessageHandler, public: -long HWNDMessageHandler::last_touch_message_time_ = 0; +long HWNDMessageHandler::last_touch_or_pen_message_time_ = 0; HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) : msg_handled_(FALSE), @@ -360,6 +361,9 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) is_first_nccalc_(true), menu_depth_(0), id_generator_(0), + pen_processor_( + &id_generator_, + base::FeatureList::IsEnabled(features::kDirectManipulationStylus)), in_size_loop_(false), touch_down_contexts_(0), last_mouse_hwheel_time_(0), @@ -2322,7 +2326,7 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, ScreenToClient(hwnd(), &point); - last_touch_message_time_ = ::GetMessageTime(); + last_touch_or_pen_message_time_ = ::GetMessageTime(); gfx::Point touch_point(point.x, point.y); unsigned int touch_id = id_generator_.GetGeneratedID(input[i].dwID); @@ -2346,7 +2350,7 @@ LRESULT HWNDMessageHandler::OnTouchEvent(UINT message, touch_ids_.erase(input[i].dwID); GenerateTouchEvent(ui::ET_TOUCH_RELEASED, touch_point, touch_id, event_time, &touch_events); - id_generator_.ReleaseNumber(input[i].dwID); + id_generator_.MaybeReleaseNumber(input[i].dwID); } } } @@ -2715,8 +2719,16 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, return -1; } - POINTER_INFO pointer_info = pointer_touch_info.pointerInfo; + // Ignore enter/leave events, otherwise they will be converted in + // |GetTouchEventType| to ET_TOUCH_PRESSED/ET_TOUCH_RELEASED events, which + // is not correct. + if (message == WM_POINTERENTER || message == WM_POINTERLEAVE) { + SetMsgHandled(TRUE); + return 0; + } + unsigned int mapped_pointer_id = id_generator_.GetGeneratedID(pointer_id); + POINTER_INFO pointer_info = pointer_touch_info.pointerInfo; POINT client_point = pointer_info.ptPixelLocationRaw; ScreenToClient(hwnd(), &client_point); gfx::Point touch_point = gfx::Point(client_point.x, client_point.y); @@ -2741,9 +2753,9 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, ui::TouchEvent event( event_type, touch_point, event_time, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, pointer_id, - radius_x, radius_y, pressure, 0.0f, 0.0f, 0.0f, - pointer_touch_info.orientation), + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, + mapped_pointer_id, radius_x, radius_y, pressure, 0.0f, + 0.0f, 0.0f, pointer_touch_info.orientation), ui::GetModifiersFromKeyState(), rotation_angle); event.latency()->AddLatencyNumberWithTimestamp( @@ -2754,6 +2766,8 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, base::WeakPtr<HWNDMessageHandler> ref(weak_factory_.GetWeakPtr()); delegate_->HandleTouchEvent(event); + if (event_type == ui::ET_TOUCH_RELEASED) + id_generator_.MaybeReleaseNumber(pointer_id); if (ref) SetMsgHandled(TRUE); return 0; @@ -2774,82 +2788,31 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypePen(UINT message, return -1; } - // We are now creating a fake mouse event with pointer type of pen from - // the WM_POINTER message and then setting up an associated pointer - // details in the MouseEvent which contains the pen's information. - ui::EventPointerType input_type = ui::EventPointerType::POINTER_TYPE_PEN; - // TODO(lanwei): penFlags of PEN_FLAG_INVERTED may also indicate we are using - // an eraser, but it is under debate. Please see - // https://github.com/w3c/pointerevents/issues/134/. - if (pointer_pen_info.penFlags & PEN_FLAG_ERASER) - input_type = ui::EventPointerType::POINTER_TYPE_ERASER; - - float pressure = static_cast<float>(pointer_pen_info.pressure) / 1024; - float rotation = pointer_pen_info.rotation; - int tilt_x = pointer_pen_info.tiltX; - int tilt_y = pointer_pen_info.tiltY; POINT client_point = pointer_pen_info.pointerInfo.ptPixelLocationRaw; ScreenToClient(hwnd(), &client_point); gfx::Point point = gfx::Point(client_point.x, client_point.y); - ui::EventType event_type = ui::ET_MOUSE_MOVED; - int flag = 0; - int click_count = 0; - switch (message) { - case WM_POINTERDOWN: - event_type = ui::ET_MOUSE_PRESSED; - if (pointer_pen_info.pointerInfo.ButtonChangeType == - POINTER_CHANGE_SECONDBUTTON_DOWN) { - flag = ui::EF_RIGHT_MOUSE_BUTTON; - } else { - flag = ui::EF_LEFT_MOUSE_BUTTON; - } - click_count = 1; - break; - case WM_POINTERUP: - event_type = ui::ET_MOUSE_RELEASED; - if (pointer_pen_info.pointerInfo.ButtonChangeType == - POINTER_CHANGE_SECONDBUTTON_UP) { - flag = ui::EF_RIGHT_MOUSE_BUTTON; - } else { - flag = ui::EF_LEFT_MOUSE_BUTTON; - } - click_count = 1; - break; - case WM_POINTERUPDATE: - event_type = ui::ET_MOUSE_DRAGGED; - if (pointer_pen_info.pointerInfo.pointerFlags & - POINTER_FLAG_FIRSTBUTTON) { - flag = ui::EF_LEFT_MOUSE_BUTTON; - } else if (pointer_pen_info.pointerInfo.pointerFlags & - POINTER_FLAG_SECONDBUTTON) { - flag = ui::EF_RIGHT_MOUSE_BUTTON; - } else { - event_type = ui::ET_MOUSE_MOVED; - } - break; - case WM_POINTERENTER: - event_type = ui::ET_MOUSE_ENTERED; - break; - case WM_POINTERLEAVE: - event_type = ui::ET_MOUSE_EXITED; - break; - default: - NOTREACHED(); - } - ui::PointerDetails pointer_details( - input_type, pointer_id, /* radius_x */ 0.0f, /* radius_y */ 0.0f, - pressure, tilt_x, tilt_y, /* tangential_pressure */ 0.0f, rotation); - ui::MouseEvent event(event_type, point, point, base::TimeTicks::Now(), flag, - flag, pointer_details); - event.SetClickCount(click_count); + + std::unique_ptr<ui::Event> event = pen_processor_.GenerateEvent( + message, pointer_id, pointer_pen_info, point); // 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); + if (event) { + if (event->IsTouchEvent()) { + delegate_->HandleTouchEvent(*event->AsTouchEvent()); + } else if (event->IsMouseEvent()) { + delegate_->HandleMouseEvent(*event->AsMouseEvent()); + } else { + NOTREACHED(); + } + last_touch_or_pen_message_time_ = ::GetMessageTime(); + } + // Always mark as handled as we don't want to generate WM_MOUSE compatiblity + // events. if (ref) - SetMsgHandled(handled); + SetMsgHandled(TRUE); return 0; } @@ -2861,9 +2824,10 @@ bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message, // Ignore mouse messages which occur at the same location as the current // cursor position and within a time difference of 500 ms from the last // touch message. - if (last_touch_message_time_ && message_time >= last_touch_message_time_ && - ((message_time - last_touch_message_time_) <= - kSynthesizedMouseTouchMessagesTimeDifference)) { + if (last_touch_or_pen_message_time_ && + message_time >= last_touch_or_pen_message_time_ && + ((message_time - last_touch_or_pen_message_time_) <= + kSynthesizedMouseMessagesTimeDifference)) { POINT mouse_location = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); ::ClientToScreen(hwnd(), &mouse_location); POINT cursor_pos = {0}; diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index 51f399a202f..ad432b4d9ac 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -29,6 +29,7 @@ #include "ui/gfx/sequential_id_generator.h" #include "ui/gfx/win/window_impl.h" #include "ui/views/views_export.h" +#include "ui/views/win/pen_event_processor.h" namespace gfx { class ImageSkia; @@ -543,6 +544,19 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, WPARAM w_param, LPARAM l_param); + LRESULT GenerateMouseEventFromPointerEvent( + UINT message, + UINT32 pointer_id, + const POINTER_INFO& pointer_info, + const gfx::Point& point, + const ui::PointerDetails& pointer_details); + LRESULT GenerateTouchEventFromPointerEvent( + UINT message, + UINT32 pointer_id, + const POINTER_INFO& pointer_info, + const gfx::Point& point, + const ui::PointerDetails& pointer_details); + // Returns true if the mouse message passed in is an OS synthesized mouse // message. // |message| identifies the mouse message. @@ -671,6 +685,8 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, // Generates touch-ids for touch-events. ui::SequentialIDGenerator id_generator_; + PenEventProcessor pen_processor_; + // Set to true if we are in the context of a sizing operation. bool in_size_loop_; @@ -685,11 +701,11 @@ class VIEWS_EXPORT HWNDMessageHandler : public gfx::WindowImpl, // native SetFocus calls invoked in the views code. int touch_down_contexts_; - // Time the last touch message was received. Used to flag mouse messages - // synthesized by Windows for touch which are not flagged by the OS as - // synthesized mouse messages. For more information please refer to - // the IsMouseEventFromTouch function. - static long last_touch_message_time_; + // Time the last touch or pen message was received. Used to flag mouse + // messages synthesized by Windows for touch which are not flagged by the OS + // as synthesized mouse messages. For more information please refer to the + // IsMouseEventFromTouch function. + static long last_touch_or_pen_message_time_; // Time the last WM_MOUSEHWHEEL message is received. Please refer to the // HandleMouseEventInternal function as to why this is needed. diff --git a/chromium/ui/views/win/hwnd_message_handler_delegate.h b/chromium/ui/views/win/hwnd_message_handler_delegate.h index 72d3213945d..2168aedea4b 100644 --- a/chromium/ui/views/win/hwnd_message_handler_delegate.h +++ b/chromium/ui/views/win/hwnd_message_handler_delegate.h @@ -188,6 +188,10 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // handled by the delegate. virtual bool HandleMouseEvent(const ui::MouseEvent& event) = 0; + // Called when a pointer event is received. Returns true if the event was + // handled by the delegate. + virtual bool HandlePointerEvent(ui::PointerEvent* event) = 0; + // Called when an untranslated key event is received (i.e. pre-IME // translation). virtual void HandleKeyEvent(ui::KeyEvent* event) = 0; diff --git a/chromium/ui/views/win/pen_event_processor.cc b/chromium/ui/views/win/pen_event_processor.cc new file mode 100644 index 00000000000..d42fe1eff3c --- /dev/null +++ b/chromium/ui/views/win/pen_event_processor.cc @@ -0,0 +1,197 @@ +// 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 "pen_event_processor.h" + +#include "base/memory/ptr_util.h" +#include "base/time/time.h" +#include "ui/events/event_utils.h" + +namespace views { +namespace { + +int GetFlagsFromPointerMessage(UINT message, const POINTER_INFO& pointer_info) { + int flags = ui::EF_NONE; + if (pointer_info.pointerFlags & POINTER_FLAG_FIRSTBUTTON) + flags |= ui::EF_LEFT_MOUSE_BUTTON; + + if (pointer_info.pointerFlags & POINTER_FLAG_SECONDBUTTON) + flags |= ui::EF_RIGHT_MOUSE_BUTTON; + + if (message == WM_POINTERUP) { + if (pointer_info.ButtonChangeType == POINTER_CHANGE_SECONDBUTTON_UP) + flags |= ui::EF_RIGHT_MOUSE_BUTTON; + else + flags |= ui::EF_LEFT_MOUSE_BUTTON; + } + return flags; +} + +} // namespace + +PenEventProcessor::PenEventProcessor(ui::SequentialIDGenerator* id_generator, + bool direct_manipulation_enabled) + : id_generator_(id_generator), + direct_manipulation_enabled_(direct_manipulation_enabled) {} + +PenEventProcessor::~PenEventProcessor() {} + +std::unique_ptr<ui::Event> PenEventProcessor::GenerateEvent( + UINT message, + UINT32 pointer_id, + const POINTER_PEN_INFO& pointer_pen_info, + const gfx::Point& point) { + unsigned int mapped_pointer_id = id_generator_->GetGeneratedID(pointer_id); + + // We are now creating a fake mouse event with pointer type of pen from + // the WM_POINTER message and then setting up an associated pointer + // details in the MouseEvent which contains the pen's information. + ui::EventPointerType input_type = ui::EventPointerType::POINTER_TYPE_PEN; + // TODO(lanwei): penFlags of PEN_FLAG_INVERTED may also indicate we are using + // an eraser, but it is under debate. Please see + // https://github.com/w3c/pointerevents/issues/134/. + if (pointer_pen_info.penFlags & PEN_FLAG_ERASER) + input_type = ui::EventPointerType::POINTER_TYPE_ERASER; + + // convert pressure into a float [0, 1]. The range of the pressure is + // [0, 1024] as specified on MSDN. + float pressure = static_cast<float>(pointer_pen_info.pressure) / 1024; + float rotation = pointer_pen_info.rotation; + int tilt_x = pointer_pen_info.tiltX; + int tilt_y = pointer_pen_info.tiltY; + ui::PointerDetails pointer_details( + input_type, mapped_pointer_id, /* radius_x */ 0.0f, /* radius_y */ 0.0f, + pressure, tilt_x, tilt_y, /* tangential_pressure */ 0.0f, rotation); + + // If the flag is disabled, we send mouse events for all pen inputs. + if (!direct_manipulation_enabled_) { + return GenerateMouseEvent(message, pointer_id, pointer_pen_info.pointerInfo, + point, pointer_details); + } + bool is_pointer_event = + message == WM_POINTERENTER || message == WM_POINTERLEAVE; + + // Send MouseEvents when the pen is hovering or any buttons (other than the + // tip) are depressed when the stylus makes contact with the digitizer. Ensure + // we read |send_touch_for_pen_| before we process the event as we want to + // ensure a TouchRelease is sent appropriately at the end when the stylus is + // no longer in contact with the digitizer. + bool send_touch = send_touch_for_pen_; + if (pointer_pen_info.pointerInfo.pointerFlags & POINTER_FLAG_INCONTACT) { + if (!pen_in_contact_) { + send_touch = send_touch_for_pen_ = + (pointer_pen_info.pointerInfo.pointerFlags & + (POINTER_FLAG_SECONDBUTTON | POINTER_FLAG_THIRDBUTTON | + POINTER_FLAG_FOURTHBUTTON | POINTER_FLAG_FIFTHBUTTON)) == 0; + } + pen_in_contact_ = true; + } else { + pen_in_contact_ = false; + send_touch_for_pen_ = false; + } + + if (is_pointer_event || !send_touch) { + return GenerateMouseEvent(message, pointer_id, pointer_pen_info.pointerInfo, + point, pointer_details); + } + + return GenerateTouchEvent(message, pointer_id, pointer_pen_info.pointerInfo, + point, pointer_details); +} + +std::unique_ptr<ui::Event> PenEventProcessor::GenerateMouseEvent( + UINT message, + UINT32 pointer_id, + const POINTER_INFO& pointer_info, + const gfx::Point& point, + const ui::PointerDetails& pointer_details) { + ui::EventType event_type = ui::ET_MOUSE_MOVED; + int flag = GetFlagsFromPointerMessage(message, pointer_info); + int changed_flag = ui::EF_NONE; + int click_count = 0; + switch (message) { + case WM_POINTERDOWN: + event_type = ui::ET_MOUSE_PRESSED; + if (pointer_info.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_DOWN) + changed_flag = ui::EF_LEFT_MOUSE_BUTTON; + else + changed_flag = ui::EF_RIGHT_MOUSE_BUTTON; + click_count = 1; + sent_mouse_down_ = true; + break; + case WM_POINTERUP: + event_type = ui::ET_MOUSE_RELEASED; + if (pointer_info.ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP) + changed_flag = ui::EF_LEFT_MOUSE_BUTTON; + else + changed_flag = ui::EF_RIGHT_MOUSE_BUTTON; + id_generator_->MaybeReleaseNumber(pointer_id); + click_count = 1; + if (!sent_mouse_down_) + return nullptr; + sent_mouse_down_ = false; + break; + case WM_POINTERUPDATE: + event_type = ui::ET_MOUSE_DRAGGED; + if (flag == ui::EF_NONE) + event_type = ui::ET_MOUSE_MOVED; + break; + case WM_POINTERENTER: + event_type = ui::ET_MOUSE_ENTERED; + break; + case WM_POINTERLEAVE: + event_type = ui::ET_MOUSE_EXITED; + id_generator_->MaybeReleaseNumber(pointer_id); + break; + default: + NOTREACHED(); + } + std::unique_ptr<ui::Event> event = base::MakeUnique<ui::MouseEvent>( + event_type, point, point, ui::EventTimeForNow(), flag, changed_flag, + pointer_details); + event->AsMouseEvent()->SetClickCount(click_count); + return event; +} + +std::unique_ptr<ui::Event> PenEventProcessor::GenerateTouchEvent( + UINT message, + UINT32 pointer_id, + const POINTER_INFO& pointer_info, + const gfx::Point& point, + const ui::PointerDetails& pointer_details) { + int flags = GetFlagsFromPointerMessage(message, pointer_info); + + ui::EventType event_type = ui::ET_TOUCH_MOVED; + switch (message) { + case WM_POINTERDOWN: + event_type = ui::ET_TOUCH_PRESSED; + sent_touch_start_ = true; + break; + case WM_POINTERUP: + event_type = ui::ET_TOUCH_RELEASED; + id_generator_->MaybeReleaseNumber(pointer_id); + if (!sent_touch_start_) + return nullptr; + sent_touch_start_ = false; + break; + case WM_POINTERUPDATE: + event_type = ui::ET_TOUCH_MOVED; + break; + default: + NOTREACHED(); + } + + const base::TimeTicks event_time = ui::EventTimeForNow(); + + int rotation_angle = static_cast<int>(pointer_details.twist) % 180; + if (rotation_angle < 0) + rotation_angle += 180; + std::unique_ptr<ui::Event> event = base::MakeUnique<ui::TouchEvent>( + event_type, point, event_time, pointer_details, flags, rotation_angle); + event->latency()->AddLatencyNumberWithTimestamp( + ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0, event_time, 1); + return event; +} + +} // namespace views diff --git a/chromium/ui/views/win/pen_event_processor.h b/chromium/ui/views/win/pen_event_processor.h new file mode 100644 index 00000000000..60d55489d5e --- /dev/null +++ b/chromium/ui/views/win/pen_event_processor.h @@ -0,0 +1,63 @@ +// 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_VIEWS_WIN_PEN_EVENT_PROCESSOR_H_ +#define UI_VIEWS_WIN_PEN_EVENT_PROCESSOR_H_ + +#include <windows.h> + +#include <memory> + +#include "ui/events/event.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/sequential_id_generator.h" +#include "ui/views/views_export.h" + +namespace views { + +// This class handles the processing pen event state information +// from native Windows events and returning appropriate +// ui::Events for the current state. +class VIEWS_EXPORT PenEventProcessor { + public: + // |id_generator| must outlive this object's lifecycle. + PenEventProcessor(ui::SequentialIDGenerator* id_generator, + bool direct_manipulation_enabled); + ~PenEventProcessor(); + + // Generate an appropriate ui::Event for a given pen pointer. + // May return nullptr if no event should be dispatched. + std::unique_ptr<ui::Event> GenerateEvent( + UINT message, + UINT32 pointer_id, + const POINTER_PEN_INFO& pen_pointer_info, + const gfx::Point& point); + + private: + std::unique_ptr<ui::Event> GenerateMouseEvent( + UINT message, + UINT32 pointer_id, + const POINTER_INFO& pointer_info, + const gfx::Point& point, + const ui::PointerDetails& pointer_details); + std::unique_ptr<ui::Event> GenerateTouchEvent( + UINT message, + UINT32 pointer_id, + const POINTER_INFO& pointer_info, + const gfx::Point& point, + const ui::PointerDetails& pointer_details); + + ui::SequentialIDGenerator* id_generator_; + bool direct_manipulation_enabled_; + bool pen_in_contact_ = false; + bool send_touch_for_pen_ = false; + bool sent_mouse_down_ = false; + bool sent_touch_start_ = false; + + DISALLOW_COPY_AND_ASSIGN(PenEventProcessor); +}; + +} // namespace views + +#endif // UI_VIEWS_WIN_PEN_EVENT_PROCESSOR_H_ diff --git a/chromium/ui/views/win/pen_event_processor_unittest.cc b/chromium/ui/views/win/pen_event_processor_unittest.cc new file mode 100644 index 00000000000..228d2453b67 --- /dev/null +++ b/chromium/ui/views/win/pen_event_processor_unittest.cc @@ -0,0 +1,151 @@ +// 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/win/pen_event_processor.h" + +#include <gtest/gtest.h> + +#include "ui/gfx/sequential_id_generator.h" + +namespace views { + +TEST(PenProcessorTest, TypicalCaseDMDisabled) { + ui::SequentialIDGenerator id_generator(0); + PenEventProcessor processor(&id_generator, + /*direct_manipulation_enabled*/ false); + + POINTER_PEN_INFO pen_info; + memset(&pen_info, 0, sizeof(POINTER_PEN_INFO)); + gfx::Point point(100, 100); + + std::unique_ptr<ui::Event> event = + processor.GenerateEvent(WM_POINTERENTER, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_ENTERED, event->AsMouseEvent()->type()); + + pen_info.pointerInfo.pointerFlags = + POINTER_FLAG_INCONTACT | POINTER_FLAG_FIRSTBUTTON; + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_DOWN; + + event = processor.GenerateEvent(WM_POINTERDOWN, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_PRESSED, event->AsMouseEvent()->type()); + EXPECT_EQ(1, event->AsMouseEvent()->GetClickCount()); + EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, + event->AsMouseEvent()->changed_button_flags()); + + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_NONE; + event = processor.GenerateEvent(WM_POINTERUPDATE, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_DRAGGED, event->AsMouseEvent()->type()); + + pen_info.pointerInfo.pointerFlags = POINTER_FLAG_INCONTACT; + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP; + event = processor.GenerateEvent(WM_POINTERUP, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_RELEASED, event->AsMouseEvent()->type()); + EXPECT_EQ(ui::EF_LEFT_MOUSE_BUTTON, + event->AsMouseEvent()->changed_button_flags()); + + pen_info.pointerInfo.pointerFlags = POINTER_FLAG_NONE; + event = processor.GenerateEvent(WM_POINTERUPDATE, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_MOVED, event->AsMouseEvent()->type()); + + event = processor.GenerateEvent(WM_POINTERLEAVE, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_EXITED, event->AsMouseEvent()->type()); +} + +TEST(PenProcessorTest, TypicalCaseDMEnabled) { + ui::SequentialIDGenerator id_generator(0); + PenEventProcessor processor(&id_generator, + /*direct_manipulation_enabled*/ true); + + POINTER_PEN_INFO pen_info; + memset(&pen_info, 0, sizeof(POINTER_PEN_INFO)); + gfx::Point point(100, 100); + + std::unique_ptr<ui::Event> event = + processor.GenerateEvent(WM_POINTERENTER, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_ENTERED, event->AsMouseEvent()->type()); + + pen_info.pointerInfo.pointerFlags = + POINTER_FLAG_INCONTACT | POINTER_FLAG_FIRSTBUTTON; + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_DOWN; + + event = processor.GenerateEvent(WM_POINTERDOWN, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsTouchEvent()); + EXPECT_EQ(ui::ET_TOUCH_PRESSED, event->AsTouchEvent()->type()); + + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_NONE; + event = processor.GenerateEvent(WM_POINTERUPDATE, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsTouchEvent()); + EXPECT_EQ(ui::ET_TOUCH_MOVED, event->AsTouchEvent()->type()); + + pen_info.pointerInfo.pointerFlags = POINTER_FLAG_NONE; + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP; + event = processor.GenerateEvent(WM_POINTERUP, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsTouchEvent()); + EXPECT_EQ(ui::ET_TOUCH_RELEASED, event->AsTouchEvent()->type()); + + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_NONE; + event = processor.GenerateEvent(WM_POINTERUPDATE, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_MOVED, event->type()); + + event = processor.GenerateEvent(WM_POINTERLEAVE, 0, pen_info, point); + ASSERT_TRUE(event); + ASSERT_TRUE(event->IsMouseEvent()); + EXPECT_EQ(ui::ET_MOUSE_EXITED, event->AsMouseEvent()->type()); +} + +TEST(PenProcessorTest, UnpairedPointerDownTouchDMEnabled) { + ui::SequentialIDGenerator id_generator(0); + PenEventProcessor processor(&id_generator, + /*direct_manipulation_enabled*/ true); + + POINTER_PEN_INFO pen_info; + memset(&pen_info, 0, sizeof(POINTER_PEN_INFO)); + gfx::Point point(100, 100); + + pen_info.pointerInfo.pointerFlags = + POINTER_FLAG_INCONTACT | POINTER_FLAG_FIRSTBUTTON; + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP; + + std::unique_ptr<ui::Event> event = + processor.GenerateEvent(WM_POINTERUP, 0, pen_info, point); + EXPECT_EQ(nullptr, event.get()); +} + +TEST(PenProcessorTest, UnpairedPointerDownMouseDMEnabled) { + ui::SequentialIDGenerator id_generator(0); + PenEventProcessor processor(&id_generator, + /*direct_manipulation_enabled*/ true); + + POINTER_PEN_INFO pen_info; + memset(&pen_info, 0, sizeof(POINTER_PEN_INFO)); + gfx::Point point(100, 100); + + pen_info.pointerInfo.pointerFlags = POINTER_FLAG_FIRSTBUTTON; + pen_info.pointerInfo.ButtonChangeType = POINTER_CHANGE_FIRSTBUTTON_UP; + + std::unique_ptr<ui::Event> event = + processor.GenerateEvent(WM_POINTERUP, 0, pen_info, point); + EXPECT_EQ(nullptr, event.get()); +} + +} // namespace views diff --git a/chromium/ui/views/win/windows_session_change_observer.cc b/chromium/ui/views/win/windows_session_change_observer.cc index d3663e5cc22..e2021dbcdc3 100644 --- a/chromium/ui/views/win/windows_session_change_observer.cc +++ b/chromium/ui/views/win/windows_session_change_observer.cc @@ -13,10 +13,9 @@ #include "base/callback.h" #include "base/location.h" #include "base/macros.h" -#include "base/memory/ref_counted.h" #include "base/memory/singleton.h" #include "base/observer_list.h" -#include "base/task_runner.h" +#include "base/task_scheduler/post_task.h" #include "ui/gfx/win/singleton_hwnd.h" #include "ui/gfx/win/singleton_hwnd_observer.h" #include "ui/views/views_delegate.h" @@ -67,19 +66,19 @@ class WindowsSessionChangeObserver::WtsRegistrationNotificationManager { singleton_hwnd_observer_.reset(new gfx::SingletonHwndObserver( base::Bind(&WtsRegistrationNotificationManager::OnWndProc, base::Unretained(this)))); - scoped_refptr<base::TaskRunner> task_runner; - if (ViewsDelegate::GetInstance()) { - task_runner = ViewsDelegate::GetInstance()->GetBlockingPoolTaskRunner(); - } - base::Closure wts_register = - base::Bind(base::IgnoreResult(&WTSRegisterSessionNotification), - gfx::SingletonHwnd::GetInstance()->hwnd(), - NOTIFY_FOR_THIS_SESSION); - if (task_runner) { - task_runner->PostTask(FROM_HERE, wts_register); + base::OnceClosure wts_register = base::BindOnce( + base::IgnoreResult(&WTSRegisterSessionNotification), + gfx::SingletonHwnd::GetInstance()->hwnd(), NOTIFY_FOR_THIS_SESSION); + + // This should probably always be async but it wasn't async in the absence + // of a ViewsDelegate prior to the migration to TaskScheduler and making it + // async breaks many unrelated views unittests. + if (ViewsDelegate::GetInstance()) { + base::CreateCOMSTATaskRunnerWithTraits({})->PostTask( + FROM_HERE, std::move(wts_register)); } else { - wts_register.Run(); + std::move(wts_register).Run(); } } diff --git a/chromium/ui/views/window/custom_frame_view.cc b/chromium/ui/views/window/custom_frame_view.cc index a2eaafc2fbb..22d0bed6be8 100644 --- a/chromium/ui/views/window/custom_frame_view.cc +++ b/chromium/ui/views/window/custom_frame_view.cc @@ -185,9 +185,9 @@ void CustomFrameView::GetWindowMask(const gfx::Size& size, } void CustomFrameView::ResetWindowControls() { - restore_button_->SetState(CustomButton::STATE_NORMAL); - minimize_button_->SetState(CustomButton::STATE_NORMAL); - maximize_button_->SetState(CustomButton::STATE_NORMAL); + restore_button_->SetState(Button::STATE_NORMAL); + minimize_button_->SetState(Button::STATE_NORMAL); + maximize_button_->SetState(Button::STATE_NORMAL); // The close button isn't affected by this constraint. } @@ -598,11 +598,11 @@ ImageButton* CustomFrameView::InitWindowCaptionButton( ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); ImageButton* button = new ImageButton(this); button->SetAccessibleName(l10n_util::GetStringUTF16(accessibility_string_id)); - button->SetImage(CustomButton::STATE_NORMAL, + button->SetImage(Button::STATE_NORMAL, rb.GetImageNamed(normal_image_id).ToImageSkia()); - button->SetImage(CustomButton::STATE_HOVERED, + button->SetImage(Button::STATE_HOVERED, rb.GetImageNamed(hot_image_id).ToImageSkia()); - button->SetImage(CustomButton::STATE_PRESSED, + button->SetImage(Button::STATE_PRESSED, rb.GetImageNamed(pushed_image_id).ToImageSkia()); AddChildView(button); return button; diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc index 7d62cadf15e..256ba5dcc0d 100644 --- a/chromium/ui/views/window/dialog_client_view.cc +++ b/chromium/ui/views/window/dialog_client_view.cc @@ -12,8 +12,8 @@ #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/controls/button/blue_button.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/button/checkbox.h" -#include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/md_text_button.h" #include "ui/views/layout/grid_layout.h" @@ -139,9 +139,11 @@ const DialogClientView* DialogClientView::AsDialogClientView() const { // DialogClientView, View overrides: gfx::Size DialogClientView::CalculatePreferredSize() const { + gfx::Size contents_size = ClientView::CalculatePreferredSize(); + const gfx::Insets& content_margins = GetDialogDelegate()->margins(); + contents_size.Enlarge(content_margins.width(), content_margins.height()); return GetBoundingSizeForVerticalStack( - ClientView::CalculatePreferredSize(), - button_row_container_->GetPreferredSize()); + contents_size, button_row_container_->GetPreferredSize()); } gfx::Size DialogClientView::GetMinimumSize() const { @@ -172,8 +174,11 @@ void DialogClientView::Layout() { button_row_container_->SetSize( gfx::Size(width(), button_row_container_->GetHeightForWidth(width()))); button_row_container_->SetY(height() - button_row_container_->height()); - if (contents_view()) - contents_view()->SetSize(gfx::Size(width(), button_row_container_->y())); + if (contents_view()) { + gfx::Rect contents_bounds(width(), button_row_container_->y()); + contents_bounds.Inset(GetDialogDelegate()->margins()); + contents_view()->SetBoundsRect(contents_bounds); + } } bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) { @@ -395,7 +400,7 @@ void DialogClientView::SetupLayout() { // will be in |link[0]|. Skip that if it is not a button, or if it is a // Checkbox (which extends LabelButton). Otherwise, link everything. bool skip_first_link = - views[0] && (!CustomButton::AsCustomButton(views[0]) || + views[0] && (!Button::AsButton(views[0]) || views[0]->GetClassName() == Checkbox::kViewClassName); if (skip_first_link) column_set->LinkColumnSizes(link[1], link[2], -1); diff --git a/chromium/ui/views/window/dialog_client_view_unittest.cc b/chromium/ui/views/window/dialog_client_view_unittest.cc index a143aa95082..4192b0ee039 100644 --- a/chromium/ui/views/window/dialog_client_view_unittest.cc +++ b/chromium/ui/views/window/dialog_client_view_unittest.cc @@ -465,4 +465,18 @@ TEST_F(DialogClientViewTest, FocusMultipleButtons) { EXPECT_TRUE(client_view()->cancel_button()->HasFocus()); } +// Ensures that the focus persistence works correctly when buttons are removed. +TEST_F(DialogClientViewTest, FocusChangingButtons) { + // Start with ok and cancel buttons. + widget()->Show(); + SetDialogButtons(ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK); + client_view()->cancel_button()->RequestFocus(); // Set focus. + FocusManager* focus_manager = GetFocusManager(); + EXPECT_EQ(client_view()->cancel_button(), focus_manager->GetFocusedView()); + + // Remove buttons. + SetDialogButtons(ui::DIALOG_BUTTON_NONE); + EXPECT_EQ(nullptr, focus_manager->GetFocusedView()); +} + } // namespace views diff --git a/chromium/ui/views/window/dialog_delegate.cc b/chromium/ui/views/window/dialog_delegate.cc index 0484d5d06fd..ee398d8077f 100644 --- a/chromium/ui/views/window/dialog_delegate.cc +++ b/chromium/ui/views/window/dialog_delegate.cc @@ -31,11 +31,20 @@ namespace views { //////////////////////////////////////////////////////////////////////////////// // DialogDelegate: -DialogDelegate::DialogDelegate() : supports_custom_frame_(true) { +DialogDelegate::DialogDelegate() + : supports_custom_frame_(true), + // TODO(crbug.com/733040): Most subclasses assume they must set their own + // margins explicitly, so we set them to 0 here for now to avoid doubled + // margins. + margins_(0) { UMA_HISTOGRAM_BOOLEAN("Dialog.DialogDelegate.Create", true); + creation_time_ = base::TimeTicks::Now(); } -DialogDelegate::~DialogDelegate() {} +DialogDelegate::~DialogDelegate() { + UMA_HISTOGRAM_LONG_TIMES("Dialog.DialogDelegate.Duration", + base::TimeTicks::Now() - creation_time_); +} // static Widget* DialogDelegate::CreateDialogWidget(WidgetDelegate* delegate, diff --git a/chromium/ui/views/window/dialog_delegate.h b/chromium/ui/views/window/dialog_delegate.h index 8d2205b4462..598e8bae52e 100644 --- a/chromium/ui/views/window/dialog_delegate.h +++ b/chromium/ui/views/window/dialog_delegate.h @@ -8,6 +8,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/strings/string16.h" +#include "base/time/time.h" #include "ui/accessibility/ax_enums.h" #include "ui/base/models/dialog_model.h" #include "ui/base/ui_base_types.h" @@ -111,6 +112,9 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, // frame. virtual bool ShouldUseCustomFrame() const; + const gfx::Insets& margins() const { return margins_; } + void set_margins(const gfx::Insets& margins) { margins_ = margins; } + // A helper for accessing the DialogClientView object contained by this // delegate's Window. const DialogClientView* GetDialogClientView() const; @@ -124,6 +128,14 @@ class VIEWS_EXPORT DialogDelegate : public ui::DialogModel, // A flag indicating whether this dialog is able to use the custom frame // style for dialogs. bool supports_custom_frame_; + + // The margins between the content and the inside of the border. + gfx::Insets margins_; + + // The time the dialog is created. + base::TimeTicks creation_time_; + + DISALLOW_COPY_AND_ASSIGN(DialogDelegate); }; // A DialogDelegate implementation that is-a View. Used to override GetWidget() diff --git a/chromium/ui/views/window/nav_button_provider.h b/chromium/ui/views/window/nav_button_provider.h new file mode 100644 index 00000000000..499563328f0 --- /dev/null +++ b/chromium/ui/views/window/nav_button_provider.h @@ -0,0 +1,54 @@ +// 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_VIEWS_NAV_BUTTON_PROVIDER_H_ +#define UI_VIEWS_NAV_BUTTON_PROVIDER_H_ + +#include "ui/views/controls/button/button.h" + +namespace chrome { +enum class FrameButtonDisplayType; +} + +namespace gfx { +class ImageSkia; +class Insets; +} // namespace gfx + +namespace views { + +class NavButtonProvider { + public: + virtual ~NavButtonProvider() {} + + // Redraws all images and updates all size state. |top_area_height| + // is the total available height to render the buttons, and buttons + // may be drawn larger when more height is available. |active| + // indicates if the window the buttons reside in has activation. + virtual void RedrawImages(int top_area_height, + bool maximized, + bool active) = 0; + + // Gets the cached button image corresponding to |type| and |state|. + virtual gfx::ImageSkia GetImage(chrome::FrameButtonDisplayType type, + views::Button::ButtonState state) const = 0; + + // Gets the external margin around each button. The left inset + // represents the leading margin, and the right inset represents the + // trailing margin. + virtual gfx::Insets GetNavButtonMargin( + chrome::FrameButtonDisplayType type) const = 0; + + // Gets the internal spacing (padding + border) of the top area. + // The left inset represents the leading spacing, and the right + // inset represents the trailing spacing. + virtual gfx::Insets GetTopAreaSpacing() const = 0; + + // Gets the spacing to be used to separate buttons. + virtual int GetInterNavButtonSpacing() const = 0; +}; + +} // namespace views + +#endif // UI_VIEWS_NAV_BUTTON_PROVIDER_H_ diff --git a/chromium/ui/webui/PLATFORM_OWNERS b/chromium/ui/webui/PLATFORM_OWNERS index 249ebb89100..a4e5de4c6f9 100644 --- a/chromium/ui/webui/PLATFORM_OWNERS +++ b/chromium/ui/webui/PLATFORM_OWNERS @@ -1,8 +1,8 @@ # Please use more specific OWNERS when possible. bauerb@chromium.org calamity@chromium.org -dbeam@chromium.org dpapad@chromium.org +dschuyler@chromium.org michaelpg@chromium.org pam@chromium.org tommycli@chromium.org diff --git a/chromium/ui/webui/resources/PRESUBMIT.py b/chromium/ui/webui/resources/PRESUBMIT.py index 2b24710521a..26bc5ec895e 100644 --- a/chromium/ui/webui/resources/PRESUBMIT.py +++ b/chromium/ui/webui/resources/PRESUBMIT.py @@ -28,9 +28,12 @@ def _CheckForTranslations(input_api, output_api): for f in input_api.AffectedFiles(): local_path = f.LocalPath() + # Allow translation in i18n_behavior.js. if local_path.endswith('i18n_behavior.js'): continue - + # Allow translation in the cr_components directory. + if 'cr_components' in local_path: + continue keywords = None if local_path.endswith('.js'): keywords = js_keywords diff --git a/chromium/ui/webui/resources/cr_components/OWNERS b/chromium/ui/webui/resources/cr_components/OWNERS new file mode 100644 index 00000000000..ae9d7fbf6f8 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/OWNERS @@ -0,0 +1,2 @@ +stevenjb@chromium.org +dpapad@chromium.org diff --git a/chromium/ui/webui/resources/cr_components/README.md b/chromium/ui/webui/resources/cr_components/README.md new file mode 100644 index 00000000000..a4ea0138a7d --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/README.md @@ -0,0 +1,12 @@ +This directory contains complex Polymer web components for Web UI. They may be +shared between Settings, login, stand alone dialogs, etc. + +These components are allowed to use I18nBehavior. The Web UI hosting these +components is expected to provide loadTimeData with any necessary strings. +TODO(stevenjb/dschuyler): Add support for i18n{} substitution. + +These components may also use chrome and extension APIs, e.g. chrome.send +(through a browser proxy) or chrome.settingsPrivate. The C++ code hosting the +component is expected to handle these calls. + +For simpler components with no I18n or chrome dependencies, see cr_elements. 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 new file mode 100644 index 00000000000..82be73ab885 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.html @@ -0,0 +1,57 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/paper_checkbox_style_css.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-checkbox/paper-checkbox.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner.html"> +<link rel="import" href="certificate_shared_css.html"> +<link rel="import" href="certificates_browser_proxy.html"> + +<dom-module id="ca-trust-edit-dialog"> + <template> + <style include="certificate-shared paper-button-style paper-checkbox-style"> + paper-checkbox { + display: block; + } + + paper-checkbox, + #description { + margin: 15px 0; + } + </style> + + <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <div slot="title"> + [[i18n('certificateManagerCaTrustEditDialogTitle')]] + </div> + <div slot="body"> + <div>[[explanationText_]]</div> + <div id="description"> + [[i18n('certificateManagerCaTrustEditDialogDescription')]] + </div> + <paper-checkbox id="ssl" checked="[[trustInfo_.ssl]]"> + [[i18n('certificateManagerCaTrustEditDialogSsl')]] + </paper-checkbox> + <paper-checkbox id="email" checked="[[trustInfo_.email]]"> + [[i18n('certificateManagerCaTrustEditDialogEmail')]] + </paper-checkbox> + <paper-checkbox id="objSign" checked="[[trustInfo_.objSign]]"> + [[i18n('certificateManagerCaTrustEditDialogObjSign')]] + </paper-checkbox> + </div> + <div slot="button-container"> + <paper-spinner id="spinner"></paper-spinner> + <paper-button class="cancel-button" on-tap="onCancelTap_"> + [[i18n('cancel')]] + </paper-button> + <paper-button id="ok" class="action-button" on-tap="onOkTap_"> + [[i18n('ok')]] + </paper-button> + </div> + </dialog> + </template> + <script src="ca_trust_edit_dialog.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.js new file mode 100644 index 00000000000..c33d5558efa --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/ca_trust_edit_dialog.js @@ -0,0 +1,80 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview 'ca-trust-edit-dialog' allows the user to: + * - specify the trust level of a certificate authority that is being + * imported. + * - edit the trust level of an already existing certificate authority. + */ +Polymer({ + is: 'ca-trust-edit-dialog', + + behaviors: [I18nBehavior], + + properties: { + /** @type {!CertificateSubnode|!NewCertificateSubNode} */ + model: Object, + + /** @private {?CaTrustInfo} */ + trustInfo_: Object, + + /** @private {string} */ + explanationText_: String, + }, + + /** @private {?certificate_manager.CertificatesBrowserProxy} */ + browserProxy_: null, + + /** @override */ + ready: function() { + this.browserProxy_ = + certificate_manager.CertificatesBrowserProxyImpl.getInstance(); + }, + + /** @override */ + attached: function() { + this.explanationText_ = loadTimeData.getStringF( + 'certificateManagerCaTrustEditDialogExplanation', this.model.name); + + // A non existing |model.id| indicates that a new certificate is being + // imported, otherwise an existing certificate is being edited. + if (this.model.id) { + this.browserProxy_.getCaCertificateTrust(this.model.id) + .then(trustInfo => { + this.trustInfo_ = trustInfo; + this.$.dialog.showModal(); + }); + } else { + /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); + } + }, + + /** @private */ + onCancelTap_: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + + /** @private */ + onOkTap_: function() { + this.$.spinner.active = true; + + var whenDone = this.model.id ? + this.browserProxy_.editCaCertificateTrust( + this.model.id, this.$.ssl.checked, this.$.email.checked, + this.$.objSign.checked) : + this.browserProxy_.importCaCertificateTrustSelected( + this.$.ssl.checked, this.$.email.checked, this.$.objSign.checked); + + whenDone.then( + () => { + this.$.spinner.active = false; + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + error => { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + this.fire('certificates-error', {error: error, anchor: null}); + }); + }, +}); 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 new file mode 100644 index 00000000000..fbf253c3a5d --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.html @@ -0,0 +1,30 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="certificate_shared_css.html"> +<link rel="import" href="certificates_browser_proxy.html"> + +<dom-module id="certificate-delete-confirmation-dialog"> + <template> + <style include="certificate-shared"></style> + <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <div slot="title"> + [[getTitleText_(model, certificateType)]] + </div> + <div slot="body"> + <div>[[getDescriptionText_(model, certificateType)]]</div> + </div> + <div slot="button-container"> + <paper-button class="cancel-button" on-tap="onCancelTap_"> + [[i18n('cancel')]] + </paper-button> + <paper-button id="ok" class="action-button" on-tap="onOkTap_"> + [[i18n('ok')]] + </paper-button> + </div> + </dialog> + </template> + <script src="certificate_delete_confirmation_dialog.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js new file mode 100644 index 00000000000..f914b0865ad --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_delete_confirmation_dialog.js @@ -0,0 +1,97 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview A confirmation dialog allowing the user to delete various types + * of certificates. + */ +Polymer({ + is: 'certificate-delete-confirmation-dialog', + + behaviors: [I18nBehavior], + + properties: { + /** @type {!CertificateSubnode} */ + model: Object, + + /** @type {!CertificateType} */ + certificateType: String, + }, + + /** @private {?certificate_manager.CertificatesBrowserProxy} */ + browserProxy_: null, + + /** @override */ + ready: function() { + this.browserProxy_ = + certificate_manager.CertificatesBrowserProxyImpl.getInstance(); + }, + + /** @override */ + attached: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); + }, + + /** + * @private + * @return {string} + */ + getTitleText_: function() { + /** + * @param {string} localizedMessageId + * @return {string} + */ + var getString = localizedMessageId => + loadTimeData.getStringF(localizedMessageId, this.model.name); + + switch (this.certificateType) { + case CertificateType.PERSONAL: + return getString('certificateManagerDeleteUserTitle'); + case CertificateType.SERVER: + return getString('certificateManagerDeleteServerTitle'); + case CertificateType.CA: + return getString('certificateManagerDeleteCaTitle'); + case CertificateType.OTHER: + return getString('certificateManagerDeleteOtherTitle'); + } + assertNotReached(); + }, + + /** + * @private + * @return {string} + */ + getDescriptionText_: function() { + var getString = loadTimeData.getString.bind(loadTimeData); + switch (this.certificateType) { + case CertificateType.PERSONAL: + return getString('certificateManagerDeleteUserDescription'); + case CertificateType.SERVER: + return getString('certificateManagerDeleteServerDescription'); + case CertificateType.CA: + return getString('certificateManagerDeleteCaDescription'); + case CertificateType.OTHER: + return ''; + } + assertNotReached(); + }, + + /** @private */ + onCancelTap_: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + + /** @private */ + onOkTap_: function() { + this.browserProxy_.deleteCertificate(this.model.id) + .then( + () => { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + error => { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + this.fire('certificates-error', {error: error, anchor: null}); + }); + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.html new file mode 100644 index 00000000000..8dcb44091d0 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.html @@ -0,0 +1,39 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="certificate_shared_css.html"> +<link rel="import" href="certificate_subentry.html"> +<link rel="import" href="certificates_browser_proxy.html"> + +<dom-module id="certificate-entry"> + <template> + <style include="certificate-shared iron-flex"> + .expand-box { + align-items: center; + border-top: var(--cr-separator-line); + display: flex; + min-height: 48px; + padding: 0 20px; + } + </style> + <div class="expand-box"> + <div class="flex">[[model.id]]</div> + <cr-expand-button expanded="{{expanded_}}" + alt="[[i18n('certificateManagerExpandA11yLabel')]]"> + </cr-expand-button> + </div> + <template is="dom-if" if="[[expanded_]]"> + <div class="list-frame"> + <template is="dom-repeat" items="[[model.subnodes]]"> + <certificate-subentry model="[[item]]" + certificate-type="[[certificateType]]" + is-last$="[[isLast_(index, model)]]"> + </certificate-subentry> + </template> + </div> + </template> + </template> + <script src="certificate_entry.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.js new file mode 100644 index 00000000000..bcd900e48c1 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_entry.js @@ -0,0 +1,29 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview An element that represents an SSL certificate entry. + */ +Polymer({ + is: 'certificate-entry', + + behaviors: [I18nBehavior], + + properties: { + /** @type {!Certificate} */ + model: Object, + + /** @type {!CertificateType} */ + certificateType: String, + }, + + /** + * @param {number} index + * @return {boolean} Whether the given index corresponds to the last sub-node. + * @private + */ + isLast_: function(index) { + return index == this.model.subnodes.length - 1; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.html new file mode 100644 index 00000000000..55396b5f371 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.html @@ -0,0 +1,41 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="certificate_entry.html"> +<link rel="import" href="certificate_manager_types.html"> +<link rel="import" href="certificate_shared_css.html"> +<link rel="import" href="certificates_browser_proxy.html"> + +<dom-module id="certificate-list"> + <template> + <style include="certificate-shared iron-flex"> + .button-box { + align-items: center; + display: flex; + margin-bottom: 24px; + min-height: 48px; + padding: 0 20px; + } + </style> + <div class="button-box"> + <span class="flex"> + [[getDescription_(certificateType, certificates)]]</span> + <paper-button id="import" on-tap="onImportTap_" + hidden="[[!canImport_(certificateType)]]"> + [[i18n('certificateManagerImport')]]</paper-button> +<if expr="chromeos"> + <paper-button id="importAndBind" on-tap="onImportAndBindTap_" + hidden="[[!canImportAndBind_(certificateType, isGuest_)]]"> + [[i18n('certificateManagerImportAndBind')]]</paper-button> +</if> + </div> + <template is="dom-repeat" items="[[certificates]]"> + <certificate-entry model="[[item]]" + certificate-type="[[certificateType]]"> + </certificate-entry> + </template> + </template> + <script src="certificate_list.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.js new file mode 100644 index 00000000000..d24361fdeee --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_list.js @@ -0,0 +1,158 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview 'certificate-list' is an element that displays a list of + * certificates. + */ +Polymer({ + is: 'certificate-list', + + properties: { + /** @type {!Array<!Certificate>} */ + certificates: { + type: Array, + value: function() { + return []; + }, + }, + + /** @type {!CertificateType} */ + certificateType: String, + + // 'if expr="chromeos"' here is breaking vulcanize. TODO(stevenjb/dpapad): + // Restore after migrating to polymer-bundler, crbug.com/731881. + /** @private */ + isGuest_: { + type: Boolean, + value: function() { + return loadTimeData.valueExists('isGuest') && + loadTimeData.getBoolean('isGuest'); + }, + }, + }, + + behaviors: [I18nBehavior], + + /** + * @return {string} + * @private + */ + getDescription_: function() { + if (this.certificates.length == 0) + return this.i18n('certificateManagerNoCertificates'); + + switch (this.certificateType) { + case CertificateType.PERSONAL: + return this.i18n('certificateManagerYourCertificatesDescription'); + case CertificateType.SERVER: + return this.i18n('certificateManagerServersDescription'); + case CertificateType.CA: + return this.i18n('certificateManagerAuthoritiesDescription'); + case CertificateType.OTHER: + return this.i18n('certificateManagerOthersDescription'); + } + + assertNotReached(); + }, + + /** + * @return {boolean} + * @private + */ + canImport_: function() { + return this.certificateType != CertificateType.OTHER; + }, + + // <if expr="chromeos"> + /** + * @return {boolean} + * @private + */ + canImportAndBind_: function() { + return !this.isGuest_ && this.certificateType == CertificateType.PERSONAL; + }, + // </if> + + /** + * Handles a rejected Promise returned from |browserProxy_|. + * @param {!HTMLElement} anchor + * @param {*} error Expects {!CertificatesError|!CertificatesImportError}. + * @private + */ + onRejected_: function(anchor, error) { + if (error === null) { + // Nothing to do here. Null indicates that the user clicked "cancel" on + // a native file chooser dialog. + return; + } + + // Otherwise propagate the error to the parents, such that a dialog + // displaying the error will be shown. + this.fire('certificates-error', {error: error, anchor: anchor}); + }, + + + /** + * @param {?NewCertificateSubNode} subnode + * @param {!HTMLElement} anchor + * @private + */ + dispatchImportActionEvent_: function(subnode, anchor) { + this.fire( + CertificateActionEvent, + /** @type {!CertificateActionEventDetail} */ ({ + action: CertificateAction.IMPORT, + subnode: subnode, + certificateType: this.certificateType, + anchor: anchor, + })); + }, + + /** + * @param {!Event} e + * @private + */ + onImportTap_: function(e) { + this.handleImport_( + false, /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget)); + }, + + // <if expr="chromeos"> + /** + * @private + * @param {!Event} e + */ + onImportAndBindTap_: function(e) { + this.handleImport_( + true, /** @type {!HTMLElement} */ (Polymer.dom(e).localTarget)); + }, + // </if> + + /** + * @param {boolean} useHardwareBacked + * @param {!HTMLElement} anchor + * @private + */ + handleImport_: function(useHardwareBacked, anchor) { + var browserProxy = + certificate_manager.CertificatesBrowserProxyImpl.getInstance(); + if (this.certificateType == CertificateType.PERSONAL) { + browserProxy.importPersonalCertificate(useHardwareBacked) + .then(showPasswordPrompt => { + if (showPasswordPrompt) + this.dispatchImportActionEvent_(null, anchor); + }, this.onRejected_.bind(this, anchor)); + } else if (this.certificateType == CertificateType.CA) { + browserProxy.importCaCertificate().then(certificateName => { + this.dispatchImportActionEvent_({name: certificateName}, anchor); + }, this.onRejected_.bind(this, anchor)); + } else if (this.certificateType == CertificateType.SERVER) { + browserProxy.importServerCertificate().catch( + this.onRejected_.bind(this, anchor)); + } else { + assertNotReached(); + } + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html new file mode 100644 index 00000000000..9c9ebcb86c3 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html @@ -0,0 +1,106 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html"> +<link rel="import" href="ca_trust_edit_dialog.html"> +<link rel="import" href="certificate_delete_confirmation_dialog.html"> +<link rel="import" href="certificate_list.html"> +<link rel="import" href="certificate_manager_types.html"> +<link rel="import" href="certificate_password_decryption_dialog.html"> +<link rel="import" href="certificate_password_encryption_dialog.html"> +<link rel="import" href="certificates_browser_proxy.html"> +<link rel="import" href="certificates_error_dialog.html"> + +<dom-module id="certificate-manager"> + <template> + <style> + :host { + --paper-tabs-selection-bar-color: var(--paper-blue-500); + } + + paper-tabs { + font-size: inherit; + height: 40px; + margin-bottom: 24px; + } + + paper-tab { + --paper-tab-content: { + color: var(--paper-grey-800); + }; + --paper-tab-content-unselected: { + color: var(--paper-grey-600); + }; + text-transform: uppercase; + } + </style> + + <template is="dom-if" if="[[showCaTrustEditDialog_]]" restamp> + <ca-trust-edit-dialog model="[[dialogModel_]]"> + </ca-trust-edit-dialog> + </template> + <template is="dom-if" if="[[showDeleteConfirmationDialog_]]" restamp> + <certificate-delete-confirmation-dialog + model="[[dialogModel_]]" + certificate-type="[[dialogModelCertificateType_]]"> + </certificate-delete-confirmation-dialog> + </template> + <template is="dom-if" if="[[showPasswordEncryptionDialog_]]" restamp> + <certificate-password-encryption-dialog + model="[[dialogModel_]]"> + </certificate-password-encryption-dialog> + </template> + <template is="dom-if" if="[[showPasswordDecryptionDialog_]]" restamp> + <certificate-password-decryption-dialog> + </certificate-password-decryption-dialog> + </template> + <template is="dom-if" if="[[showErrorDialog_]]" restamp> + <certificates-error-dialog model="[[errorDialogModel_]]"> + </certificates-error-dialog> + </template> + + <paper-tabs noink selected="{{selected}}"> + <paper-tab>[[i18n('certificateManagerYourCertificates')]]</paper-tab> + <paper-tab>[[i18n('certificateManagerServers')]]</paper-tab> + <paper-tab>[[i18n('certificateManagerAuthorities')]]</paper-tab> + <paper-tab>[[i18n('certificateManagerOthers')]]</paper-tab> + </paper-tabs> + <iron-pages selected="[[selected]]"> + <div> + <certificate-list id="personalCerts" + certificates="[[personalCerts]]" + certificate-type="[[certificateTypeEnum_.PERSONAL]]"> + </certificate-list> + </div> + <div> + <template is="dom-if" if="[[isTabSelected_(selected, 1)]]"> + <certificate-list id="serverCerts" + certificates="[[serverCerts]]" + certificate-type="[[certificateTypeEnum_.SERVER]]"> + </certificate-list> + </template> + </div> + <div> + <template is="dom-if" if="[[isTabSelected_(selected, 2)]]"> + <certificate-list id="caCerts" + certificates="[[caCerts]]" + certificate-type="[[certificateTypeEnum_.CA]]"> + </certificate-list> + </template> + </div> + <div> + <template is="dom-if" if="[[isTabSelected_(selected, 3)]]"> + <certificate-list id="otherCerts" + certificates="[[otherCerts]]" + certificate-type="[[certificateTypeEnum_.OTHER]]"> + </certificate-list> + </template> + </div> + </iron-pages> + </template> + <script src="certificate_manager.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js new file mode 100644 index 00000000000..0b1049d56df --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js @@ -0,0 +1,189 @@ +// 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. + +/** + * @fileoverview The 'certificate-manager' component manages SSL certificates. + */ +Polymer({ + is: 'certificate-manager', + + behaviors: [I18nBehavior, WebUIListenerBehavior], + + properties: { + /** @type {number} */ + selected: { + type: Number, + value: 0, + }, + + /** @type {!Array<!Certificate>} */ + personalCerts: { + type: Array, + value: function() { + return []; + }, + }, + + /** @type {!Array<!Certificate>} */ + serverCerts: { + type: Array, + value: function() { + return []; + }, + }, + + /** @type {!Array<!Certificate>} */ + caCerts: { + type: Array, + value: function() { + return []; + }, + }, + + /** @type {!Array<!Certificate>} */ + otherCerts: { + type: Array, + value: function() { + return []; + }, + }, + + /** @private */ + certificateTypeEnum_: { + type: Object, + value: CertificateType, + readOnly: true, + }, + + /** @private */ + showCaTrustEditDialog_: Boolean, + + /** @private */ + showDeleteConfirmationDialog_: Boolean, + + /** @private */ + showPasswordEncryptionDialog_: Boolean, + + /** @private */ + showPasswordDecryptionDialog_: Boolean, + + /** @private */ + showErrorDialog_: Boolean, + + /** + * The model to be passed to dialogs that refer to a given certificate. + * @private {?CertificateSubnode} + */ + dialogModel_: Object, + + /** + * The certificate type to be passed to dialogs that refer to a given + * certificate. + * @private {?CertificateType} + */ + dialogModelCertificateType_: String, + + /** + * The model to be passed to the error dialog. + * @private {null|!CertificatesError|!CertificatesImportError} + */ + errorDialogModel_: Object, + + /** + * The element to return focus to, when the currently shown dialog is + * closed. + * @private {?HTMLElement} + */ + activeDialogAnchor_: Object, + }, + + /** @override */ + attached: function() { + this.addWebUIListener('certificates-changed', this.set.bind(this)); + certificate_manager.CertificatesBrowserProxyImpl.getInstance() + .refreshCertificates(); + }, + + /** + * @param {number} selectedIndex + * @param {number} tabIndex + * @return {boolean} Whether to show tab at |tabIndex|. + * @private + */ + isTabSelected_: function(selectedIndex, tabIndex) { + return selectedIndex == tabIndex; + }, + + /** @override */ + ready: function() { + this.addEventListener(CertificateActionEvent, event => { + this.dialogModel_ = event.detail.subnode; + this.dialogModelCertificateType_ = event.detail.certificateType; + + if (event.detail.action == CertificateAction.IMPORT) { + if (event.detail.certificateType == CertificateType.PERSONAL) { + this.openDialog_( + 'certificate-password-decryption-dialog', + 'showPasswordDecryptionDialog_', event.detail.anchor); + } else if (event.detail.certificateType == CertificateType.CA) { + this.openDialog_( + 'ca-trust-edit-dialog', 'showCaTrustEditDialog_', + event.detail.anchor); + } + } else { + if (event.detail.action == CertificateAction.EDIT) { + this.openDialog_( + 'ca-trust-edit-dialog', 'showCaTrustEditDialog_', + event.detail.anchor); + } else if (event.detail.action == CertificateAction.DELETE) { + this.openDialog_( + 'certificate-delete-confirmation-dialog', + 'showDeleteConfirmationDialog_', event.detail.anchor); + } else if (event.detail.action == CertificateAction.EXPORT_PERSONAL) { + this.openDialog_( + 'certificate-password-encryption-dialog', + 'showPasswordEncryptionDialog_', event.detail.anchor); + } + } + + event.stopPropagation(); + }); + + this.addEventListener('certificates-error', event => { + var detail = /** @type {!CertificatesErrorEventDetail} */ (event.detail); + this.errorDialogModel_ = detail.error; + this.openDialog_( + 'certificates-error-dialog', 'showErrorDialog_', detail.anchor); + event.stopPropagation(); + }); + }, + + /** + * Opens a dialog and registers a listener for removing the dialog from the + * DOM once is closed. The listener is destroyed when the dialog is removed + * (because of 'restamp'). + * + * @param {string} dialogTagName The tag name of the dialog to be shown. + * @param {string} domIfBooleanName The name of the boolean variable + * corresponding to the dialog. + * @param {?HTMLElement} anchor The element to focus when the dialog is + * closed. If null, the previous anchor element should be reused. This + * happens when a 'certificates-error-dialog' is opened, which when closed + * should focus the anchor of the previous dialog (the one that generated + * the error). + * @private + */ + openDialog_: function(dialogTagName, domIfBooleanName, anchor) { + if (anchor) + this.activeDialogAnchor_ = anchor; + this.set(domIfBooleanName, true); + this.async(() => { + var dialog = this.$$(dialogTagName); + dialog.addEventListener('close', () => { + this.set(domIfBooleanName, false); + cr.ui.focusWithoutInk(assert(this.activeDialogAnchor_)); + }); + }); + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.html new file mode 100644 index 00000000000..271b2666939 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.html @@ -0,0 +1 @@ +<script src="certificate_manager_types.js"></script> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.js new file mode 100644 index 00000000000..76d448770cf --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_manager_types.js @@ -0,0 +1,44 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Closure compiler typedefs. + */ + +/** + * The payload of the 'certificate-action' event. + * @typedef {{ + * action: !CertificateAction, + * subnode: (null|CertificateSubnode|NewCertificateSubNode), + * certificateType: !CertificateType, + * anchor: !HTMLElement + * }} + */ +var CertificateActionEventDetail; + +/** + * The payload of the 'certificates-error' event. + * @typedef {{ + * error: (null|CertificatesError|CertificatesImportError), + * anchor: ?HTMLElement + * }} + */ +var CertificatesErrorEventDetail; + +/** + * Enumeration of actions that require a popup menu to be shown to the user. + * @enum {number} + */ +var CertificateAction = { + DELETE: 0, + EDIT: 1, + EXPORT_PERSONAL: 2, + IMPORT: 3, +}; + +/** + * The name of the event fired when a certificate action is selected from the + * dropdown menu. CertificateActionEventDetail is passed as the event detail. + */ +var CertificateActionEvent = 'certificate-action'; 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 new file mode 100644 index 00000000000..850da130e7f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.html @@ -0,0 +1,34 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> +<link rel="import" href="certificate_shared_css.html"> +<link rel="import" href="certificates_browser_proxy.html"> + +<dom-module id="certificate-password-decryption-dialog"> + <template> + <style include="certificate-shared"></style> + <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <div slot="title"> + [[i18n('certificateManagerDecryptPasswordTitle')]] + </div> + <div slot="body"> + <paper-input type="password" id="password" + label="[[i18n('certificateManagerPassword')]]" + value="{{password_}}"> + </paper-input> + </div> + <div slot="button-container"> + <paper-button class="cancel-button" on-tap="onCancelTap_"> + [[i18n('cancel')]] + </paper-button> + <paper-button id="ok" class="action-button" on-tap="onOkTap_"> + [[i18n('ok')]] + </paper-button> + </div> + </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_decryption_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js new file mode 100644 index 00000000000..0e502404c1b --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_decryption_dialog.js @@ -0,0 +1,53 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview A dialog prompting the user for a decryption password such that + * a previously exported personal certificate can be imported. + */ +Polymer({ + is: 'certificate-password-decryption-dialog', + + behaviors: [I18nBehavior], + + properties: { + /** @private */ + password_: { + type: String, + value: '', + }, + }, + + /** @private {?certificate_manager.CertificatesBrowserProxy} */ + browserProxy_: null, + + /** @override */ + ready: function() { + this.browserProxy_ = + certificate_manager.CertificatesBrowserProxyImpl.getInstance(); + }, + + /** @override */ + attached: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); + }, + + /** @private */ + onCancelTap_: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + + /** @private */ + onOkTap_: function() { + this.browserProxy_.importPersonalCertificatePasswordSelected(this.password_) + .then( + () => { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + error => { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + this.fire('certificates-error', {error: error, anchor: null}); + }); + }, +}); 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 new file mode 100644 index 00000000000..f17b2825fef --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.html @@ -0,0 +1,44 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> +<link rel="import" href="certificate_shared_css.html"> +<link rel="import" href="certificates_browser_proxy.html"> + +<dom-module id="certificate-password-encryption-dialog"> + <template> + <style include="certificate-shared"> + .password-buttons { + margin-bottom: 20px; + } + </style> + <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <div slot="title"> + [[i18n('certificateManagerEncryptPasswordTitle')]] + </div> + <div slot="body"> + <div>[[i18n('certificateManagerEncryptPasswordDescription')]]</div> + <div class="password-buttons"> + <paper-input type="password" value="{{password_}}" id="password" + label="[[i18n('certificateManagerPassword')]]" + on-input="validate_"></paper-input> + <paper-input type="password" + value="{{confirmPassword_}}" id="confirmPassword" + label="[[i18n('certificateManagerConfirmPassword')]]" + on-input="validate_"></paper-input> + </div> + </div> + <div slot="button-container"> + <paper-button class="cancel-button" on-tap="onCancelTap_"> + [[i18n('cancel')]] + </paper-button> + <paper-button id="ok" class="action-button" on-tap="onOkTap_" disabled> + [[i18n('ok')]] + </paper-button> + </div> + </dialog> + </template> + <script src="certificate_password_encryption_dialog.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js new file mode 100644 index 00000000000..b605ee7c22a --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_password_encryption_dialog.js @@ -0,0 +1,69 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview A dialog prompting the user to encrypt a personal certificate + * before it is exported to disk. + */ +Polymer({ + is: 'certificate-password-encryption-dialog', + + behaviors: [I18nBehavior], + + properties: { + /** @type {!CertificateSubnode} */ + model: Object, + + /** @private */ + password_: { + type: String, + value: '', + }, + + /** @private */ + confirmPassword_: { + type: String, + value: '', + }, + }, + + /** @private {?certificate_manager.CertificatesBrowserProxy} */ + browserProxy_: null, + + /** @override */ + ready: function() { + this.browserProxy_ = + certificate_manager.CertificatesBrowserProxyImpl.getInstance(); + }, + + /** @override */ + attached: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); + }, + + /** @private */ + onCancelTap_: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + + /** @private */ + onOkTap_: function() { + this.browserProxy_.exportPersonalCertificatePasswordSelected(this.password_) + .then( + () => { + this.$.dialog.close(); + }, + error => { + this.$.dialog.close(); + this.fire('certificates-error', {error: error, anchor: null}); + }); + }, + + /** @private */ + validate_: function() { + var isValid = + this.password_ != '' && this.password_ == this.confirmPassword_; + this.$.ok.disabled = !isValid; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_shared_css.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_shared_css.html new file mode 100644 index 00000000000..88fbdb2e211 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_shared_css.html @@ -0,0 +1,35 @@ +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> + +<!-- Common styles for certificate elements. --> + +<dom-module id="certificate-shared"> + <template> + <style include="cr-shared-style"> + /* .list-frame and .list-item match the styling in settings_shared_css. */ + .list-frame { + -webkit-padding-end: 20px; + -webkit-padding-start: 60px; + align-items: center; + display: block; + } + + .list-item { + align-items: center; + display: flex; + min-height: 48px; + } + + .list-item.underbar { + border-bottom: var(--cr-separator-line); + } + + .list-item.selected { + font-weight: 500; + } + + .list-item > .start { + flex: 1; + } + </style> + </template> +</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 new file mode 100644 index 00000000000..691625fb095 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.html @@ -0,0 +1,63 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html"> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> +<link rel="import" href="certificate_manager_types.html"> +<link rel="import" href="certificate_shared_css.html"> +<link rel="import" href="certificates_browser_proxy.html"> + +<dom-module id="certificate-subentry"> + <template> + <style include="certificate-shared cr-icons"> + .name { + flex: auto; + } + + .untrusted { + -webkit-margin-end: 16px; + color: var(--paper-red-700); + font-weight: 500; + text-transform: uppercase; + } + + :host([is-last]) .list-item { + border-bottom: none; + } + </style> + <div class="list-item underbar"> + <div class="untrusted" hidden$="[[!model.untrusted]]"> + [[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 class="dropdown-item" id="view" + on-tap="onViewTap_"> + [[i18n('certificateManagerView')]] + </button> + <button class="dropdown-item" id="edit" + hidden$="[[!canEdit_(certificateType, model)]]" + on-tap="onEditTap_"> + [[i18n('edit')]] + </button> + <button class="dropdown-item" id="export" + hidden$="[[!canExport_(certificateType, model)]]" + on-tap="onExportTap_"> + [[i18n('certificateManagerExport')]] + </button> + <button class="dropdown-item" id="delete" + hidden$="[[!canDelete_(model)]]" + on-tap="onDeleteTap_"> + [[i18n('certificateManagerDelete')]] + </button> + </dialog> + </template> + <div> + </template> + <script src="certificate_subentry.js"></script> +</dom-module> 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 new file mode 100644 index 00000000000..f2bc22e51fc --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificate_subentry.js @@ -0,0 +1,150 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview certificate-subentry represents an SSL certificate sub-entry. + */ + +Polymer({ + is: 'certificate-subentry', + + behaviors: [I18nBehavior], + + properties: { + /** @type {!CertificateSubnode} */ + model: Object, + + /** @type {!CertificateType} */ + certificateType: String, + }, + + /** @private {certificate_manager.CertificatesBrowserProxy} */ + browserProxy_: null, + + /** @override */ + created: function() { + this.browserProxy_ = + certificate_manager.CertificatesBrowserProxyImpl.getInstance(); + }, + + /** + * Dispatches an event indicating which certificate action was tapped. It is + * used by the parent of this element to display a modal dialog accordingly. + * @param {!CertificateAction} action + * @private + */ + dispatchCertificateActionEvent_: function(action) { + this.fire( + CertificateActionEvent, + /** @type {!CertificateActionEventDetail} */ ({ + action: action, + subnode: this.model, + certificateType: this.certificateType, + anchor: this.$.dots, + })); + }, + + /** + * Handles the case where a call to the browser resulted in a rejected + * promise. + * @param {*} error Expects {?CertificatesError}. + * @private + */ + onRejected_: function(error) { + if (error === null) { + // Nothing to do here. Null indicates that the user clicked "cancel" on + // the native file chooser dialog. + return; + } + + // Otherwise propagate the error to the parents, such that a dialog + // displaying the error will be shown. + this.fire('certificates-error', {error: error, anchor: this.$.dots}); + }, + + /** + * @param {!Event} event + * @private + */ + onViewTap_: function(event) { + this.closePopupMenu_(); + this.browserProxy_.viewCertificate(this.model.id); + }, + + /** + * @param {!Event} event + * @private + */ + onEditTap_: function(event) { + this.closePopupMenu_(); + this.dispatchCertificateActionEvent_(CertificateAction.EDIT); + }, + + /** + * @param {!Event} event + * @private + */ + onDeleteTap_: function(event) { + this.closePopupMenu_(); + this.dispatchCertificateActionEvent_(CertificateAction.DELETE); + }, + + /** + * @param {!Event} event + * @private + */ + onExportTap_: function(event) { + this.closePopupMenu_(); + if (this.certificateType == CertificateType.PERSONAL) { + this.browserProxy_.exportPersonalCertificate(this.model.id).then(() => { + this.dispatchCertificateActionEvent_(CertificateAction.EXPORT_PERSONAL); + }, this.onRejected_.bind(this)); + } else { + this.browserProxy_.exportCertificate(this.model.id); + } + }, + + /** + * @param {!CertificateType} certificateType + * @param {!CertificateSubnode} model + * @return {boolean} Whether the certificate can be edited. + * @private + */ + canEdit_: function(certificateType, model) { + return certificateType == CertificateType.CA && !model.policy; + }, + + /** + * @param {!CertificateType} certificateType + * @param {!CertificateSubnode} model + * @return {boolean} Whether the certificate can be exported. + * @private + */ + canExport_: function(certificateType, model) { + if (certificateType == CertificateType.PERSONAL) { + return model.extractable; + } + return true; + }, + + /** + * @param {!CertificateSubnode} model + * @return {boolean} Whether the certificate can be deleted. + * @private + */ + canDelete_: function(model) { + return !model.readonly && !model.policy; + }, + + /** @private */ + closePopupMenu_: function() { + this.$$('dialog[is=cr-action-menu]').close(); + }, + + /** @private */ + onDotsTap_: function() { + var actionMenu = /** @type {!CrActionMenuElement} */ (this.$.menu.get()); + actionMenu.showAt(this.$.dots); + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.html b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.html new file mode 100644 index 00000000000..0dc7c572ff1 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.html @@ -0,0 +1 @@ +<script src="certificates_browser_proxy.js"></script> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.js new file mode 100644 index 00000000000..bbf1d73f17a --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_browser_proxy.js @@ -0,0 +1,271 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview A helper object used from the "Manage certificates" section + * to interact with the browser. + */ + +/** + * @typedef {{ + * extractable: boolean, + * id: string, + * name: string, + * policy: boolean, + * readonly: boolean, + * untrusted: boolean, + * }} + * @see chrome/browser/ui/webui/settings/certificates_handler.cc + */ +var CertificateSubnode; + +/** + * A data structure describing a certificate that is currently being imported, + * therefore it has no ID yet, but it has a name. Used within JS only. + * @typedef {{ + * name: string, + * }} + */ +var NewCertificateSubNode; + +/** + * @typedef {{ + * id: string, + * name: string, + * subnodes: !Array<!CertificateSubnode> + * }} + * @see chrome/browser/ui/webui/settings/certificates_handler.cc + */ +var Certificate; + +/** + * @typedef {{ + * ssl: boolean, + * email: boolean, + * objSign: boolean + * }} + */ +var CaTrustInfo; + +/** + * Generic error returned from C++ via a Promise reject callback. + * @typedef {{ + * title: string, + * description: string + * }} + * @see chrome/browser/ui/webui/settings/certificates_handler.cc + */ +var CertificatesError; + +/** + * Enumeration of all possible certificate types. + * @enum {string} + */ +var CertificateType = { + CA: 'ca', + OTHER: 'other', + PERSONAL: 'personal', + SERVER: 'server', +}; + + +/** + * Error returned from C++ via a Promise reject callback, when some certificates + * fail to be imported. + * @typedef {{ + * title: string, + * description: string, + * certificateErrors: !Array<{name: string, error: string}> + * }} + * @see chrome/browser/ui/webui/settings/certificates_handler.cc + */ +var CertificatesImportError; + +cr.define('certificate_manager', function() { + /** @interface */ + class CertificatesBrowserProxy { + /** + * Triggers 5 events in the following order + * 1x 'certificates-model-ready' event. + * 4x 'certificates-changed' event, one for each certificate category. + */ + refreshCertificates() {} + + /** @param {string} id */ + viewCertificate(id) {} + + /** @param {string} id */ + exportCertificate(id) {} + + /** + * @param {string} id + * @return {!Promise} A promise resolved when the certificate has been + * deleted successfully or rejected with a CertificatesError. + */ + deleteCertificate(id) {} + + /** + * @param {string} id + * @return {!Promise<!CaTrustInfo>} + */ + getCaCertificateTrust(id) {} + + /** + * @param {string} id + * @param {boolean} ssl + * @param {boolean} email + * @param {boolean} objSign + * @return {!Promise} + */ + editCaCertificateTrust(id, ssl, email, objSign) {} + + cancelImportExportCertificate() {} + + /** + * @param {string} id + * @return {!Promise} A promise firing once the user has selected + * the export location. A prompt should be shown to asking for a + * password to use for encrypting the file. The password should be + * passed back via a call to + * exportPersonalCertificatePasswordSelected(). + */ + exportPersonalCertificate(id) {} + + /** + * @param {string} password + * @return {!Promise} + */ + exportPersonalCertificatePasswordSelected(password) {} + + /** + * @param {boolean} useHardwareBacked + * @return {!Promise<boolean>} A promise firing once the user has selected + * the file to be imported. If true a password prompt should be shown to + * the user, and the password should be passed back via a call to + * importPersonalCertificatePasswordSelected(). + */ + importPersonalCertificate(useHardwareBacked) {} + + /** + * @param {string} password + * @return {!Promise} + */ + importPersonalCertificatePasswordSelected(password) {} + + /** + * @return {!Promise} A promise firing once the user has selected + * the file to be imported, or failing with CertificatesError. + * Upon success, a prompt should be shown to the user to specify the + * trust levels, and that information should be passed back via a call + * to importCaCertificateTrustSelected(). + */ + importCaCertificate() {} + + /** + * @param {boolean} ssl + * @param {boolean} email + * @param {boolean} objSign + * @return {!Promise} A promise firing once the trust level for the imported + * certificate has been successfully set. The promise is rejected if an + * error occurred with either a CertificatesError or + * CertificatesImportError. + */ + importCaCertificateTrustSelected(ssl, email, objSign) {} + + /** + * @return {!Promise} A promise firing once the certificate has been + * imported. The promise is rejected if an error occurred, with either + * a CertificatesError or CertificatesImportError. + */ + importServerCertificate() {} + } + + /** + * @implements {certificate_manager.CertificatesBrowserProxy} + */ + class CertificatesBrowserProxyImpl { + /** @override */ + refreshCertificates() { + chrome.send('refreshCertificates'); + } + + /** @override */ + viewCertificate(id) { + chrome.send('viewCertificate', [id]); + } + + /** @override */ + exportCertificate(id) { + chrome.send('exportCertificate', [id]); + } + + /** @override */ + deleteCertificate(id) { + return cr.sendWithPromise('deleteCertificate', id); + } + + /** @override */ + exportPersonalCertificate(id) { + return cr.sendWithPromise('exportPersonalCertificate', id); + } + + /** @override */ + exportPersonalCertificatePasswordSelected(password) { + return cr.sendWithPromise( + 'exportPersonalCertificatePasswordSelected', password); + } + + /** @override */ + importPersonalCertificate(useHardwareBacked) { + return cr.sendWithPromise('importPersonalCertificate', useHardwareBacked); + } + + /** @override */ + importPersonalCertificatePasswordSelected(password) { + return cr.sendWithPromise( + 'importPersonalCertificatePasswordSelected', password); + } + + /** @override */ + getCaCertificateTrust(id) { + return cr.sendWithPromise('getCaCertificateTrust', id); + } + + /** @override */ + editCaCertificateTrust(id, ssl, email, objSign) { + return cr.sendWithPromise( + 'editCaCertificateTrust', id, ssl, email, objSign); + } + + /** @override */ + importCaCertificateTrustSelected(ssl, email, objSign) { + return cr.sendWithPromise( + 'importCaCertificateTrustSelected', ssl, email, objSign); + } + + /** @override */ + cancelImportExportCertificate() { + chrome.send('cancelImportExportCertificate'); + } + + /** @override */ + importCaCertificate() { + return cr.sendWithPromise('importCaCertificate'); + } + + /** @override */ + importServerCertificate() { + return cr.sendWithPromise('importServerCertificate'); + } + } + + // The singleton instance_ is replaced with a test version of this wrapper + // during testing. + cr.addSingletonGetter(CertificatesBrowserProxyImpl); + + return { + CertificatesBrowserProxy: CertificatesBrowserProxy, + CertificatesBrowserProxyImpl: CertificatesBrowserProxyImpl, + }; +}); 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 new file mode 100644 index 00000000000..2c0bb746765 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.html @@ -0,0 +1,29 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="certificate_shared_css.html"> + +<dom-module id="certificates-error-dialog"> + <template> + <style include="certificate-shared"></style> + <dialog is="cr-dialog" id="dialog" close-text="[[i18n('close')]]"> + <div slot="title">[[model.title]]</div> + <div slot="body"> + <div>[[model.description]]</div> + <template is="dom-if" if="[[model.certificateErrors]]"> + <template is="dom-repeat" items="[[model.certificateErrors]]"> + <div>[[getCertificateErrorText_(item)]]</div> + </template> + </template> + </div> + <div slot="button-container"> + <paper-button id="ok" class="action-button" on-tap="onOkTap_"> + [[i18n('ok')]] + </paper-button> + </div> + </dialog> + </template> + <script src="certificates_error_dialog.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.js b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.js new file mode 100644 index 00000000000..2b3cd779653 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/certificates_error_dialog.js @@ -0,0 +1,38 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview A dialog for showing SSL certificate related error messages. + * The user can only close the dialog, there is no other possible interaction. + */ +Polymer({ + is: 'certificates-error-dialog', + + behaviors: [I18nBehavior], + + properties: { + /** @type {!CertificatesError|!CertificatesImportError} */ + model: Object, + }, + + /** @override */ + attached: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).showModal(); + }, + + /** @private */ + onOkTap_: function() { + /** @type {!CrDialogElement} */ (this.$.dialog).close(); + }, + + /** + * @param {{name: string, error: string}} importError + * @return {string} + * @private + */ + getCertificateErrorText_: function(importError) { + return loadTimeData.getStringF( + 'certificateImportErrorFormat', importError.name, importError.error); + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/certificate_manager/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/certificate_manager/compiled_resources2.gyp new file mode 100644 index 00000000000..fef217bee75 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/certificate_manager/compiled_resources2.gyp @@ -0,0 +1,125 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'ca_trust_edit_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_delete_confirmation_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_entry', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_list', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + 'certificate_manager_types', + 'certificate_subentry', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_manager', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:web_ui_listener_behavior', + '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:focus_without_ink', + 'certificate_list', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_manager_types', + 'dependencies': [ + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_password_decryption_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_password_encryption_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificate_subentry', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_action_menu/compiled_resources2.gyp:cr_action_menu', + '<(DEPTH)/ui/webui/resources/cr_elements/cr_lazy_render/compiled_resources2.gyp:cr_lazy_render', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificates_browser_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'certificates_error_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data', + 'certificate_manager_types', + 'certificates_browser_proxy', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html new file mode 100644 index 00000000000..a89ba4d5fde --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.html @@ -0,0 +1,137 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.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-input/paper-input.html"> + +<dom-module id="bluetooth-dialog"> + <template> + <style include="cr-hidden-style iron-flex"> + #pairing { + margin-bottom: 10px; + } + + #pairing paper-input { + text-align: center; + } + + #pinDiv { + margin-top: 10px; + } + + .dialog-message { + margin-bottom: 10px; + } + + div.contents { + height: 250px; + } + + /* .display indicates a displayed pin code or passkey. */ + span.display { + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: 0 0 0 1px #222; + color: #222; + font-size: 123.08%; /* 16px / 13px */ + height: 38px; + line-height: 38px; + margin: 0 5px; + padding: 0 15px; + text-align: center; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + } + + span.display.next { + background: rgb(77, 144, 254); + border: 2px solid rgb(77, 144, 254); + box-shadow: none; + color: #fff; + } + + span.display.untyped { + border: 1px solid #d4d4d4; + box-shadow: 0 0 0 1px #888; + color: #666; + } + + /* .confirm indicates a confirmation passkey. */ + span.confirm { + color: #999; + font-size: 153.85%; /* 20px / 13px */ + font-weight: 600; /* semibold */ + margin: 0 20px; + } + </style> + <!-- TODO(stevenjb/dschuyler): Find a solution to support i18n{} here --> + <dialog is="cr-dialog" id="dialog" on-cancel="onDialogCanceled_" + close-text="[[i18n('close')]]" on-closed="onDialogCanceled_"> + <div slot="title">[[title]]</div> + <div slot="body"> + <div class="contents layout vertical center center-justified"> + <template is="dom-if" if="[[!errorMessage_]]"> + <div id="pairing" class="layout vertical center center-justified"> + <div class="dialog-message"> + [[getMessage_(pairingDevice, pairingEvent_)]] + </div> + <div hidden$="[[!showEnterPincode_(pairingEvent_)]]"> + <paper-input id="pincode" minlength="1" maxlength="16" + type="text" auto-validate value="{{pinOrPass_}}"> + </paper-input> + </div> + <div hidden$="[[!showEnterPasskey_(pairingEvent_)]]"> + <paper-input id="passkey" minlength="6" maxlength="6" + type="text" auto-validate value="{{pinOrPass_}}"> + </paper-input> + </div> + <div id="pinDiv" class="layout horizontal center center-justified" + hidden="[[!showDisplayPassOrPin_(pairingEvent_)]]"> + <template is="dom-repeat" items="[[digits_]]"> + <span class$="[[getPinClass_(index, pairingEvent_)]]"> + [[getPinDigit_(index, pairingEvent_)]] + </span> + </template> + <span class$="[[getPinClass_(-1, pairingEvent_)]]" + hidden="[[showAcceptReject_(pairingEvent_)]]"> + [[i18n('bluetoothEnterKey')]] + </span> + </div> + </div> + </template> + <template is="dom-if" if="[[errorMessage_]]"> + <div class="layout vertical center center-justified"> + <div class="dialog-message">[[errorMessage_]]</div> + </div> + </template> + </div> + </div> + <div slot="button-container"> + <template is="dom-if" if="[[!errorMessage_]]"> + <paper-button hidden$="[[!showAcceptReject_(pairingEvent_)]]" + on-tap="onAcceptTap_">[[i18n('bluetoothAccept')]]</paper-button> + <paper-button hidden$="[[!showAcceptReject_(pairingEvent_)]]" + on-tap="onRejectTap_">[[i18n('bluetoothReject')]]</paper-button> + <paper-button hidden$="[[!showConnect_(pairingEvent_)]]" + disabled="[[!enableConnect_(pairingEvent_, pinOrPass_)]]" + on-tap="onConnectTap_">[[i18n('bluetoothPair')]]</paper-button> + <paper-button + hidden$="[[!showDismiss_(pairingDevice, pairingEvent_)]]" + on-tap="close">[[i18n('ok')]]</paper-button> + <paper-button hidden$="[[showDismiss_(pairingDevice, pairingEvent_)]]" + on-tap="onCancelTap_"> + [[i18n('cancel')]] + </paper-button> + </template> + <template is="dom-if" if="[[errorMessage_]]"> + <paper-button on-tap="close">[[i18n('ok')]]</paper-button> + </template> + </div> + </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 new file mode 100644 index 00000000000..b061349e266 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/bluetooth_dialog.js @@ -0,0 +1,459 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview + * Dialog used for pairing a provided |pairing-device|. Set |show-error| to + * show the error results from a pairing event instead of the pairing UI. + * NOTE: This module depends on I18nBehavior which depends on loadTimeData. + */ + +var PairingEventType = chrome.bluetoothPrivate.PairingEventType; + +Polymer({ + is: 'bluetooth-dialog', + + behaviors: [I18nBehavior], + + properties: { + /** + * Interface for bluetooth calls. Set in bluetooth-page. + * @type {Bluetooth} + * @private + */ + bluetooth: { + type: Object, + value: chrome.bluetooth, + }, + + /** + * Interface for bluetoothPrivate calls. + * @type {BluetoothPrivate} + */ + bluetoothPrivate: { + type: Object, + value: chrome.bluetoothPrivate, + }, + + /** Dialog title */ + title: String, + + /** + * Current Pairing device. + * @type {!chrome.bluetooth.Device|undefined} + */ + pairingDevice: Object, + + /** + * Current Pairing event. + * @private {?chrome.bluetoothPrivate.PairingEvent} + */ + pairingEvent_: { + type: Object, + value: null, + }, + + /** + * May be set by the host to show a pairing error result, or may be + * set by the dialog if a pairing or connect error occured. + * @private + */ + errorMessage_: String, + + /** + * Pincode or passkey value, used to trigger connect enabled changes. + * @private + */ + pinOrPass_: String, + + /** + * @const {!Array<number>} + * @private + */ + digits_: { + type: Array, + readOnly: true, + value: [0, 1, 2, 3, 4, 5], + }, + }, + + observers: [ + 'dialogUpdated_(errorMessage_, pairingEvent_)', + 'pairingChanged_(pairingDevice, pairingEvent_)', + ], + + /** + * Listener for chrome.bluetoothPrivate.onPairing events. + * @private {?function(!chrome.bluetoothPrivate.PairingEvent)} + */ + bluetoothPrivateOnPairingListener_: null, + + /** + * Listener for chrome.bluetooth.onBluetoothDeviceChanged events. + * @private {?function(!chrome.bluetooth.Device)} + */ + bluetoothDeviceChangedListener_: null, + + open: function() { + this.startPairing(); + this.pinOrPass_ = ''; + this.getDialog_().showModal(); + this.itemWasFocused_ = false; + }, + + close: function() { + this.endPairing(); + var dialog = this.getDialog_(); + if (dialog.open) + dialog.close(); + }, + + /** + * Updates the dialog after a connect attempt. + * @param {!chrome.bluetooth.Device} device The device connected to + * @param {!{message: string}} lastError chrome.runtime.lastError + * @param {chrome.bluetoothPrivate.ConnectResultType} result The connect + * result + * @return {boolean} + */ + handleError: function(device, lastError, result) { + var error; + if (lastError) { + error = lastError.message; + } else { + switch (result) { + case chrome.bluetoothPrivate.ConnectResultType.IN_PROGRESS: + case chrome.bluetoothPrivate.ConnectResultType.ALREADY_CONNECTED: + case chrome.bluetoothPrivate.ConnectResultType.AUTH_CANCELED: + case chrome.bluetoothPrivate.ConnectResultType.SUCCESS: + this.errorMessage_ = ''; + return false; + default: + error = result; + } + } + + var name = device.name || device.address; + var id = 'bluetooth_connect_' + error; + if (this.i18nExists(id)) { + this.errorMessage_ = this.i18n(id, name); + } else { + this.errorMessage_ = error; + console.error('Unexpected error connecting to: ' + name + ': ' + error); + } + return true; + }, + + /** @private */ + dialogUpdated_: function() { + if (this.showEnterPincode_()) + this.$$('#pincode').focus(); + else if (this.showEnterPasskey_()) + this.$$('#passkey').focus(); + }, + + /** + * @return {!CrDialogElement} + * @private + */ + getDialog_: function() { + return /** @type {!CrDialogElement} */ (this.$.dialog); + }, + + /** @private */ + onCancelTap_: function() { + this.getDialog_().cancel(); + }, + + /** @private */ + onDialogCanceled_: function() { + if (!this.errorMessage_) + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CANCEL); + this.endPairing(); + }, + + /** Called when the dialog is opened. Starts listening for pairing events. */ + startPairing: function() { + if (!this.bluetoothPrivateOnPairingListener_) { + this.bluetoothPrivateOnPairingListener_ = + this.onBluetoothPrivateOnPairing_.bind(this); + this.bluetoothPrivate.onPairing.addListener( + this.bluetoothPrivateOnPairingListener_); + } + if (!this.bluetoothDeviceChangedListener_) { + this.bluetoothDeviceChangedListener_ = + this.onBluetoothDeviceChanged_.bind(this); + this.bluetooth.onDeviceChanged.addListener( + this.bluetoothDeviceChangedListener_); + } + }, + + /** Called when the dialog is closed. */ + endPairing: function() { + if (this.bluetoothPrivateOnPairingListener_) { + this.bluetoothPrivate.onPairing.removeListener( + this.bluetoothPrivateOnPairingListener_); + this.bluetoothPrivateOnPairingListener_ = null; + } + if (this.bluetoothDeviceChangedListener_) { + this.bluetooth.onDeviceChanged.removeListener( + this.bluetoothDeviceChangedListener_); + this.bluetoothDeviceChangedListener_ = null; + } + this.pairingEvent_ = null; + }, + + /** + * Process bluetoothPrivate.onPairing events. + * @param {!chrome.bluetoothPrivate.PairingEvent} event + * @private + */ + onBluetoothPrivateOnPairing_: function(event) { + if (!this.pairingDevice || + event.device.address != this.pairingDevice.address) { + return; + } + if (event.pairing == PairingEventType.KEYS_ENTERED && + event.passkey === undefined && this.pairingEvent_) { + // 'keysEntered' event might not include the updated passkey so preserve + // the current one. + event.passkey = this.pairingEvent_.passkey; + } + this.pairingEvent_ = event; + }, + + /** + * Process bluetooth.onDeviceChanged events. This ensures that the dialog + * updates when the connection state changes. + * @param {!chrome.bluetooth.Device} device + * @private + */ + onBluetoothDeviceChanged_: function(device) { + if (!this.pairingDevice || device.address != this.pairingDevice.address) + return; + this.pairingDevice = device; + }, + + /** @private */ + pairingChanged_: function() { + // Auto-close the dialog when pairing completes. + if (this.pairingDevice.paired && !this.pairingDevice.connecting && + this.pairingDevice.connected) { + this.close(); + return; + } + this.errorMessage_ = ''; + this.pinOrPass_ = ''; + }, + + /** + * @return {string} + * @private + */ + getMessage_: function() { + var message; + if (!this.pairingEvent_) + message = 'bluetoothStartConnecting'; + else + message = this.getEventDesc_(this.pairingEvent_.pairing); + return this.i18n(message, this.pairingDevice.name); + }, + + /** + * @return {boolean} + * @private + */ + showEnterPincode_: function() { + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE; + }, + + /** + * @return {boolean} + * @private + */ + showEnterPasskey_: function() { + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.REQUEST_PASSKEY; + }, + + /** + * @return {boolean} + * @private + */ + showDisplayPassOrPin_: function() { + if (!this.pairingEvent_) + return false; + var pairing = this.pairingEvent_.pairing; + return ( + pairing == PairingEventType.DISPLAY_PINCODE || + pairing == PairingEventType.DISPLAY_PASSKEY || + pairing == PairingEventType.CONFIRM_PASSKEY || + pairing == PairingEventType.KEYS_ENTERED); + }, + + /** + * @return {boolean} + * @private + */ + showAcceptReject_: function() { + return !!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY; + }, + + /** + * @return {boolean} + * @private + */ + showConnect_: function() { + if (!this.pairingEvent_) + return false; + var pairing = this.pairingEvent_.pairing; + return pairing == PairingEventType.REQUEST_PINCODE || + pairing == PairingEventType.REQUEST_PASSKEY; + }, + + /** + * @return {boolean} + * @private + */ + enableConnect_: function() { + if (!this.showConnect_()) + return false; + var inputId = + (this.pairingEvent_.pairing == PairingEventType.REQUEST_PINCODE) ? + '#pincode' : + '#passkey'; + var paperInput = /** @type {!PaperInputElement} */ (this.$$(inputId)); + assert(paperInput); + /** @type {string} */ var value = paperInput.value; + return !!value && paperInput.validate(); + }, + + /** + * @return {boolean} + * @private + */ + showDismiss_: function() { + return this.pairingDevice.paired || + (!!this.pairingEvent_ && + this.pairingEvent_.pairing == PairingEventType.COMPLETE); + }, + + /** @private */ + onAcceptTap_: function() { + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CONFIRM); + }, + + /** @private */ + onConnectTap_: function() { + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.CONFIRM); + }, + + /** @private */ + onRejectTap_: function() { + this.sendResponse_(chrome.bluetoothPrivate.PairingResponse.REJECT); + }, + + /** + * @param {!chrome.bluetoothPrivate.PairingResponse} response + * @private + */ + sendResponse_: function(response) { + if (!this.pairingDevice) + return; + var options = + /** @type {!chrome.bluetoothPrivate.SetPairingResponseOptions} */ ( + {device: this.pairingDevice, response: response}); + if (response == chrome.bluetoothPrivate.PairingResponse.CONFIRM) { + var pairing = this.pairingEvent_.pairing; + if (pairing == PairingEventType.REQUEST_PINCODE) + options.pincode = this.$$('#pincode').value; + else if (pairing == PairingEventType.REQUEST_PASSKEY) + options.passkey = parseInt(this.$$('#passkey').value, 10); + } + this.bluetoothPrivate.setPairingResponse(options, () => { + if (chrome.runtime.lastError) { + // TODO(stevenjb): Show error. + console.error( + 'Error setting pairing response: ' + options.device.name + + ': Response: ' + options.response + + ': Error: ' + chrome.runtime.lastError.message); + } + this.close(); + }); + + this.fire('response', options); + }, + + /** + * @param {!PairingEventType} eventType + * @return {string} + * @private + */ + getEventDesc_: function(eventType) { + assert(eventType); + if (eventType == PairingEventType.COMPLETE || + eventType == PairingEventType.KEYS_ENTERED || + eventType == PairingEventType.REQUEST_AUTHORIZATION) { + return 'bluetoothStartConnecting'; + } + return 'bluetooth_' + /** @type {string} */ (eventType); + }, + + /** + * @param {number} index + * @return {string} + * @private + */ + getPinDigit_: function(index) { + if (!this.pairingEvent_) + return ''; + var digit = '0'; + var pairing = this.pairingEvent_.pairing; + if (pairing == PairingEventType.DISPLAY_PINCODE && + this.pairingEvent_.pincode && + index < this.pairingEvent_.pincode.length) { + digit = this.pairingEvent_.pincode[index]; + } else if ( + this.pairingEvent_.passkey && + (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]; + } + return digit; + }, + + /** + * @param {number} index + * @return {string} + * @private + */ + getPinClass_: function(index) { + if (!this.pairingEvent_) + return ''; + if (this.pairingEvent_.pairing == PairingEventType.CONFIRM_PASSKEY) + return 'confirm'; + var cssClass = 'display'; + if (this.pairingEvent_.pairing == PairingEventType.DISPLAY_PASSKEY) { + if (index == 0) + cssClass += ' next'; + else + cssClass += ' untyped'; + } else if ( + this.pairingEvent_.pairing == PairingEventType.KEYS_ENTERED && + this.pairingEvent_.enteredKey) { + var enteredKey = this.pairingEvent_.enteredKey; // 1-7 + var lastKey = this.digits_.length; // 6 + if ((index == -1 && enteredKey > lastKey) || (index + 1 == enteredKey)) + cssClass += ' next'; + else if (index > enteredKey) + cssClass += ' untyped'; + } + return cssClass; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/chromeos/compiled_resources2.gyp new file mode 100644 index 00000000000..1e6afe0491a --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/compiled_resources2.gyp @@ -0,0 +1,30 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'network_resources', + 'type': 'none', + 'dependencies': [ + 'network/compiled_resources2.gyp:*', + ], + }, + { + 'target_name': 'bluetooth_dialog', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/cr_dialog/compiled_resources2.gyp:cr_dialog', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + '<(DEPTH)/third_party/polymer/v1_0/components-chromium/iron-resizable-behavior/compiled_resources2.gyp:iron-resizable-behavior-extracted', + '<(DEPTH)/third_party/polymer/v1_0/components-chromium/paper-input/compiled_resources2.gyp:paper-input-extracted', + '<(EXTERNS_GYP):bluetooth', + '<(EXTERNS_GYP):bluetooth_private', + '<(INTERFACES_GYP):bluetooth_interface', + '<(INTERFACES_GYP):bluetooth_private_interface', + ], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp new file mode 100644 index 00000000000..a18b88baaa0 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/compiled_resources2.gyp @@ -0,0 +1,63 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'network_apnlist', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'network_ip_config', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'network_nameservers', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'network_property_list', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_network_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'network_proxy', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp:cr_policy_network_behavior', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'network_proxy_input', + 'dependencies': [ + '<(DEPTH)/ui/webui/resources/cr_elements/chromeos/network/compiled_resources2.gyp:cr_onc_types', + '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:i18n_behavior', + ], + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + { + 'target_name': 'network_proxy_exclusions', + 'includes': ['../../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, + ], +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html new file mode 100644 index 00000000000..1a2c716e7ee --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.html @@ -0,0 +1,43 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/md_select_css.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="network_property_list.html"> +<link rel="import" href="network_shared_css.html"> + +<dom-module id="network-apnlist"> + <template> + <style include="network-shared md-select"> + paper-button { + margin: 4px 0; + } + </style> + <div class="property-box"> + <div class="start">[[i18n('networkAccessPoint')]]</div> + <div class="md-select-wrapper"> + <select id="selectApn" class="md-select" on-change="onSelectApnChange_" + value="[[selectedApn_]]" + aria-label="[[i18n('networkAccessPoint')]]"> + <template is="dom-repeat" items="[[apnSelectList_]]"> + <option value="[[item.AccessPointName]]">[[apnDesc_(item)]]</option> + </template> + </select> + <span class="md-select-underline"></span> + </div> + </div> + + <div class="property-box single-column indented" + hidden$="[[!isOtherSelected_(selectedApn_, networkProperties)]]"> + <network-property-list on-property-change="onOtherApnChange_" + fields="[[otherApnFields_]]" property-dict="[[otherApn_]]" + edit-field-types="[[otherApnEditTypes_]]" prefix="Cellular.APN."> + </network-property-list> + <paper-button class="action-button" on-tap="onSaveOtherTap_"> + [[i18n('save')]] + </paper-button> + </div> + </template> + <script src="network_apnlist.js"></script> +</dom-module> 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 new file mode 100644 index 00000000000..c4e5034e13d --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_apnlist.js @@ -0,0 +1,271 @@ +// 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. + +/** + * @fileoverview Polymer element for displaying and modifying a list of cellular + * access points. + */ +Polymer({ + is: 'network-apnlist', + + behaviors: [I18nBehavior], + + properties: { + /** + * The current set of properties for the network matching |guid|. + * @type {!CrOnc.NetworkProperties|undefined} + */ + networkProperties: { + type: Object, + observer: 'networkPropertiesChanged_', + }, + + /** + * The CrOnc.APNProperties.AccessPointName value of the selected APN. + * @private + */ + selectedApn_: { + type: String, + value: '', + }, + + /** + * Selectable list of APN dictionaries for the UI. Includes an entry + * corresponding to |otherApn| (see below). + * @private {!Array<!CrOnc.APNProperties>} + */ + apnSelectList_: { + type: Array, + value: function() { + return []; + } + }, + + /** + * The user settable properties for a new ('other') APN. The values for + * AccessPointName, Username, and Password will be set to the currently + * active APN if it does not match an existing list entry. + * @private {CrOnc.APNProperties|undefined} + */ + otherApn_: { + type: Object, + }, + + /** + * Array of property names to pass to the Other APN property list. + * @private {!Array<string>} + */ + otherApnFields_: { + type: Array, + value: function() { + return ['AccessPointName', 'Username', 'Password']; + }, + readOnly: true + }, + + /** + * Array of edit types to pass to the Other APN property list. + * @private + */ + otherApnEditTypes_: { + type: Object, + value: function() { + return { + 'AccessPointName': 'String', + 'Username': 'String', + 'Password': 'Password' + }; + }, + readOnly: true + }, + }, + + /** @const */ + DefaultAccessPointName: 'none', + + /** + * Polymer networkProperties changed method. + */ + networkPropertiesChanged_: function() { + if (!this.networkProperties || !this.networkProperties.Cellular) + return; + + /** @type {!CrOnc.APNProperties|undefined} */ var activeApn; + var cellular = this.networkProperties.Cellular; + /** @type {!chrome.networkingPrivate.ManagedAPNProperties|undefined} */ var + apn = cellular.APN; + if (apn && apn.AccessPointName) { + activeApn = /** @type {!CrOnc.APNProperties|undefined} */ ( + CrOnc.getSimpleActiveProperties(apn)); + } else if (cellular.LastGoodAPN && cellular.LastGoodAPN.AccessPointName) { + activeApn = cellular.LastGoodAPN; + } + this.setApnSelectList_(activeApn); + }, + + /** + * Sets the list of selectable APNs for the UI. Appends an 'Other' entry + * (see comments for |otherApn_| above). + * @param {CrOnc.APNProperties|undefined} activeApn The currently active APN + * properties. + * @private + */ + setApnSelectList_: function(activeApn) { + // Copy the list of APNs from this.networkProperties. + var result = this.getApnList_().slice(); + + // Test whether |activeApn| is in the current APN list in networkProperties. + var activeApnInList = activeApn && result.some(function(a) { + return a.AccessPointName == activeApn.AccessPointName; + }); + + // If |activeApn| is specified and not in the list, use the active + // properties for 'other'. Otherwise use any existing 'other' properties. + var otherApnProperties = + (activeApn && !activeApnInList) ? activeApn : this.otherApn_; + var otherApn = this.createApnObject_(otherApnProperties); + + // Always use 'Other' for the name of custom APN entries (the name does + // not get saved). + otherApn.Name = 'Other'; + + // If no 'active' or 'other' AccessPointName was provided, use the default. + otherApn.AccessPointName = + otherApn.AccessPointName || this.DefaultAccessPointName; + + // Save the 'other' properties. + this.otherApn_ = otherApn; + + // Append 'other' to the end of the list of APNs. + result.push(otherApn); + + this.apnSelectList_ = result; + // Set selectedApn_ after dom-repeat has been stamped. + this.async(() => { + this.selectedApn_ = + (activeApn && activeApn.AccessPointName) || otherApn.AccessPointName; + }); + }, + + /** + * @param {!CrOnc.APNProperties|undefined=} apnProperties + * @return {!CrOnc.APNProperties} A new APN object with properties from + * |apnProperties| if provided. + * @private + */ + createApnObject_: function(apnProperties) { + var newApn = {AccessPointName: ''}; + if (apnProperties) + Object.assign(newApn, apnProperties); + return newApn; + }, + + /** + * @return {!Array<!CrOnc.APNProperties>} The list of APN properties in + * |networkProperties| or an empty list if the property is not set. + * @private + */ + getApnList_: function() { + if (!this.networkProperties || !this.networkProperties.Cellular) + return []; + /** @type {!chrome.networkingPrivate.ManagedAPNList|undefined} */ var + apnlist = this.networkProperties.Cellular.APNList; + if (!apnlist) + return []; + return /** @type {!Array<!CrOnc.APNProperties>} */ ( + CrOnc.getActiveValue(apnlist)); + }, + + /** + * Event triggered when the selectApn selection changes. + * @param {!Event} event + * @private + */ + onSelectApnChange_: function(event) { + var target = /** @type {!HTMLSelectElement} */ (event.target); + var accessPointName = target.value; + // When selecting 'Other', don't set a change event unless a valid + // non-default value has been set for Other. + if (this.isOtherSelected_(accessPointName) && + (!this.otherApn_ || !this.otherApn_.AccessPointName || + this.otherApn_.AccessPointName == this.DefaultAccessPointName)) { + this.selectedApn_ = accessPointName; + return; + } + this.sendApnChange_(accessPointName); + }, + + /** + * Event triggered when any 'Other' APN network property changes. + * @param {!{detail: {field: string, value: string}}} event + * @private + */ + onOtherApnChange_: function(event) { + this.set('otherApn_.' + event.detail.field, event.detail.value); + // Don't send a change event for 'Other' until the 'Save' button is tapped. + }, + + /** + * Event triggered when the Other APN 'Save' button is tapped. + * @param {!Event} event + * @private + */ + onSaveOtherTap_: function(event) { + this.sendApnChange_(this.selectedApn_); + }, + + /** + * Send the apn-change event. + * @param {string} accessPointName + * @private + */ + sendApnChange_: function(accessPointName) { + var apnList = this.getApnList_(); + var apn = this.findApnInList(apnList, accessPointName); + if (apn == undefined) { + apn = this.createApnObject_(); + if (this.otherApn_) { + apn.AccessPointName = this.otherApn_.AccessPointName; + apn.Username = this.otherApn_.Username; + apn.Password = this.otherApn_.Password; + } + } + this.fire('apn-change', {field: 'APN', value: apn}); + }, + + /** + * @param {string} accessPointName + * @return {boolean} True if the 'other' APN is currently selected. + * @private + */ + isOtherSelected_: function(accessPointName) { + if (!this.networkProperties || !this.networkProperties.Cellular) + return false; + var apnList = this.getApnList_(); + var apn = this.findApnInList(apnList, accessPointName); + return apn == undefined; + }, + + /** + * @param {!CrOnc.APNProperties} apn + * @return {string} The most descriptive name for the access point. + * @private + */ + apnDesc_: function(apn) { + return apn.LocalizedName || apn.Name || apn.AccessPointName; + }, + + /** + * @param {!Array<!CrOnc.APNProperties>} apnList + * @param {string} accessPointName + * @return {CrOnc.APNProperties|undefined} The entry in |apnList| matching + * |accessPointName| if it exists, or undefined. + * @private + */ + findApnInList: function(apnList, accessPointName) { + return apnList.find(function(a) { + return a.AccessPointName == accessPointName; + }); + } +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html new file mode 100644 index 00000000000..8999e86a290 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.html @@ -0,0 +1,30 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> +<link rel="import" href="network_property_list.html"> +<link rel="import" href="network_shared_css.html"> + +<dom-module id="network-ip-config"> + <template> + <style include="network-shared iron-flex"></style> + <div class="property-box"> + <div id="autoIPConfigLabel" class="start"> + [[i18n('networkIPConfigAuto')]] + </div> + <paper-toggle-button checked="{{automatic_}}" disabled="[[!editable]]" + aria-labelledby="autoIPConfigLabel"> + </paper-toggle-button> + </div> + <div class="property-box single-column indented stretch" + hidden$="[[!ipConfig_]]"> + <network-property-list + fields="[[ipConfigFields_]]" property-dict="[[ipConfig_]]" + edit-field-types="[[getIPEditFields_(editable, automatic_)]]" + on-property-change="onIPChange_"> + </network-property-list> + </div> + </template> + <script src="network_ip_config.js"></script> +</dom-module> 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 new file mode 100644 index 00000000000..519367b120a --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_ip_config.js @@ -0,0 +1,214 @@ +// 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. + +/** + * @fileoverview Polymer element for displaying the IP Config properties for + * a network state. TODO(stevenjb): Allow editing of static IP configurations + * when 'editable' is true. + */ +Polymer({ + is: 'network-ip-config', + + behaviors: [I18nBehavior], + + properties: { + /** + * The network properties dictionary containing the IP Config properties to + * display and modify. + * @type {!CrOnc.NetworkProperties|undefined} + */ + networkProperties: { + type: Object, + observer: 'networkPropertiesChanged_', + }, + + /** + * Whether or not the IP Address can be edited. + */ + editable: { + type: Boolean, + value: false, + }, + + /** + * State of 'Configure IP Addresses Automatically'. + * @private + */ + automatic_: { + type: Boolean, + value: true, + observer: 'automaticChanged_', + }, + + /** + * The currently visible IP Config property dictionary. The 'RoutingPrefix' + * property is a human-readable mask instead of a prefix length. + * @private {?{ + * ipv4: !CrOnc.IPConfigUIProperties, + * ipv6: (!CrOnc.IPConfigUIProperties|undefined) + * }} + */ + ipConfig_: { + type: Object, + value: null, + }, + + /** + * Array of properties to pass to the property list. + * @private {!Array<string>} + */ + ipConfigFields_: { + type: Array, + value: function() { + return [ + 'ipv4.IPAddress', + 'ipv4.RoutingPrefix', + 'ipv4.Gateway', + 'ipv6.IPAddress', + ]; + }, + readOnly: true + }, + }, + + /** + * Saved static IP configuration properties when switching to 'automatic'. + * @private {!CrOnc.IPConfigUIProperties|undefined} + */ + savedStaticIp_: undefined, + + /** + * Polymer networkProperties changed method. + */ + networkPropertiesChanged_: function(newValue, oldValue) { + if (!this.networkProperties) + return; + + var properties = this.networkProperties; + if (newValue.GUID != (oldValue && oldValue.GUID)) + this.savedStaticIp_ = undefined; + + // Update the 'automatic' property. + if (properties.IPAddressConfigType) { + var ipConfigType = CrOnc.getActiveValue(properties.IPAddressConfigType); + this.automatic_ = (ipConfigType != CrOnc.IPConfigType.STATIC); + } + + if (properties.IPConfigs || properties.StaticIPConfig) { + // Update the 'ipConfig' property. + var ipv4 = CrOnc.getIPConfigForType(properties, CrOnc.IPType.IPV4); + var ipv6 = CrOnc.getIPConfigForType(properties, CrOnc.IPType.IPV6); + this.ipConfig_ = { + ipv4: this.getIPConfigUIProperties_(ipv4), + ipv6: this.getIPConfigUIProperties_(ipv6) + }; + } else { + this.ipConfig_ = null; + } + }, + + /** @private */ + automaticChanged_: function() { + if (!this.automatic_) { + // Ensure that there is a valid IPConfig object. + this.ipConfig_ = this.ipConfig_ || { + ipv4: { + Gateway: '192.168.1.1', + IPAddress: '192.168.1.1', + RoutingPrefix: '255.255.255.0', + Type: CrOnc.IPType.IPV4, + }, + }; + this.sendStaticIpConfig_(); + return; + } + + // Save the static IP configuration when switching to automatic. + if (this.ipConfig_) + this.savedStaticIp_ = this.ipConfig_.ipv4; + // Send the change. + this.fire('ip-change', { + field: 'IPAddressConfigType', + value: CrOnc.IPConfigType.DHCP, + }); + }, + + /** + * @param {!CrOnc.IPConfigProperties|undefined} ipconfig + * @return {!CrOnc.IPConfigUIProperties} A new IPConfigUIProperties object + * with RoutingPrefix expressed as a string mask instead of a prefix + * length. Returns an empty object if |ipconfig| is undefined. + * @private + */ + getIPConfigUIProperties_: function(ipconfig) { + var result = {}; + if (!ipconfig) + return result; + for (var key in ipconfig) { + var value = ipconfig[key]; + if (key == 'RoutingPrefix') + result.RoutingPrefix = CrOnc.getRoutingPrefixAsNetmask(value); + else + result[key] = value; + } + return result; + }, + + /** + * @param {!CrOnc.IPConfigUIProperties} ipconfig The IP Config UI properties. + * @return {!CrOnc.IPConfigProperties} A new IPConfigProperties object with + * RoutingPrefix expressed as a a prefix length. + * @private + */ + getIPConfigProperties_: function(ipconfig) { + var result = {}; + for (var key in ipconfig) { + var value = ipconfig[key]; + if (key == 'RoutingPrefix') + result.RoutingPrefix = CrOnc.getRoutingPrefixAsLength(value); + else + result[key] = value; + } + return result; + }, + + /** + * @return {Object} An object with the edit type for each editable field. + * @private + */ + getIPEditFields_: function() { + if (!this.editable || this.automatic_) + return {}; + return { + 'ipv4.IPAddress': 'String', + 'ipv4.RoutingPrefix': 'String', + 'ipv4.Gateway': 'String' + }; + }, + + /** + * Event triggered when the network property list changes. + * @param {!{detail: {field: string, value: string}}} event The + * network-property-list change event. + * @private + */ + onIPChange_: function(event) { + if (!this.ipConfig_) + return; + var field = event.detail.field; + var value = event.detail.value; + // Note: |field| includes the 'ipv4.' prefix. + this.set('ipConfig_.' + field, value); + this.sendStaticIpConfig_(); + }, + + /** @private */ + sendStaticIpConfig_: function() { + // This will also set IPAddressConfigType to STATIC. + this.fire('ip-change', { + field: 'StaticIPConfig', + value: this.getIPConfigProperties_(this.ipConfig_.ipv4) + }); + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html new file mode 100644 index 00000000000..a64f6ca853d --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.html @@ -0,0 +1,43 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/md_select_css.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html"> +<link rel="import" href="network_shared_css.html"> + +<dom-module id="network-nameservers"> + <template> + <style include=" network-shared md-select"> + paper-input-container { + -webkit-padding-start: 4px; + } + </style> + + <div class="property-box"> + <div class="start">[[i18n('networkNameservers')]]</div> + <div class="md-select-wrapper"> + <select id="nameserverType" class="md-select" on-change="onTypeChange_" + value="[[nameserversType_]]" + aria-label="[[i18n('networkNameservers')]]"> + <template is="dom-repeat" items="[[nameserverTypeNames_]]"> + <option value="[[item]]">[[nameserverTypeDesc_(item)]]</option> + </template> + </select> + <span class="md-select-underline"></span> + </div> + </div> + + <div class="property-box single-column indented" + hidden$="[[!nameservers_.length]]"> + <template is="dom-repeat" items="[[nameservers_]]"> + <paper-input-container no-label-float> + <input id="nameserver[[index]]" is="iron-input" value="[[item]]" + disabled="[[!canEdit_(editable, nameserversType_)]]" + on-change="onValueChange_"> + </paper-input-container> + </template> + </div> + </template> + <script src="network_nameservers.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js new file mode 100644 index 00000000000..237614403be --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_nameservers.js @@ -0,0 +1,214 @@ +// 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. + +/** + * @fileoverview Polymer element for displaying network nameserver options. + */ +Polymer({ + is: 'network-nameservers', + + behaviors: [I18nBehavior], + + properties: { + /** + * The network properties dictionary containing the nameserver properties to + * display and modify. + * @type {!CrOnc.NetworkProperties|undefined} + */ + networkProperties: { + type: Object, + observer: 'networkPropertiesChanged_', + }, + + /** Whether or not the nameservers can be edited. */ + editable: { + type: Boolean, + value: false, + }, + + /** + * Array of nameserver addresses stored as strings. + * @private {!Array<string>} + */ + nameservers_: { + type: Array, + value: function() { + return []; + }, + }, + + /** + * The selected nameserver type. + * @private + */ + nameserversType_: { + type: String, + value: 'automatic', + }, + + /** + * Array of nameserver types. + * @private + */ + nameserverTypeNames_: { + type: Array, + value: ['automatic', 'google', 'custom'], + readOnly: true, + }, + }, + + /** @const */ + GOOGLE_NAMESERVERS: [ + '8.8.4.4', + '8.8.8.8', + ], + + /** @const */ + MAX_NAMESERVERS: 4, + + /** + * Saved nameservers when switching to 'automatic'. + * @private {!Array<string>} + */ + savedNameservers_: [], + + /** @private */ + networkPropertiesChanged_: function(newValue, oldValue) { + if (!this.networkProperties) + return; + + if (!oldValue || newValue.GUID != oldValue.GUID) + this.savedNameservers_ = []; + + // Update the 'nameservers' property. + var nameservers = []; + var ipv4 = + CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4); + if (ipv4 && ipv4.NameServers) + nameservers = ipv4.NameServers; + + // Update the 'nameserversType' property. + var configType = + CrOnc.getActiveValue(this.networkProperties.NameServersConfigType); + var type; + if (configType == CrOnc.IPConfigType.STATIC) { + if (nameservers.join(',') == this.GOOGLE_NAMESERVERS.join(',')) { + type = 'google'; + } else { + type = 'custom'; + } + } else { + type = 'automatic'; + } + this.setNameservers_(type, nameservers); + }, + + /** + * @param {string} nameserversType + * @param {!Array<string>} nameservers + * @private + */ + setNameservers_: function(nameserversType, nameservers) { + if (nameserversType == 'custom') { + // Add empty entries for unset custom nameservers. + for (var i = nameservers.length; i < this.MAX_NAMESERVERS; ++i) + nameservers[i] = ''; + } + this.nameservers_ = nameservers; + // Set nameserversType_ after dom-repeat has been stamped. + this.async(() => { + this.nameserversType_ = nameserversType; + }); + }, + + /** + * @param {string} type The nameservers type. + * @return {string} The description for |type|. + * @private + */ + nameserverTypeDesc_: function(type) { + // TODO(stevenjb): Translate. + if (type == 'custom') + return 'Custom name servers'; + if (type == 'google') + return 'Google name servers'; + return 'Automatic name servers'; + }, + + /** + * @param {boolean} editable + * @param {string} nameserversType + * @return {boolean} True if the nameservers are editable. + * @private + */ + canEdit_: function(editable, nameserversType) { + return editable && nameserversType == 'custom'; + }, + + /** + * Event triggered when the selected type changes. Updates nameservers and + * sends the change value if necessary. + * @param {!Event} event + * @private + */ + onTypeChange_: function(event) { + if (this.nameserversType_ == 'custom') + this.savedNameservers_ = this.nameservers_; + var target = /** @type {!HTMLSelectElement} */ (event.target); + var type = target.value; + this.nameserversType_ = type; + if (type == 'custom') { + // Restore the saved nameservers. + this.setNameservers_(type, this.savedNameservers_); + // Only send custom nameservers if they are not empty. + if (this.savedNameservers_.length == 0) + return; + } + this.sendNameServers_(); + }, + + /** + * Event triggered when a nameserver value changes. + * @private + */ + onValueChange_: function() { + if (this.nameserversType_ != 'custom') { + // If a user inputs Google nameservers in the custom nameservers fields, + // |nameserversType| will change to 'google' so don't send the values. + return; + } + this.sendNameServers_(); + }, + + /** + * Sends the current nameservers type (for automatic) or value. + * @private + */ + sendNameServers_: function() { + var type = this.nameserversType_; + + if (type == 'custom') { + var nameservers = new Array(this.MAX_NAMESERVERS); + for (var i = 0; i < this.MAX_NAMESERVERS; ++i) { + var nameserverInput = this.$$('#nameserver' + i); + nameservers[i] = nameserverInput ? nameserverInput.value : ''; + } + this.fire('nameservers-change', { + field: 'NameServers', + value: nameservers, + }); + } else if (type == 'google') { + this.fire('nameservers-change', { + field: 'NameServers', + value: this.GOOGLE_NAMESERVERS, + }); + } else { + // automatic + this.fire('nameservers-change', { + field: 'NameServersConfigType', + value: CrOnc.IPConfigType.DHCP, + }); + } + }, +}); 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 new file mode 100644 index 00000000000..0d73594c0af --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.html @@ -0,0 +1,71 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> +<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_indicator.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html"> +<link rel="import" href="network_shared_css.html"> + +<dom-module id="network-property-list"> + <template> + <style include="network-shared iron-flex"> + paper-input-container { + margin-bottom: -12px; + margin-top: -8px; + } + + /* Property lists are embedded; remove the padding. */ + .property-box { + padding: 0; + } + + .secondary { + color: var(--paper-grey-600); + font-weight: 400; + } + + cr-policy-network-indicator { + -webkit-margin-start: var(--settings-controlled-by-spacing); + } + </style> + <template is="dom-repeat" items="[[fields]]" + filter="[[computeFilter_(prefix, propertyDict, editFieldTypes)]]"> + <div class="property-box single-column stretch"> + <!-- Property label --> + <div>[[getPropertyLabel_(item, prefix)]]</div> + <!-- Uneditable property value --> + <div class="layout horizontal" + hidden="[[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> + <!-- Editable String property value --> + <template is="dom-if" if="[[isEditable_( + item, 'String', propertyDict, editFieldTypes)]]"> + <paper-input-container no-label-float> + <input id="[[item]]" is="iron-input" + 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" + value="[[getPropertyValue_(item, prefix, propertyDict)]]" + on-change="onValueChange_"> + </paper-input-container> + </template> + <!-- TODO(stevenjb): Support other types. --> + </div> + </template> + </template> + <script src="network_property_list.js"></script> +</dom-module> 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 new file mode 100644 index 00000000000..f333386cb78 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_property_list.js @@ -0,0 +1,214 @@ +// 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. + +/** + * @fileoverview Polymer element for displaying a list of network properties + * in a list. This also supports editing fields inline for fields listed in + * editFieldTypes. + */ +Polymer({ + is: 'network-property-list', + + behaviors: [I18nBehavior, CrPolicyNetworkBehavior], + + properties: { + /** + * The dictionary containing the properties to display. + * @type {!Object|undefined} + */ + propertyDict: {type: Object}, + + /** + * Fields to display. + * @type {!Array<string>} + */ + fields: { + type: Array, + value: function() { + return []; + }, + }, + + /** + * 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. + * 'Password' - A string with input type = password. + * TODO(stevenjb): Support types with custom validation, e.g. IPAddress. + * TODO(stevenjb): Support 'Number'. + * When a field changes, the 'property-change' event will be fired with + * the field name and the new value provided in the event detail. + */ + editFieldTypes: { + type: Object, + value: function() { + return {}; + }, + }, + + /** Prefix used to look up property key translations. */ + prefix: { + type: String, + value: '', + }, + }, + + /** + * Event triggered when an input field changes. Fires a 'property-change' + * event with the field (property) name set to the target id, and the value + * set to the target input value. + * @param {!Event} event The input change event. + * @private + */ + onValueChange_: function(event) { + if (!this.propertyDict) + return; + var field = event.target.id; + var curValue = this.get(field, this.propertyDict); + if (typeof curValue == 'object') { + // Extract the property from an ONC managed dictionary. + curValue = CrOnc.getActiveValue( + /** @type {!CrOnc.ManagedProperty} */ (curValue)); + } + var newValue = event.target.value; + if (newValue == curValue) + return; + this.fire('property-change', {field: field, value: newValue}); + }, + + /** + * @param {string} key The property key. + * @param {string} prefix + * @return {string} The text to display for the property label. + * @private + */ + getPropertyLabel_: function(key, prefix) { + var oncKey = 'Onc' + prefix + key; + oncKey = oncKey.replace(/\./g, '-'); + if (this.i18nExists(oncKey)) + return this.i18n(oncKey); + // We do not provide translations for every possible network property key. + // For keys specific to a type, strip the type prefix. + var result = prefix + key; + for (var entry in chrome.networkingPrivate.NetworkType) { + var type = chrome.networkingPrivate.NetworkType[entry]; + if (result.startsWith(type + '.')) { + result = result.substr(type.length + 1); + break; + } + } + return result; + }, + + /** + * Generates a filter function dependent on propertyDict and editFieldTypes. + * @param {string} prefix + * @param {!Object} propertyDict + * @param {!Object} editFieldTypes + * @private + */ + computeFilter_: function(prefix, propertyDict, editFieldTypes) { + return key => { + if (editFieldTypes.hasOwnProperty(key)) + return true; + var value = this.getPropertyValue_(key, prefix, propertyDict); + return value !== undefined && value !== ''; + }; + }, + + /** + * @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) { + var property = /** @type {!CrOnc.ManagedProperty|undefined} */ ( + this.get(key, propertyDict)); + if (this.isNetworkPolicyEnforced(property)) + return false; + var editType = editFieldTypes[key]; + return editType !== undefined && (type == '' || editType == type); + }, + + /** + * @param {string} key The property key. + * @param {!Object} propertyDict + * @return {*} The managed property dictionary associated with |key|. + * @private + */ + getProperty_: function(key, propertyDict) { + return this.get(key, propertyDict); + }, + + /** + * @param {string} key The property key. + * @param {string} prefix + * @param {!Object} propertyDict + * @return {string} The text to display for the property value. + * @private + */ + getPropertyValue_: function(key, prefix, propertyDict) { + var value = this.get(key, propertyDict); + if (value === undefined) + return ''; + if (typeof value == 'object') { + // Extract the property from an ONC managed dictionary + value = + CrOnc.getActiveValue(/** @type {!CrOnc.ManagedProperty} */ (value)); + } + 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; + oncKey = oncKey.replace(/\./g, '-'); + oncKey += '_' + valueStr; + if (this.i18nExists(oncKey)) + return this.i18n(oncKey); + return valueStr; + }, + + /** + * @param {string} key The property key. + * @param {*} value The property value. + * @return {string} The text to display for the property value. If the key + * does not correspond to a custom property, an empty string is returned. + */ + getCustomPropertyValue_: function(key, value) { + if (key == 'Tether.BatteryPercentage') { + assert(typeof value == 'number'); + return this.i18n('OncTether-BatteryPercentage_Value', value.toString()); + } + + if (key == 'Tether.SignalStrength') { + assert(typeof value == 'number'); + // Possible |signalStrength| values should be 0, 25, 50, 75, and 100. Add + // <= checks for robustness. + if (value <= 24) + return this.i18n('OncTether-SignalStrength_Weak'); + if (value <= 49) + return this.i18n('OncTether-SignalStrength_Okay'); + if (value <= 74) + return this.i18n('OncTether-SignalStrength_Good'); + if (value <= 99) + return this.i18n('OncTether-SignalStrength_Strong'); + return this.i18n('OncTether-SignalStrength_VeryStrong'); + } + + if (key == 'Tether.Carrier') { + assert(typeof value == 'string'); + return (!value || value == 'unknown-carrier') ? + this.i18n('tetherUnknownCarrier') : + value; + } + + return ''; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html new file mode 100644 index 00000000000..4b328f9145b --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.html @@ -0,0 +1,154 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/chromeos/network/cr_onc_types.html"> +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_network_behavior.html"> +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/md_select_css.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-keys/iron-a11y-keys.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-toggle-button/paper-toggle-button.html"> +<link rel="import" href="network_proxy_exclusions.html"> +<link rel="import" href="network_proxy_input.html"> +<link rel="import" href="network_shared_css.html"> + +<dom-module id="network-proxy"> + <template> + <style include="network-shared md-select cr-hidden-style iron-flex iron-flex-alignment"> + network-proxy-input { + margin-bottom: 10px; + } + + network-proxy-exclusions { + margin: 10px 0; + } + + #manualProxy { + -webkit-padding-start: var(--cr-section-padding); + } + </style> + + <!-- Proxy type dropdown --> + <div class="property-box"> + <div class="start">[[i18n('networkProxyConnectionType')]]</div> + <div class="md-select-wrapper"> + <select id="proxyType" class="md-select" on-change="onTypeChange_" + value="[[proxy_.Type]]" + disabled="[[!isEditable_('Type', networkProperties, editable, + useSharedProxies)]]" + aria-label="[[i18n('networkProxyConnectionType')]]"> + <template is="dom-repeat" items="[[proxyTypes_]]"> + <option value="[[item]]">[[getProxyTypeDesc_(item)]]</option> + </template> + </select> + <span class="md-select-underline"></span> + </div> + </div> + + <!-- Autoconfiguration (PAC) --> + <div class="property-box indented" + hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.PAC)]]"> + <div>[[i18n('networkProxyAutoConfig')]]</div> + <paper-input no-label-float class="middle" value="{{proxy_.PAC}}" + disabled="[[!isEditable_('PAC', networkProperties, editable, + useSharedProxies)]]" + on-change="onPACChange_"> + </paper-input> + </div> + + <!-- Web Proxy Auto Discovery (WPAD) --> + <div class="property-box indented" + hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.WPAD)]]"> + <div>[[i18n('networkProxyWpad')]]</div> + <div class="middle">[[WPAD_]]</div> + </div> + + <!-- Manual --> + <div class="property-box indented" + hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.MANUAL)]]"> + <div id="networkProxyToggleLabel" class="flex"> + [[i18n('networkProxyUseSame')]] + </div> + <paper-toggle-button checked="{{useSameProxy_}}" + disabled="[[!isEditable_('Type', networkProperties, editable, + useSharedProxies)]]" + aria-labelledby="networkProxyToggleLabel"> + </paper-toggle-button> + </div> + + <div id="manualProxy" class="layout vertical start" + hidden$="[[!matches_(proxy_.Type, ProxySettingsType_.MANUAL)]]"> + <div hidden$="[[!useSameProxy_]]" class="layout vertical"> + <network-proxy-input + on-proxy-change="onProxyInputChange_" + editable="[[isEditable_('Manual.HTTPProxy.Host', networkProperties, + editable, useSharedProxies)]]" + value="{{proxy_.Manual.HTTPProxy}}" + label="[[i18n('networkProxy')]]"> + </network-proxy-input> + </div> + <div hidden$="[[useSameProxy_]]" class="layout vertical"> + <network-proxy-input + on-proxy-change="onProxyInputChange_" + editable="[[isEditable_('Manual.HTTPProxy.Host', networkProperties, + editable, useSharedProxies)]]" + value="{{proxy_.Manual.HTTPProxy}}" + label="[[i18n('networkProxyHttp')]]"> + </network-proxy-input> + <network-proxy-input + on-proxy-change="onProxyInputChange_" + editable="[[isEditable_('Manual.SecureHTTPProxy.Host', + networkProperties, editable, useSharedProxies)]]" + value="{{proxy_.Manual.SecureHTTPProxy}}" + label="[[i18n('networkProxyShttp')]]"> + </network-proxy-input> + <network-proxy-input + on-proxy-change="onProxyInputChange_" + editable="[[isEditable_('Manual.FTPProxy.Host', networkProperties, + editable, useSharedProxies)]]" + value="{{proxy_.Manual.FTPProxy}}" + label="[[i18n('networkProxyFtp')]]"> + </network-proxy-input> + <network-proxy-input + on-proxy-change="onProxyInputChange_" + editable="[[isEditable_('Manual.SOCKS.Host', networkProperties, + editable, useSharedProxies)]]" + value="{{proxy_.Manual.SOCKS}}" + label="[[i18n('networkProxySocks')]]"> + </network-proxy-input> + </div> + + <div hidden="[[!isEditable_('Type', networkProperties, editable, + useSharedProxies)]]"> + <div>[[i18n('networkProxyExceptionList')]]</div> + <network-proxy-exclusions on-proxy-change="onProxyExclusionsChange_" + exclusions="{{proxy_.ExcludeDomains}}"> + </network-proxy-exclusions> + <div class="layout horizontal"> + <paper-input-container no-label-float class="flex"> + <input id="proxyExclusion" is="iron-input"> + <iron-a11y-keys keys="enter" + on-keys-pressed="onAddProxyExclusionTap_"> + </iron-a11y-keys> + </paper-input-container> + <paper-button on-tap="onAddProxyExclusionTap_"> + [[i18n('networkProxyAddException')]] + </paper-button> + </div> + </div> + + <paper-button id="saveManualProxy" + on-tap="onSaveProxyTap_" class="action-button" + disabled="[[!isSaveManualProxyEnabled_(networkProperties, + proxyModified_, proxy_.*)]]"> + [[i18n('save')]] + </paper-button> + </div> + + </template> + <script src="network_proxy.js"></script> +</dom-module> 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 new file mode 100644 index 00000000000..a1f2056eba8 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js @@ -0,0 +1,428 @@ +// 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. + +/** + * @fileoverview Polymer element for displaying and editing network proxy + * values. + */ +Polymer({ + is: 'network-proxy', + + behaviors: [ + CrPolicyNetworkBehavior, + I18nBehavior, + ], + + properties: { + /** + * The network properties dictionary containing the proxy properties to + * display and modify. + * @type {!CrOnc.NetworkProperties|undefined} + */ + networkProperties: { + type: Object, + observer: 'networkPropertiesChanged_', + }, + + /** Whether or not the proxy values can be edited. */ + editable: { + type: Boolean, + value: false, + }, + + /** Whether shared proxies are allowed. */ + useSharedProxies: { + type: Boolean, + value: false, + observer: 'updateProxy_', + }, + + /** + * UI visible / edited proxy configuration. + * @private {!CrOnc.ProxySettings} + */ + proxy_: { + type: Object, + value: function() { + return this.createDefaultProxySettings_(); + }, + }, + + /** + * The Web Proxy Auto Discovery URL extracted from networkProperties. + * @private + */ + WPAD_: { + type: String, + value: '', + }, + + /** + * Whether or not to use the same manual proxy for all protocols. + * @private + */ + useSameProxy_: { + type: Boolean, + value: false, + observer: 'useSameProxyChanged_', + }, + + /** + * Array of proxy configuration types. + * @private {!Array<string>} + * @const + */ + proxyTypes_: { + type: Array, + value: [ + CrOnc.ProxySettingsType.DIRECT, + CrOnc.ProxySettingsType.PAC, + CrOnc.ProxySettingsType.WPAD, + CrOnc.ProxySettingsType.MANUAL, + ], + readOnly: true + }, + + /** + * Object providing proxy type values for data binding. + * @private {!Object} + * @const + */ + ProxySettingsType_: { + type: Object, + value: { + DIRECT: CrOnc.ProxySettingsType.DIRECT, + PAC: CrOnc.ProxySettingsType.PAC, + MANUAL: CrOnc.ProxySettingsType.MANUAL, + WPAD: CrOnc.ProxySettingsType.WPAD, + }, + readOnly: true, + }, + }, + + /** + * Saved Manual properties so that switching to another type does not loose + * any set properties while the UI is open. + * @private {!CrOnc.ManualProxySettings|undefined} + */ + savedManual_: undefined, + + /** + * Saved ExcludeDomains properties so that switching to a non-Manual type does + * not loose any set exclusions while the UI is open. + * @private {!Array<string>|undefined} + */ + savedExcludeDomains_: undefined, + + /** + * Set to true while modifying proxy values so that an update does not + * override the edited values. + * @private {boolean} + */ + proxyModified_: false, + + /** @override */ + attached: function() { + this.reset(); + }, + + /** + * Called any time the page is refreshed or navigated to so that the proxy + * is updated correctly. + */ + reset: function() { + this.proxyModified_ = false; + this.proxy_ = this.createDefaultProxySettings_(); + this.updateProxy_(); + }, + + /** @private */ + networkPropertiesChanged_: function() { + if (this.proxyModified_) + return; // Ignore update. + this.updateProxy_(); + }, + + /** @private */ + updateProxy_: function() { + if (!this.networkProperties) + return; + + /** @type {!CrOnc.ProxySettings} */ + var proxy = this.createDefaultProxySettings_(); + + // For shared networks with unmanaged proxy settings, ignore any saved + // proxy settings (use the default values). + if (this.isShared_()) { + var property = this.getProxySettingsTypeProperty_(); + if (!this.isControlled(property) && !this.useSharedProxies) { + this.setProxyAsync_(proxy); + return; // Proxy settings will be ignored. + } + } + + /** @type {!chrome.networkingPrivate.ManagedProxySettings|undefined} */ + var proxySettings = this.networkProperties.ProxySettings; + if (proxySettings) { + proxy.Type = /** @type {!CrOnc.ProxySettingsType} */ ( + CrOnc.getActiveValue(proxySettings.Type)); + if (proxySettings.Manual) { + proxy.Manual.HTTPProxy = /** @type {!CrOnc.ProxyLocation|undefined} */ ( + CrOnc.getSimpleActiveProperties( + proxySettings.Manual.HTTPProxy)) || + {Host: '', Port: 80}; + proxy.Manual.SecureHTTPProxy = + /** @type {!CrOnc.ProxyLocation|undefined} */ ( + CrOnc.getSimpleActiveProperties( + proxySettings.Manual.SecureHTTPProxy)) || + {Host: '', Port: 80}; + proxy.Manual.FTPProxy = + /** @type {!CrOnc.ProxyLocation|undefined} */ ( + CrOnc.getSimpleActiveProperties( + proxySettings.Manual.FTPProxy)) || + {Host: '', Port: 80}; + proxy.Manual.SOCKS = + /** @type {!CrOnc.ProxyLocation|undefined} */ ( + CrOnc.getSimpleActiveProperties(proxySettings.Manual.SOCKS)) || + {Host: '', Port: 80}; + var jsonHttp = proxy.Manual.HTTPProxy; + this.useSameProxy_ = + (CrOnc.proxyMatches(jsonHttp, proxy.Manual.SecureHTTPProxy) && + CrOnc.proxyMatches(jsonHttp, proxy.Manual.FTPProxy) && + CrOnc.proxyMatches(jsonHttp, proxy.Manual.SOCKS)) || + (!proxy.Manual.SecureHTTPProxy.Host && + !proxy.Manual.FTPProxy.Host && !proxy.Manual.SOCKS.Host); + } + if (proxySettings.ExcludeDomains) { + proxy.ExcludeDomains = /** @type {!Array<string>|undefined} */ ( + CrOnc.getActiveValue(proxySettings.ExcludeDomains)); + } + proxy.PAC = /** @type {string|undefined} */ ( + CrOnc.getActiveValue(proxySettings.PAC)); + } + // Use saved ExcludeDomains and Manual if not defined. + proxy.ExcludeDomains = proxy.ExcludeDomains || this.savedExcludeDomains_; + proxy.Manual = proxy.Manual || this.savedManual_; + + // Set the Web Proxy Auto Discovery URL. + var ipv4 = + CrOnc.getIPConfigForType(this.networkProperties, CrOnc.IPType.IPV4); + this.WPAD_ = (ipv4 && ipv4.WebProxyAutoDiscoveryUrl) || ''; + + this.setProxyAsync_(proxy); + }, + + /** + * @param {!CrOnc.ProxySettings} proxy + * @private + */ + setProxyAsync_: function(proxy) { + // Set this.proxy_ after dom-repeat has been stamped. + this.async(() => { + this.proxy_ = proxy; + this.proxyModified_ = false; + }); + }, + + /** @private */ + useSameProxyChanged_: function() { + this.proxyModified_ = true; + }, + + /** + * @return {CrOnc.ProxySettings} An empty/default proxy settings object. + * @private + */ + createDefaultProxySettings_: function() { + return { + Type: CrOnc.ProxySettingsType.DIRECT, + ExcludeDomains: [], + Manual: { + HTTPProxy: {Host: '', Port: 80}, + SecureHTTPProxy: {Host: '', Port: 80}, + FTPProxy: {Host: '', Port: 80}, + SOCKS: {Host: '', Port: 1080} + }, + PAC: '' + }; + }, + + /** + * Called when the proxy changes in the UI. + * @private + */ + sendProxyChange_: function() { + var proxy = + /** @type {!CrOnc.ProxySettings} */ (Object.assign({}, this.proxy_)); + if (proxy.Type == CrOnc.ProxySettingsType.MANUAL) { + var manual = proxy.Manual; + var defaultProxy = manual.HTTPProxy || {Host: '', Port: 80}; + if (this.useSameProxy_) { + proxy.Manual.SecureHTTPProxy = /** @type {!CrOnc.ProxyLocation} */ ( + Object.assign({}, defaultProxy)); + proxy.Manual.FTPProxy = /** @type {!CrOnc.ProxyLocation} */ ( + Object.assign({}, defaultProxy)); + proxy.Manual.SOCKS = /** @type {!CrOnc.ProxyLocation} */ ( + Object.assign({}, defaultProxy)); + } else { + // Remove properties with empty hosts to unset them. + if (manual.HTTPProxy && !manual.HTTPProxy.Host) + delete manual.HTTPProxy; + if (manual.SecureHTTPProxy && !manual.SecureHTTPProxy.Host) + delete manual.SecureHTTPProxy; + if (manual.FTPProxy && !manual.FTPProxy.Host) + delete manual.FTPProxy; + if (manual.SOCKS && !manual.SOCKS.Host) + delete manual.SOCKS; + } + this.savedManual_ = Object.assign({}, manual); + this.savedExcludeDomains_ = proxy.ExcludeDomains; + } else if (proxy.Type == CrOnc.ProxySettingsType.PAC) { + if (!proxy.PAC) + return; + } + this.fire('proxy-change', {field: 'ProxySettings', value: proxy}); + this.proxyModified_ = false; + }, + + /** + * Event triggered when the selected proxy type changes. + * @param {!Event} event + * @private + */ + onTypeChange_: function(event) { + var target = /** @type {!HTMLSelectElement} */ (event.target); + var type = /** @type {chrome.networkingPrivate.ProxySettingsType} */ ( + target.value); + this.set('proxy_.Type', type); + if (type == CrOnc.ProxySettingsType.MANUAL) + this.proxyModified_ = true; + else + this.sendProxyChange_(); + }, + + /** @private */ + onPACChange_: function() { + this.sendProxyChange_(); + }, + + /** @private */ + onProxyInputChange_: function() { + this.proxyModified_ = true; + }, + + /** + * Event triggered when a proxy exclusion is added. + * @param {!Event} event The add proxy exclusion event. + * @private + */ + onAddProxyExclusionTap_: function(event) { + var value = this.$.proxyExclusion.value; + if (!value) + return; + this.push('proxy_.ExcludeDomains', value); + // Clear input. + this.$.proxyExclusion.value = ''; + this.proxyModified_ = true; + }, + + /** + * Event triggered when the proxy exclusion list changes. + * @param {!Event} event The remove proxy exclusions change event. + * @private + */ + onProxyExclusionsChange_: function(event) { + this.proxyModified_ = true; + }, + + /** @private */ + onSaveProxyTap_: function() { + this.sendProxyChange_(); + }, + + /** + * @param {string} proxyType The proxy type. + * @return {string} The description for |proxyType|. + * @private + */ + getProxyTypeDesc_: function(proxyType) { + if (proxyType == CrOnc.ProxySettingsType.MANUAL) + return this.i18n('networkProxyTypeManual'); + if (proxyType == CrOnc.ProxySettingsType.PAC) + return this.i18n('networkProxyTypePac'); + if (proxyType == CrOnc.ProxySettingsType.WPAD) + return this.i18n('networkProxyTypeWpad'); + return this.i18n('networkProxyTypeDirect'); + }, + + /** + * @return {!CrOnc.ManagedProperty|undefined} + * @private + */ + getProxySettingsTypeProperty_: function() { + return /** @type {!CrOnc.ManagedProperty|undefined} */ ( + this.get('ProxySettings.Type', this.networkProperties)); + }, + + /** + * @param {string} propertyName + * @return {boolean} Whether the named property setting is editable. + * @private + */ + isEditable_: function(propertyName) { + if (!this.editable || (this.isShared_() && !this.useSharedProxies)) + return false; + if (!this.networkProperties.hasOwnProperty('ProxySettings')) + return true; // No proxy settings defined, so not enforced. + var property = /** @type {!CrOnc.ManagedProperty|undefined} */ ( + this.get('ProxySettings.' + propertyName, this.networkProperties)); + if (!property) + return true; + return this.isPropertyEditable_(property); + }, + + /** + * @param {!CrOnc.ManagedProperty|undefined} property + * @return {boolean} Whether |property| is editable. + * @private + */ + isPropertyEditable_: function(property) { + return !this.isNetworkPolicyEnforced(property) && + !this.isExtensionControlled(property); + }, + + /** + * @return {boolean} + * @private + */ + isShared_: function() { + return this.networkProperties.Source == 'Device' || + this.networkProperties.Source == 'DevicePolicy'; + }, + + /** + * @return {boolean} + * @private + */ + isSaveManualProxyEnabled_: function() { + if (!this.proxyModified_) + return false; + var manual = this.proxy_.Manual; + var httpHost = this.get('HTTPProxy.Host', manual); + if (this.useSameProxy_) + return !!httpHost; + return !!httpHost || !!this.get('SecureHTTPProxy.Host', manual) || + !!this.get('FTPProxy.Host', manual) || !!this.get('SOCKS.Host', manual); + }, + + /** + * @param {string} property The property to test + * @param {string} value The value to test against + * @return {boolean} True if property == value + * @private + */ + matches_: function(property, value) { + return property == value; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html new file mode 100644 index 00000000000..f574252c285 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.html @@ -0,0 +1,37 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="network_shared_css.html"> + +<dom-module id="network-proxy-exclusions"> + <template> + <style include="network-shared cr-hidden-style iron-flex"> + iron-icon { + @apply(--cr-actionable); + margin: 5px; + } + + #container { + align-self: stretch; + border: 1px solid lightgrey; + height: 100px; + margin-top: 10px; + overflow-y: auto; + padding: 5px; + } + </style> + <div id="container"> + <template is="dom-repeat" items="[[exclusions]]"> + <div class="layout horizontal center" tabindex="0" > + <div class="flex">[[item]]</div> + <iron-icon class="favicon-image" icon="cr:clear" + on-tap="onRemoveTap_" tabindex="0" hidden="[[!editable]]"> + </iron-icon> + </div> + </template> + </div> + </template> + <script src="network_proxy_exclusions.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js new file mode 100644 index 00000000000..347b22980d3 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_exclusions.js @@ -0,0 +1,46 @@ +// 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. + +/** + * @fileoverview Polymer element for displaying a list of proxy exclusions. + * Includes UI for adding, changing, and removing entries. + */ + +(function() { + +Polymer({ + is: 'network-proxy-exclusions', + + properties: { + /** Whether or not the proxy values can be edited. */ + editable: { + type: Boolean, + value: false, + }, + + /** + * The list of exclusions. + * @type {!Array<string>} + */ + exclusions: { + type: Array, + value: function() { + return []; + }, + notify: true + } + }, + + /** + * Event triggered when an item is removed. + * @param {!{model: !{index: number}}} event + * @private + */ + onRemoveTap_: function(event) { + var index = event.model.index; + this.splice('exclusions', index, 1); + this.fire('proxy-change'); + } +}); +})(); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html new file mode 100644 index 00000000000..a1104bde59f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.html @@ -0,0 +1,50 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-input/iron-input.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input-container.html"> +<link rel="import" href="network_shared_css.html"> + +<dom-module id="network-proxy-input"> + <template> + <style include="network-shared"> + paper-input-container { + -webkit-margin-start: 10px; + margin-bottom: -12px; + margin-top: -8px; + } + + #container { + align-items: center; + display: flex; + flex: 0 1 auto; + flex-direction: row; + } + + #label { + flex: 1; + } + + #host { + width: 200px; + } + + #port { + width: 50px; + } + </style> + <div id="container"> + <div id="label">[[label]]</div> + <paper-input-container id="host" no-label-float> + <input is="iron-input" bind-value="{{value.Host}}" + disabled="[[!editable]]" on-change="onValueChange_"> + </paper-input-container> + <div>[[i18n('networkProxyPort')]]</div> + <paper-input-container id="port" no-label-float> + <input is="iron-input" bind-value="{{value.Port}}" + disabled="[[!editable]]" on-change="onValueChange_"> + </paper-input-container> + </div> + </template> + <script src="network_proxy_input.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js new file mode 100644 index 00000000000..63609b074ce --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy_input.js @@ -0,0 +1,57 @@ +// 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. + +/** + * @fileoverview Polymer element for displaying and editing a single + * network proxy value. When the URL or port changes, a 'proxy-change' event is + * fired with the combined url and port values passed as a single string, + * url:port. + */ +Polymer({ + is: 'network-proxy-input', + + behaviors: [I18nBehavior], + + properties: { + /** + * Whether or not the proxy value can be edited. + */ + editable: { + type: Boolean, + value: false, + }, + + /** + * A label for the proxy value. + */ + label: { + type: String, + value: 'Proxy', + }, + + /** + * The proxy object. + * @type {!CrOnc.ProxyLocation} + */ + value: { + type: Object, + value: function() { + return {Host: '', Port: 80}; + }, + notify: true, + }, + }, + + /** + * Event triggered when an input value changes. + * @private + */ + onValueChange_: function() { + var port = parseInt(this.value.Port, 10); + if (isNaN(port)) + port = 80; + this.value.Port = port; + this.fire('proxy-change', {value: this.value}); + } +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html new file mode 100644 index 00000000000..c235c50288d --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_shared_css.html @@ -0,0 +1,51 @@ +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> + + <!-- Common styles for network elements. --> + +<dom-module id="network-shared"> + <template> + <style include="cr-shared-style"> + .property-box { + @apply(--cr-section); + border-top: none; + min-height: var(--cr-section-two-line-min-height); + padding: 0; + } + + .property-box.indented { + -webkit-margin-start: var(--cr-section-padding); + } + + .property-box.single-column { + align-items: flex-start; + flex-direction: column; + justify-content: center; + } + + .property-box.stretch { + align-items: stretch; + } + + .property-box > .start { + align-items: center; + flex: auto; + } + + .property-box > .middle { + -webkit-padding-start: 16px; + align-items: center; + flex: auto; + } + + paper-input-container { + --paper-input-container-input: { + color: var(--paper-grey-600); + font-size: inherit; + font-weight: 400; + }; + margin-bottom: 0; + margin-top: -9px; + } + </style> + </template> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_components/compiled_resources2.gyp new file mode 100644 index 00000000000..eb688de9083 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/compiled_resources2.gyp @@ -0,0 +1,15 @@ +# 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. +{ + 'targets': [ + { + 'target_name': 'cr_components_resources', + 'type': 'none', + 'dependencies': [ + 'chromeos/compiled_resources2.gyp:*', + 'certificate_manager/compiled_resources2.gyp:*', + ], + }, + ] +} diff --git a/chromium/ui/webui/resources/cr_components_resources.grdp b/chromium/ui/webui/resources/cr_components_resources.grdp new file mode 100644 index 00000000000..164e20e627f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components_resources.grdp @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="utf-8"?> +<grit-part> + <if expr="use_nss_certs"> + <structure name="IDR_WEBUI_CA_TRUST_EDIT_DIALOG_JS" + file="cr_components/certificate_manager/ca_trust_edit_dialog.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CA_TRUST_EDIT_DIALOG_HTML" + file="cr_components/certificate_manager/ca_trust_edit_dialog.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_DELETE_CONFIRMATION_DIALOG_JS" + file="cr_components/certificate_manager/certificate_delete_confirmation_dialog.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_DELETE_CONFIRMATION_DIALOG_HTML" + file="cr_components/certificate_manager/certificate_delete_confirmation_dialog.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_HTML" + file="cr_components/certificate_manager/certificate_manager.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_JS" + file="cr_components/certificate_manager/certificate_manager.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_ENCRYPTION_DIALOG_JS" + file="cr_components/certificate_manager/certificate_password_encryption_dialog.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_ENCRYPTION_DIALOG_HTML" + file="cr_components/certificate_manager/certificate_password_encryption_dialog.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_DECRYPTION_DIALOG_JS" + file="cr_components/certificate_manager/certificate_password_decryption_dialog.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_PASSWORD_DECRYPTION_DIALOG_HTML" + file="cr_components/certificate_manager/certificate_password_decryption_dialog.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATES_ERROR_DIALOG_JS" + file="cr_components/certificate_manager/certificates_error_dialog.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATES_ERROR_DIALOG_HTML" + file="cr_components/certificate_manager/certificates_error_dialog.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_ENTRY_HTML" + file="cr_components/certificate_manager/certificate_entry.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_ENTRY_JS" + file="cr_components/certificate_manager/certificate_entry.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_LIST_HTML" + file="cr_components/certificate_manager/certificate_list.html" + preprocess="true" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_LIST_JS" + file="cr_components/certificate_manager/certificate_list.js" + preprocess="true" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_TYPES_HTML" + file="cr_components/certificate_manager/certificate_manager_types.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_MANAGER_TYPES_JS" + file="cr_components/certificate_manager/certificate_manager_types.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_SHARED_CSS_HTML" + file="cr_components/certificate_manager/certificate_shared_css.html" + type="chrome_html" + preprocess="true" /> + <structure name="IDR_WEBUI_CERTIFICATE_SUBENTRY_HTML" + file="cr_components/certificate_manager/certificate_subentry.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATE_SUBENTRY_JS" + file="cr_components/certificate_manager/certificate_subentry.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATES_BROWSER_PROXY_HTML" + file="cr_components/certificate_manager/certificates_browser_proxy.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CERTIFICATES_BROWSER_PROXY_JS" + file="cr_components/certificate_manager/certificates_browser_proxy.js" + type="chrome_html" /> + </if> + <if expr="chromeos"> + <!-- Chrome OS Custom Elements --> + <structure name="IDR_WEBUI_CHROMEOS_BLUETOOTH_DIALOG_HTML" + file="cr_components/chromeos/bluetooth_dialog.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_BLUETOOTH_DIALOG_JS" + file="cr_components/chromeos/bluetooth_dialog.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_APNLIST_HTML" + file="cr_components/chromeos/network/network_apnlist.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_APNLIST_JS" + file="cr_components/chromeos/network/network_apnlist.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_IP_CONFIG_HTML" + file="cr_components/chromeos/network/network_ip_config.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_IP_CONFIG_JS" + file="cr_components/chromeos/network/network_ip_config.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_NAMESERVERS_HTML" + file="cr_components/chromeos/network/network_nameservers.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_NAMESERVERS_JS" + file="cr_components/chromeos/network/network_nameservers.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROPERTY_LIST_HTML" + file="cr_components/chromeos/network/network_property_list.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROPERTY_LIST_JS" + file="cr_components/chromeos/network/network_property_list.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_HTML" + file="cr_components/chromeos/network/network_proxy.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_JS" + file="cr_components/chromeos/network/network_proxy.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_EXCLUSIONS_HTML" + file="cr_components/chromeos/network/network_proxy_exclusions.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_EXCLUSIONS_JS" + file="cr_components/chromeos/network/network_proxy_exclusions.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_INPUT_HTML" + file="cr_components/chromeos/network/network_proxy_input.html" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PROXY_INPUT_JS" + file="cr_components/chromeos/network/network_proxy_input.js" + type="chrome_html" /> + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_SHARED_CSS_HTML" + file="cr_components/chromeos/network/network_shared_css.html" + type="chrome_html" /> + </if> +</grit-part> diff --git a/chromium/ui/webui/resources/cr_elements/OWNERS b/chromium/ui/webui/resources/cr_elements/OWNERS index 057813143a1..5fd1662abb6 100644 --- a/chromium/ui/webui/resources/cr_elements/OWNERS +++ b/chromium/ui/webui/resources/cr_elements/OWNERS @@ -1,2 +1,3 @@ michaelpg@chromium.org +scottchen@chromium.org stevenjb@chromium.org diff --git a/chromium/ui/webui/resources/cr_elements/READE.md b/chromium/ui/webui/resources/cr_elements/READE.md new file mode 100644 index 00000000000..b8b47ef98b3 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/READE.md @@ -0,0 +1,16 @@ +This directory contains simple Polymer web components for Web UI. These +components may be shared across any WebUI and should be compatible across +all platforms (including ios). + +These web components may not contain any i18n dependencies and may not use +I18nBehavior. Instead, any text (labels, tooltips, etc) should be passed as +properties. + +These web components should avoid the use of chrome.send and should generally +avoid dependencies on extension APIs as well. + +TODO(stevenjb/dpapad): Audit elements currently using chrome.settingsPrivate +and chrome.networkingPrivate and decide whether to move these or update the +guidelines. + +For more complex components, see cr_components. diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html index d2e3639bbe8..abc2e47ac82 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html +++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.html @@ -80,7 +80,7 @@ src="[[getImgSrc_(profileImageUrl_)]]" hidden="[[!profileImageUrl_]]" srcset="[[getImgSrc2x_(profileImageUrl_)]]" - title="[[profileImageLoadingLabel]]"> + title="[[profileImageLabel]]"> <!-- Shows and selects the previously selected ('old') picture. --> <img id="oldImage" role="radio" data-type$="[[selectionTypesEnum_.OLD]]" diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js index c9cb62fb793..990aaf67fbb 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js +++ b/chromium/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_list.js @@ -28,7 +28,6 @@ Polymer({ chooseFileLabel: String, oldImageLabel: String, profileImageLabel: String, - profileImageLoadingLabel: String, takePhotoLabel: String, /** diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js index 5e3992a5705..eb4d0dff4c5 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js +++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_list_item.js @@ -108,6 +108,10 @@ Polymer({ if (!this.isStateTextVisible_()) return ''; var state = this.networkState.ConnectionState; + // For Cellular, an empty ConnectionState indicates that the device is + // still initializing. + if (!state && this.networkState.Type == CrOnc.Type.CELLULAR) + return CrOncStrings.networkListItemInitializing; if (state == CrOnc.ConnectionState.CONNECTED) return CrOncStrings.networkListItemConnected; if (state == CrOnc.ConnectionState.CONNECTING) 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 0349141de90..4b98c6fff97 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 @@ -108,24 +108,55 @@ Polymer({ }, /** - * Request the list of visible networks. May be called externally to force a + * Requests the device and network states. May be called externally to force a * refresh and list update (e.g. when the element is shown). */ refreshNetworks: function() { + chrome.networkingPrivate.getDeviceStates( + this.getDeviceStatesCallback_.bind(this)); + }, + + /** + * @param {!Array<!CrOnc.DeviceStateProperties>} deviceStates + * @private + */ + getDeviceStatesCallback_: function(deviceStates) { + var uninitializedCellular = deviceStates.find(function(device) { + return device.Type == CrOnc.Type.CELLULAR && + device.State == CrOnc.DeviceState.UNINITIALIZED; + }); + this.getNetworkStates_(uninitializedCellular); + }, + + /** + * @param {!CrOnc.DeviceStateProperties|undefined} uninitializedCellular + * A cellular device state to pass to |getNetworksCallback_| or undefined. + */ + getNetworkStates_: function(uninitializedCellular) { var filter = { networkType: chrome.networkingPrivate.NetworkType.ALL, visible: true, configured: false }; - chrome.networkingPrivate.getNetworks( - filter, this.getNetworksCallback_.bind(this)); + chrome.networkingPrivate.getNetworks(filter, function(states) { + this.getNetworksCallback_(uninitializedCellular, states); + }.bind(this)); }, /** + * @param {!CrOnc.DeviceStateProperties|undefined} uninitializedCellular + * If defined, prepends a Cellular state with no ConnectionState to + * represent an uninitialized Cellular device. * @param {!Array<!CrOnc.NetworkStateProperties>} states * @private */ - getNetworksCallback_: function(states) { + getNetworksCallback_: function(uninitializedCellular, states) { + if (uninitializedCellular) { + states.unshift({ + GUID: '', + Type: uninitializedCellular.Type, + }); + } this.networkStateList_ = states; var defaultState = (this.networkStateList_.length > 0 && this.networkStateList_[0].ConnectionState == 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 8382119b6d4..4c51796778b 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 @@ -29,6 +29,7 @@ * networkListItemConnected: string, * networkListItemConnecting: string, * networkListItemConnectingTo: string, + * networkListItemInitializing: string, * networkListItemNotConnected: string, * vpnNameTemplate: string, * }} diff --git a/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp index cd7896afb8d..89de6886a21 100644 --- a/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp +++ b/chromium/ui/webui/resources/cr_elements/compiled_resources2.gyp @@ -13,6 +13,7 @@ 'cr_dialog/compiled_resources2.gyp:*', 'cr_drawer/compiled_resources2.gyp:*', 'cr_expand_button/compiled_resources2.gyp:*', + 'cr_link_row/compiled_resources2.gyp:*', 'cr_profile_avatar_selector/compiled_resources2.gyp:*', 'policy/compiled_resources2.gyp:*', ], 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 3fb14c427f5..05ca3cc16ce 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 @@ -1,7 +1,8 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> <link rel="import" href="chrome://resources/html/util.html"> -<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-action-menu"> @@ -23,6 +24,7 @@ :host ::content .dropdown-item { background: none; border: none; + border-radius: 0; box-sizing: border-box; color: var(--paper-grey-900); font: inherit; @@ -55,8 +57,8 @@ outline: none; } </style> - <div class="item-wrapper" tabindex="-1"> - <content select=".dropdown-item,hr"></content> + <div class="item-wrapper" tabindex="-1" role="menu"> + <content select=".dropdown-item,hr" id="contentNode"></content> </div> </template> <script src="cr_action_menu.js"></script> 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 181936d7132..d524c5433cf 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 @@ -30,6 +30,9 @@ var AnchorAlignment = { AFTER_END: 2, }; +/** @const {string} */ +var DROPDOWN_ITEM_CLASS = 'dropdown-item'; + (function() { /** * Returns the point to start along the X or Y axis given a start and end @@ -118,6 +121,9 @@ Polymer({ /** @private {boolean} */ hasMousemoveListener_: false, + /** @private {?PolymerDomApi.ObserveHandle} */ + contentObserver_: null, + hostAttributes: { tabindex: 0, }, @@ -137,6 +143,10 @@ Polymer({ removeListeners_: function() { window.removeEventListener('resize', this.boundClose_); window.removeEventListener('popstate', this.boundClose_); + if (this.contentObserver_) { + Polymer.dom(this.$.contentNode).unobserveNodes(this.contentObserver_); + this.contentObserver_ = null; + } }, /** @@ -321,7 +331,7 @@ Polymer({ // Restore the scroll position. doc.scrollTop = scrollTop; doc.scrollLeft = scrollLeft; - this.addCloseListeners_(); + this.addListeners_(); }, /** @private */ @@ -369,13 +379,24 @@ Polymer({ /** * @private */ - addCloseListeners_: function() { + addListeners_: function() { this.boundClose_ = this.boundClose_ || function() { if (this.open) this.close(); }.bind(this); window.addEventListener('resize', this.boundClose_); window.addEventListener('popstate', this.boundClose_); + + this.contentObserver_ = + Polymer.dom(this.$.contentNode).observeNodes((info) => { + info.addedNodes.forEach((node) => { + if (node.classList && + node.classList.contains(DROPDOWN_ITEM_CLASS) && + !node.getAttribute('role')) { + node.setAttribute('role', 'menuitem'); + } + }); + }); }, }); })(); 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 bb556e458e8..2d5528b32dc 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 @@ -13,12 +13,13 @@ --scroll-border: 1px solid var(--paper-grey-300); border: 0; border-radius: 2px; - bottom: 0; + bottom: 50%; box-shadow: 0 0 16px rgba(0, 0, 0, 0.12), 0 16px 16px rgba(0, 0, 0, 0.24); color: inherit; + overflow-y: hidden; padding: 0; - top: 0; + top: 50%; width: 512px; } @@ -55,6 +56,7 @@ font-size: calc(15 / 13 * 100%); line-height: 1; padding: 16px 16px; + @apply(--cr-dialog-title); } :host ::slotted([slot=button-container]) { @@ -95,7 +97,7 @@ .top-container { align-items: flex-start; display: flex; - min-height: 47px; + min-height: var(--cr-dialog-top-container-min-height, 47px); } .title-container { @@ -127,6 +129,7 @@ on-keypress="onCloseKeypress_"> </button> </div> + <slot name="header"></slot> <div class="body-container"> <span id="bodyTopMarker"></span> <slot name="body"></slot> 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 20d15055f7b..ad9d19fdcf8 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 @@ -64,14 +64,14 @@ outline: none; } - :host ::content .drawer-content { + :host ::slotted(.drawer-content) { height: calc(100% - 56px); overflow: auto; } </style> <div id="container" on-tap="onContainerTap_"> <div class="drawer-header" tabindex="-1">[[heading]]</div> - <content></content> + <slot></slot> </div> </template> </dom-module> 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 6428c757957..77f613fcf69 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_icons_css.html +++ b/chromium/ui/webui/resources/cr_elements/cr_icons_css.html @@ -5,7 +5,7 @@ <template> <style> :host-context([dir=rtl]) button[is='paper-icon-button-light'] { - transform: scaleX(-1); /* Flip on the X axis (aka mirror). */ + transform: scaleX(-1); /* Invert X: flip on the Y axis (aka mirror). */ } button[is='paper-icon-button-light'] { @@ -18,6 +18,15 @@ width: var(--cr-icon-ripple-size); } + button[is='paper-icon-button-light'].no-overlap { + margin-left: 0; + margin-right: 0; + } + + button[is='paper-icon-button-light'].icon-arrow-back { + background-image: url(../images/icon_arrow_back.svg); + } + button[is='paper-icon-button-light'].icon-cancel { background-image: url(../images/icon_cancel.svg); } @@ -50,10 +59,18 @@ background-image: url(../images/open_in_new.svg); } + button[is='paper-icon-button-light'].icon-menu-white { + background-image: url(../images/icon_menu_white.svg); + } + button[is='paper-icon-button-light'].icon-more-vert { background-image: url(../images/icon_more_vert.svg); } + button[is='paper-icon-button-light'].icon-refresh { + background-image: url(../images/icon_refresh.svg); + } + button[is='paper-icon-button-light'].icon-settings { background-image: url(../images/icon_settings.svg); } diff --git a/chromium/ui/webui/resources/js/chromeos/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/cr_link_row/compiled_resources2.gyp index a363475a954..11ed485f750 100644 --- a/chromium/ui/webui/resources/js/chromeos/compiled_resources2.gyp +++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/compiled_resources2.gyp @@ -1,15 +1,13 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. +# 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. { 'targets': [ { - 'target_name': 'ui_account_tweaks', + 'target_name': 'cr_link_row', 'dependencies': [ - '../compiled_resources2.gyp:cr', - '../compiled_resources2.gyp:load_time_data', ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, - ] + ], } 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 new file mode 100644 index 00000000000..0ea10f6da08 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.html @@ -0,0 +1,74 @@ +<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/hidden_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/default-theme.html"> + +<dom-module id="cr-link-row"> + <template strip-whitespace=""> + <style include="cr-hidden-style cr-icons"> + :host { + background: none; + border: none; + color: inherit; + cursor: pointer; + font-size: 100%; /* Specifically for Mac OSX, harmless elsewhere. */ + line-height: 154%; /* 20px. */ + margin: 0; + outline: none; + padding: 0; + position: relative; + width: 100%; + @apply(--cr-section); + } + + :host(.continuation), + :host(.first) { + border-top: none; + } + + :host([disabled]) { + color: var(--paper-grey-500); + cursor: auto; + pointer-events: none; + } + + #label, + #subLabel { + display: flex; + } + + #labelWrapper { + flex: 1; + flex-basis: 0.000000001px; + } + + #outer { + align-items: center; + display: flex; + min-height: var(--cr-section-two-line-min-height); + width: 100%; + } + + #outer[noSubLabel] { + min-height: var(--cr-section-min-height); + } + + #subLabel { + /* TODO(dschuyler): replace with: @apply(--cr-secondary-text); */ + color: var(--paper-grey-600); + 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> + <button class$="[[iconClass]]" is="paper-icon-button-light"></button> + </div> + </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 new file mode 100644 index 00000000000..e700b6d9c2d --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_link_row/cr_link_row.js @@ -0,0 +1,27 @@ +// 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. + +/** + * @fileoverview + * A link row is a UI element similar to a button, though usually wider than a + * button (taking up the whole 'row'). The name link comes from the intended use + * of this element to take the user to another page in the app or to an external + * page (somewhat like an HTML link). + */ +Polymer({ + is: 'cr-link-row', + extends: 'button', + + properties: { + iconClass: String, + + label: String, + + subLabel: { + type: String, + /* Value used for noSubLabel attribute. */ + value: '', + }, + }, +}); 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 9c37afe5501..29b9923b515 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 @@ -44,7 +44,7 @@ ignore-modified-key-events="[[ignoreModifiedKeyEvents]]"> <template is="dom-repeat" items="[[avatars]]"> <paper-button class="avatar" title="[[item.label]]" - style$="background-image: [[getIconImageset_(item.url)]]" + style$="background-image: [[getIconImageSet_(item.url)]]" on-tap="onAvatarTap_"> </paper-button> </template> 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 c09ce793656..28c58de1449 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 @@ -49,10 +49,10 @@ Polymer({ /** * @param {string} iconUrl - * @return {string} A CSS imageset for multiple scale factors. + * @return {string} A CSS image-set for multiple scale factors. * @private */ - getIconImageset_: function(iconUrl) { + getIconImageSet_: function(iconUrl) { return cr.icon.getImage(iconUrl); }, diff --git a/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js index 2a497807d95..9535c1a9b40 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js +++ b/chromium/ui/webui/resources/cr_elements/cr_scrollable_behavior.js @@ -63,7 +63,7 @@ var CrScrollableBehavior = { */ updateScrollableContents: function() { if (this.intervalId_ !== null) - return; // notifyResize is arelady in progress. + return; // notifyResize is already in progress. this.requestUpdateScroll(); @@ -93,7 +93,7 @@ var CrScrollableBehavior = { }, /** - * Setup the intial scrolling related classes for each scrollable container. + * Setup the initial scrolling related classes for each scrollable container. * Called from ready() and updateScrollableContents(). May also be called * directly when the contents change (e.g. when not using iron-list). */ @@ -119,7 +119,7 @@ var CrScrollableBehavior = { this.async(function() { var scrollTop = list.savedScrollTops.shift(); // Ignore scrollTop of 0 in case it was intermittent (we do not need to - // explicity scroll to 0). + // explicitly scroll to 0). if (scrollTop != 0) list.scroll(0, scrollTop); }); @@ -136,7 +136,8 @@ var CrScrollableBehavior = { }, /** - * This gets called once intially and any time a scrollable container scrolls. + * This gets called once initially and any time a scrollable container + * scrolls. * @param {!HTMLElement} scrollable * @private */ diff --git a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js index 491bbb53d60..9646f99e0be 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js +++ b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js @@ -33,9 +33,8 @@ var CrSearchFieldBehavior = { }, /** - * @abstract * @return {!HTMLInputElement} The input field element the behavior should - * use. + * use. */ getSearchInput: function() {}, 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 bfbe0c97fc8..b53e5f65276 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 @@ -1,11 +1,13 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-media-query/iron-media-query.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <dom-module id="cr-toolbar"> <template> - <style> + <style include="cr-icons"> :host { --cr-toolbar-field-width: 580px; --cr-toolbar-height: 56px; @@ -22,9 +24,7 @@ flex: 1; font-size: 123%; font-weight: 400; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + line-height: normal; } #leftContent { @@ -130,11 +130,12 @@ <div id="leftSpacer"> <!-- Note: showing #menuPromo relies on this dom-if being [restamp]. --> <template is="dom-if" if="[[showMenu]]" restamp> - <paper-icon-button id="menuButton" icon="cr20:menu" + <button id="menuButton" is="paper-icon-button-light" + class="icon-menu-white no-overlap" on-tap="onMenuTap_" title="[[titleIfNotShowMenuPromo_(menuLabel, showMenuPromo)]]" aria-label$="[[menuLabel]]"> - </paper-icon-button> + </button> <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 5b389334c39..dd002fb9c28 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 @@ -151,7 +151,6 @@ on-blur="onInputBlur_" incremental autofocus> - </input> </div> <template is="dom-if" if="[[hasSearchText]]"> <button is="paper-icon-button-light" class="icon-cancel-toolbar" diff --git a/chromium/ui/webui/resources/cr_elements/icons.html b/chromium/ui/webui/resources/cr_elements/icons.html index 40e08ef742e..bc0eae71ea2 100644 --- a/chromium/ui/webui/resources/cr_elements/icons.html +++ b/chromium/ui/webui/resources/cr_elements/icons.html @@ -17,7 +17,6 @@ blurry at 20 px). Please use 20 px icons when available. Keep these in sorted order by id="". See also http://goo.gl/Y1OdAq --> <g id="domain"><path d="M2,3 L2,17 L11.8267655,17 L13.7904799,17 L18,17 L18,7 L12,7 L12,3 L2,3 Z M8,13 L10,13 L10,15 L8,15 L8,13 Z M4,13 L6,13 L6,15 L4,15 L4,13 Z M8,9 L10,9 L10,11 L8,11 L8,9 Z M4,9 L6,9 L6,11 L4,11 L4,9 Z M12,9 L16,9 L16,15 L12,15 L12,9 Z M12,11 L14,11 L14,13 L12,13 L12,11 Z M8,5 L10,5 L10,7 L8,7 L8,5 Z M4,5 L6,5 L6,7 L4,7 L4,5 Z"></path></g> - <g id="menu"><rect x="2" y="4" width="16" height="2"></rect><rect x="2" y="9" width="16" height="2"></rect><rect x="2" y="14" width="16" height="2"></rect></g> </svg> </iron-iconset-svg> <iron-iconset-svg name="cr" size="24"> @@ -34,6 +33,7 @@ blurry at 20 px). Please use 20 px icons when available. <g id="camera-alt"><circle cx="12" cy="12" r="3.2"></circle><path d="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"></path></g> <g id="check"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path></g> </if> + <g id="account-child-invert" viewBox="0 0 48 48"><path d="M24 4c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6 2.69-6 6-6z"></path><path fill="none" d="M0 0h48v48H0V0z"></path><circle fill="none" cx="24" cy="26" r="4"></circle><path d="M24 18c-6.16 0-13 3.12-13 7.23v11.54c0 2.32 2.19 4.33 5.2 5.63 2.32 1 5.12 1.59 7.8 1.59.66 0 1.33-.06 2-.14v-5.2c-.67.08-1.34.14-2 .14-2.63 0-5.39-.57-7.68-1.55.67-2.12 4.34-3.65 7.68-3.65.86 0 1.75.11 2.6.29 2.79.62 5.2 2.15 5.2 4.04v4.47c3.01-1.31 5.2-3.31 5.2-5.63V25.23C37 21.12 30.16 18 24 18zm0 12c-2.21 0-4-1.79-4-4s1.79-4 4-4 4 1.79 4 4-1.79 4-4 4z"></path></g> <g id="chevron-right"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></g> <g id="clear"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g> <g id="close"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path></g> @@ -55,6 +55,7 @@ blurry at 20 px). Please use 20 px icons when available. suffix prevents the naming conflict. --> <g id="settings_icon"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"></path></g> <g id="star"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"></path></g> + <g id="supervisor-account" viewBox="0 0 48 48"><path d="M0 0h48v48H0z" fill="none"></path><path d="M33 24c2.76 0 4.98-2.24 4.98-5s-2.22-5-4.98-5c-2.76 0-5 2.24-5 5s2.24 5 5 5zm-15-2c3.31 0 5.98-2.69 5.98-6s-2.67-6-5.98-6c-3.31 0-6 2.69-6 6s2.69 6 6 6zm15 6c-3.67 0-11 1.84-11 5.5V38h22v-4.5c0-3.66-7.33-5.5-11-5.5zm-15-2c-4.67 0-14 2.34-14 7v5h14v-4.5c0-1.7.67-4.67 4.74-6.94C21 26.19 19.31 26 18 26z"></path></g> <g id="warning"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path></g> </defs> </svg> diff --git a/chromium/ui/webui/resources/cr_elements/paper_checkbox_style_css.html b/chromium/ui/webui/resources/cr_elements/paper_checkbox_style_css.html new file mode 100644 index 00000000000..6aab514b762 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/paper_checkbox_style_css.html @@ -0,0 +1,19 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<!-- Common paper-checkbox styling for Material Design WebUI. --> +<dom-module id="paper-checkbox-style"> + <template> + <style> + paper-checkbox { + --paper-checkbox-checked-color: var(--google-blue-500); + --paper-checkbox-ink-size: 40px; + --paper-checkbox-label-color: inherit; + --paper-checkbox-label-spacing: 20px; + --paper-checkbox-size: 16px; + --paper-checkbox-unchecked-color: var(--paper-grey-600); + -webkit-margin-start: 2px; + } + </style> + </template> +</dom-module> + diff --git a/chromium/ui/webui/resources/cr_elements/paper_toggle_style_css.html b/chromium/ui/webui/resources/cr_elements/paper_toggle_style_css.html new file mode 100644 index 00000000000..95581fd1707 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/paper_toggle_style_css.html @@ -0,0 +1,42 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> + +<!-- Common paper-button styling for Material Design WebUI. --> +<dom-module id="paper-toggle-style"> + <template> + <style> + :root { + --cr-toggle-bar-size: { + height: 12px; + left: 4px; + width: 28px; + }; + --cr-toggle-button-size: { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4); + height: 16px; + top: -2px; + width: 16px; + }; + --cr-toggle-ink-size: { + height: 40px; + top: -12px; + left: -12px; + width: 40px; + }; + + --paper-toggle-button-checked-bar: var(--cr-toggle-bar-size); + --paper-toggle-button-checked-bar-color: var(--cr-toggle-color); + --paper-toggle-button-checked-button: { + @apply(--cr-toggle-button-size); + transform: translate(18px, 0); + }; + --paper-toggle-button-checked-button-color: var(--cr-toggle-color); + --paper-toggle-button-label-spacing: 0; + --paper-toggle-button-unchecked-bar: var(--cr-toggle-bar-size); + --paper-toggle-button-unchecked-button: var(--cr-toggle-button-size); + --paper-toggle-button-unchecked-ink: var(--cr-toggle-ink-size); + }; + </style> + </template> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp b/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp index 357d75a6a0d..8b6dede4445 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp +++ b/chromium/ui/webui/resources/cr_elements/policy/compiled_resources2.gyp @@ -51,5 +51,10 @@ ], 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], }, + { + 'target_name': 'cr_tooltip_icon', + 'dependencies': [], + 'includes': ['../../../../../third_party/closure_compiler/compile_js2.gypi'], + }, ], } diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html index 0bb096776f1..41b5170eff8 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_indicator.html @@ -1,29 +1,16 @@ -<link rel="import" href="chrome://resources/cr_elements/icons.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html"> + +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="cr_policy_indicator_behavior.html"> -<link rel="import" href="cr_policy_vars_css.html"> +<link rel="import" href="cr_tooltip_icon.html"> <dom-module id="cr-policy-indicator"> <template> - <style include="cr-hidden-style"> - :host { - @apply(--cr-policy-indicator); - } - - paper-tooltip { - --paper-tooltip: var(--cr-policy-tooltip); - } - </style> - <iron-icon id="indicator" tabindex="0" aria-label$="[[iconAriaLabel]]" - aria-describedby="tooltip" hidden$="[[!indicatorVisible]]" - icon="[[indicatorIcon]]"></iron-icon> - <paper-tooltip id="tooltip" for="indicator" position="top" - fit-to-visible-bounds> - [[indicatorTooltip]] - </paper-tooltip> + <style include="cr-hidden-style"></style> + <cr-tooltip-icon hidden$="[[!indicatorVisible]]" + tooltip-text="[[indicatorTooltip]]" icon-class="[[indicatorIcon]]" + icon-aria-label="[[iconAriaLabel]]"> + </cr-tooltip-icon> </template> <script src="cr_policy_indicator.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html index 70082b7a034..0994787a73a 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_indicator.html @@ -1,30 +1,16 @@ -<link rel="import" href="chrome://resources/cr_elements/icons.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html"> + +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="cr_policy_indicator_behavior.html"> <link rel="import" href="cr_policy_network_behavior.html"> -<link rel="import" href="cr_policy_vars_css.html"> +<link rel="import" href="cr_tooltip_icon.html"> <dom-module id="cr-policy-network-indicator"> <template> - <style include="cr-hidden-style"> - :host { - @apply(--cr-policy-indicator); - } - - paper-tooltip { - --paper-tooltip: var(--cr-policy-tooltip); - } - </style> - <iron-icon id="indicator" tabindex="0" aria-describedby="tooltip" - hidden$="[[!indicatorVisible]]" icon="[[indicatorIcon]]"> - </iron-icon> - <paper-tooltip id="tooltip" for="indicator" position="top" - fit-to-visible-bounds> - [[indicatorTooltip]] - </paper-tooltip> + <style include="cr-hidden-style"></style> + <cr-tooltip-icon hidden$="[[!indicatorVisible]]" + tooltip-text="[[indicatorTooltip]]" icon-class="[[indicatorIcon]]"> + </cr-tooltip-icon> </template> <script src="cr_policy_network_indicator.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html index dd33e679cfb..8758980f279 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.html @@ -1,29 +1,16 @@ -<link rel="import" href="chrome://resources/cr_elements/icons.html"> -<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html"> + +<link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="cr_policy_indicator_behavior.html"> -<link rel="import" href="cr_policy_vars_css.html"> +<link rel="import" href="cr_tooltip_icon.html"> <dom-module id="cr-policy-pref-indicator"> <template> - <style include="cr-hidden-style"> - :host { - @apply(--cr-policy-indicator); - } - - paper-tooltip { - --paper-tooltip: var(--cr-policy-tooltip); - } - </style> - <iron-icon id="indicator" tabindex="0" aria-label$="[[iconAriaLabel]]" - aria-describedby="tooltip" hidden$="[[!indicatorVisible]]" - icon="[[indicatorIcon]]"></iron-icon> - <paper-tooltip id="tooltip" for="indicator" position="top" - fit-to-visible-bounds> - [[indicatorTooltip]] - </paper-tooltip> + <style include="cr-hidden-style"></style> + <cr-tooltip-icon hidden$="[[!indicatorVisible]]" + tooltip-text="[[indicatorTooltip]]" icon-class="[[indicatorIcon]]" + icon-aria-label="[[iconAriaLabel]]"> + </cr-tooltip-icon> </template> <script src="cr_policy_pref_indicator.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_vars_css.html b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_vars_css.html deleted file mode 100644 index c16dd6503b8..00000000000 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_vars_css.html +++ /dev/null @@ -1,17 +0,0 @@ -<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> -<style is="custom-style"> - :root { - --cr-policy-indicator: { - @apply(--cr-icon-height-width); - display: flex; /* Position independently from the line-height. */ - }; - - --cr-policy-tooltip: { - font-size: 92.31%; /* Effectively 12px if the host default is 13px. */ - font-weight: 500; - max-width: 330px; - min-width: 200px; - padding: 10px 8px; - }; - } -</style> diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html new file mode 100644 index 00000000000..c9a1587bf87 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.html @@ -0,0 +1,34 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html"> + +<dom-module id="cr-tooltip-icon"> + <template> + <style> + :host { + @apply(--cr-icon-height-width); + display: flex; /* Position independently from the line-height. */ + } + + paper-tooltip { + --paper-tooltip: { + font-size: 92.31%; /* Effectively 12px if the host default is 13px. */ + font-weight: 500; + max-width: 330px; + min-width: 200px; + padding: 10px 8px; + }; + } + </style> + <iron-icon id="indicator" tabindex="0" aria-label$="[[iconAriaLabel]]" + aria-describedby="tooltip" icon="[[iconClass]]"></iron-icon> + <paper-tooltip id="tooltip" for="indicator" position="top" + fit-to-visible-bounds> + [[tooltipText]] + </paper-tooltip> + </template> + <script src="cr_tooltip_icon.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js new file mode 100644 index 00000000000..ba918fb0ca7 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js @@ -0,0 +1,12 @@ +// 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. + +Polymer({ + is: 'cr-tooltip-icon', + properties: { + iconAriaLabel: String, + iconClass: String, + tooltipText: String, + }, +});
\ No newline at end of file diff --git a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html index 224a94768a6..2352c441aed 100644 --- a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html +++ b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html @@ -37,12 +37,43 @@ -webkit-margin-start: var(--cr-icon-button-margin-start); } + --cr-primary-text: { + color: var(--paper-grey-900); + line-height: 154%; /* 20px. */ + } + + --cr-secondary-text: { + color: var(--paper-grey-600); + font-weight: 400; + } + + /* TODO (scottchen): re-implement with paddings instead; */ + /* These are used for row items such as radio buttons, check boxes, list + * items etc. */ + --cr-section-min-height: 48px; + --cr-section-two-line-min-height: 64px; + --cr-section-three-line-min-height: 84px; + + --cr-section-padding: 20px; + --cr-section-indent-width: 40px; + --cr-section-indent-padding: calc( + var(--cr-section-padding) + var(--cr-section-indent-width)); + + --cr-section: { + align-items: center; + border-top: var(--cr-separator-line); + display: flex; + min-height: var(--cr-section-min-height); + padding: 0 var(--cr-section-padding); + }; + + --cr-toggle-color: var(--google-blue-500); + --cr-selectable-focus: { background-color: var(--cr-focused-item-color); outline: none; } --cr-separator-height: 1px; --cr-separator-line: var(--cr-separator-height) solid rgba(0, 0, 0, 0.06); - --paper-checkbox-ink-size: 40px; } </style> diff --git a/chromium/ui/webui/resources/cr_elements_images.grdp b/chromium/ui/webui/resources/cr_elements_images.grdp index a5ec224b864..23265241ab1 100644 --- a/chromium/ui/webui/resources/cr_elements_images.grdp +++ b/chromium/ui/webui/resources/cr_elements_images.grdp @@ -9,6 +9,8 @@ file="images/arrow_right.svg" type="BINDATA" /> <include name="IDR_WEBUI_IMAGES_OPEN_IN_NEW" file="images/open_in_new.svg" type="BINDATA" /> + <include name="IDR_WEBUI_IMAGES_ICON_ARROW_BACK" + file="images/icon_arrow_back.svg" type="BINDATA" /> <include name="IDR_WEBUI_IMAGES_ICON_CANCEL" file="images/icon_cancel.svg" type="BINDATA" /> <include name="IDR_WEBUI_IMAGES_ICON_CANCEL_TOOLBAR" @@ -25,8 +27,12 @@ file="images/icon_expand_more.svg" type="BINDATA" /> <include name="IDR_WEBUI_IMAGES_ICON_EXTERNAL" file="images/open_in_new.svg" type="BINDATA" /> + <include name="IDR_WEBUI_IMAGES_ICON_MENU_WHITE" + file="images/icon_menu_white.svg" type="BINDATA" /> <include name="IDR_WEBUI_IMAGES_ICON_MORE_VERT" file="images/icon_more_vert.svg" type="BINDATA" /> + <include name="IDR_WEBUI_IMAGES_ICON_REFRESH" + file="images/icon_refresh.svg" type="BINDATA" /> <include name="IDR_WEBUI_IMAGES_ICON_SETTINGS" file="images/icon_settings.svg" type="BINDATA" /> <include name="IDR_WEBUI_IMAGES_ICON_SEARCH" diff --git a/chromium/ui/webui/resources/cr_elements_resources.grdp b/chromium/ui/webui/resources/cr_elements_resources.grdp index d9db27b42bd..d7f4a170287 100644 --- a/chromium/ui/webui/resources/cr_elements_resources.grdp +++ b/chromium/ui/webui/resources/cr_elements_resources.grdp @@ -41,6 +41,12 @@ <structure name="IDR_CR_ELEMENTS_CR_LAZY_RENDER_JS" file="../../webui/resources/cr_elements/cr_lazy_render/cr_lazy_render.js" type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_CR_LINK_ROW_HTML" + file="../../webui/resources/cr_elements/cr_link_row/cr_link_row.html" + type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_CR_LINK_ROW_JS" + file="../../webui/resources/cr_elements/cr_link_row/cr_link_row.js" + type="chrome_html" /> <if expr="chromeos"> <structure name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_CR_CAMERA_HTML" file="../../webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html" @@ -145,8 +151,11 @@ <structure name="IDR_CR_ELEMENTS_CR_POLICY_PREF_INDICATOR_HTML" file="../../webui/resources/cr_elements/policy/cr_policy_pref_indicator.html" type="chrome_html" /> - <structure name="IDR_CR_ELEMENTS_CR_POLICY_VARS_CSS_HTML" - file="../../webui/resources/cr_elements/policy/cr_policy_vars_css.html" + <structure name="IDR_CR_ELEMENTS_CR_TOOLTIP_ICON_HTML" + file="../../webui/resources/cr_elements/policy/cr_tooltip_icon.html" + type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_CR_TOOLTIP_ICON_JS" + file="../../webui/resources/cr_elements/policy/cr_tooltip_icon.js" type="chrome_html" /> <structure name="IDR_CR_ELEMENTS_CR_PROFILE_AVATAR_SELECTOR_HTML" file="../../webui/resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.html" @@ -179,6 +188,12 @@ <structure name="IDR_CR_ELEMENTS_PAPER_BUTTON_STYLE_CSS_HTML" file="../../webui/resources/cr_elements/paper_button_style_css.html" type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_PAPER_CHECKBOX_STYLE_CSS_HTML" + file="../../webui/resources/cr_elements/paper_checkbox_style_css.html" + type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_PAPER_TOGGLE_STYLE_CSS_HTML" + file="../../webui/resources/cr_elements/paper_toggle_style_css.html" + type="chrome_html" /> <structure name="IDR_CR_ELEMENTS_CR_SHARED_VARS_CSS_HTML" file="../../webui/resources/cr_elements/shared_vars_css.html" type="chrome_html" /> diff --git a/chromium/ui/webui/resources/css/chrome_shared.css b/chromium/ui/webui/resources/css/chrome_shared.css index acca2c1cd22..95ef34d5564 100644 --- a/chromium/ui/webui/resources/css/chrome_shared.css +++ b/chromium/ui/webui/resources/css/chrome_shared.css @@ -8,9 +8,6 @@ @import url(chrome://resources/css/text_defaults.css); @import url(i18n_process.css); @import url(widgets.css); -<if expr="chromeos"> -@import url(chromeos/ui_account_tweaks.css); -</if> /* Prevent CSS from overriding the hidden property. */ [hidden] { diff --git a/chromium/ui/webui/resources/css/chromeos/ui_account_tweaks.css b/chromium/ui/webui/resources/css/chromeos/ui_account_tweaks.css deleted file mode 100644 index 91ae0699729..00000000000 --- a/chromium/ui/webui/resources/css/chromeos/ui_account_tweaks.css +++ /dev/null @@ -1,14 +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. */ - -/* This file defines styles for internal page elements that have special - * look and feel based on account status (owner/non-owner/guest). */ - -.guest-disabled { - color: #999; -} - -a.guest-disabled { - opacity: 0.75; -} diff --git a/chromium/ui/webui/resources/css/widgets.css b/chromium/ui/webui/resources/css/widgets.css index 997bfc04794..419304710cf 100644 --- a/chromium/ui/webui/resources/css/widgets.css +++ b/chromium/ui/webui/resources/css/widgets.css @@ -249,7 +249,7 @@ input:disabled:-webkit-any([type='password'], * * <div class="checkbox"> * <label> - * <input type="checkbox"></input> + * <input type="checkbox"> * <span> * </label> * </div> diff --git a/chromium/ui/webui/resources/html/chromeos/ui_account_tweaks.html b/chromium/ui/webui/resources/html/chromeos/ui_account_tweaks.html deleted file mode 100644 index 10b6db6d7e4..00000000000 --- a/chromium/ui/webui/resources/html/chromeos/ui_account_tweaks.html +++ /dev/null @@ -1 +0,0 @@ -<script src="chrome://resources/js/chromeos/ui_account_tweaks.js"></script> diff --git a/chromium/ui/webui/resources/images/icon_arrow_back.svg b/chromium/ui/webui/resources/images/icon_arrow_back.svg new file mode 100644 index 00000000000..838eb43618d --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_arrow_back.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#757575" preserveAspectRatio="xMidYMid meet"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></path></svg>
\ No newline at end of file diff --git a/chromium/ui/webui/resources/images/icon_menu_white.svg b/chromium/ui/webui/resources/images/icon_menu_white.svg new file mode 100644 index 00000000000..aab80273728 --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_menu_white.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="#fff" preserveAspectRatio="xMidYMid meet"><rect x="2" y="4" width="16" height="2"></rect><rect x="2" y="9" width="16" height="2"></rect><rect x="2" y="14" width="16" height="2"></rect></svg>
\ No newline at end of file diff --git a/chromium/ui/webui/resources/images/icon_refresh.svg b/chromium/ui/webui/resources/images/icon_refresh.svg new file mode 100644 index 00000000000..d7f66ccf991 --- /dev/null +++ b/chromium/ui/webui/resources/images/icon_refresh.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#757575" preserveAspectRatio="xMidYMid meet"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"></path></svg>
\ No newline at end of file diff --git a/chromium/ui/webui/resources/images/info.svg b/chromium/ui/webui/resources/images/info.svg new file mode 100644 index 00000000000..e76e2476640 --- /dev/null +++ b/chromium/ui/webui/resources/images/info.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18px" height="18px" viewBox="0 0 48 48" fill="#757575"> + <path d="M0 0h48v48H0z" fill="none"/> + <path d="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm2 30h-4V22h4v12zm0-16h-4v-4h4v4z"/> +</svg> diff --git a/chromium/ui/webui/resources/js/chromeos/ui_account_tweaks.js b/chromium/ui/webui/resources/js/chromeos/ui_account_tweaks.js deleted file mode 100644 index 1aa912312cb..00000000000 --- a/chromium/ui/webui/resources/js/chromeos/ui_account_tweaks.js +++ /dev/null @@ -1,178 +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. - -/** - * @fileoverview This file contains methods that allow to tweak - * internal page UI based on the status of current user (owner/user/guest). - * It is assumed that required data is passed via i18n strings - * (using loadTimeData dictionary) that are filled with call to - * AddAccountUITweaksLocalizedValues in ui_account_tweaks.cc. - * It is also assumed that tweaked page has chrome://resources/css/widgets.css - * included. - */ - -cr.define('uiAccountTweaks', function() { - - ///////////////////////////////////////////////////////////////////////////// - // UIAccountTweaks class: - - // String specificators for different types of sessions. - /** @const */ var SESSION_TYPE_GUEST = 'guest'; - /** @const */ var SESSION_TYPE_PUBLIC = 'public-account'; - - /** - * Encapsulated handling of ChromeOS accounts options page. - * @constructor - */ - function UIAccountTweaks() {} - - /** - * @return {boolean} Whether the current user is owner or not. - */ - UIAccountTweaks.currentUserIsOwner = function() { - return loadTimeData.getBoolean('currentUserIsOwner'); - }; - - /** - * @return {boolean} Whether we're currently in guest session. - */ - UIAccountTweaks.loggedInAsGuest = function() { - return loadTimeData.getBoolean('loggedInAsGuest'); - }; - - /** - * @return {boolean} Whether we're currently in public session. - */ - UIAccountTweaks.loggedInAsPublicAccount = function() { - return loadTimeData.getBoolean('loggedInAsPublicAccount'); - }; - - /** - * @return {boolean} Whether we're currently in supervised user mode. - */ - UIAccountTweaks.loggedInAsSupervisedUser = function() { - return loadTimeData.getBoolean('loggedInAsSupervisedUser'); - }; - - /** - * Enables an element unless it should be disabled for the session type. - * - * @param {!Element} element Element that should be enabled. - */ - UIAccountTweaks.enableElementIfPossible = function(element) { - var sessionType; - if (UIAccountTweaks.loggedInAsGuest()) - sessionType = SESSION_TYPE_GUEST; - else if (UIAccountTweaks.loggedInAsPublicAccount()) - sessionType = SESSION_TYPE_PUBLIC; - - if (sessionType && - element.getAttribute(sessionType + '-visibility') == 'disabled') { - return; - } - - element.disabled = false; - }; - - /** - * Disables or hides some elements in specified type of session in ChromeOS. - * All elements within given document with *sessionType*-visibility - * attribute are either hidden (for *sessionType*-visibility="hidden") - * or disabled (for *sessionType*-visibility="disabled"). - * - * @param {Document} document Document that should processed. - * @param {string} sessionType name of the session type processed. - * @private - */ - UIAccountTweaks.applySessionTypeVisibility_ = function( - document, sessionType) { - var elements = - document.querySelectorAll('[' + sessionType + '-visibility]'); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - var visibility = element.getAttribute(sessionType + '-visibility'); - if (visibility == 'hidden') - element.hidden = true; - else if (visibility == 'disabled') - UIAccountTweaks.disableElementsForSessionType(element, sessionType); - } - }; - - /** - * Updates specific visibility of elements for Guest session in ChromeOS. - * Calls applySessionTypeVisibility_ method. - * - * @param {Document} document Document that should processed. - */ - UIAccountTweaks.applyGuestSessionVisibility = function(document) { - if (!UIAccountTweaks.loggedInAsGuest()) - return; - UIAccountTweaks.applySessionTypeVisibility_(document, SESSION_TYPE_GUEST); - }; - - /** - * Updates specific visibility of elements for Public account session in - * ChromeOS. Calls applySessionTypeVisibility_ method. - * - * @param {Document} document Document that should processed. - */ - UIAccountTweaks.applyPublicSessionVisibility = function(document) { - if (!UIAccountTweaks.loggedInAsPublicAccount()) - return; - UIAccountTweaks.applySessionTypeVisibility_(document, SESSION_TYPE_PUBLIC); - }; - - /** - * Disables and marks page elements for specified session type. - * Adds #-disabled css class to all elements within given subtree, - * disables interactive elements (input/select/button), and removes href - * attribute from <a> elements. - * - * @param {!Element} element Root element of DOM subtree that should be - * disabled. - * @param {string} sessionType session type specificator. - */ - UIAccountTweaks.disableElementsForSessionType = function( - element, sessionType) { - UIAccountTweaks.disableElementForSessionType_(element, sessionType); - - // Walk the tree, searching each ELEMENT node. - var walker = document.createTreeWalker( - element, NodeFilter.SHOW_ELEMENT, null, false); - - var node = walker.nextNode(); - while (node) { - UIAccountTweaks.disableElementForSessionType_( - /** @type {!Element} */ (node), sessionType); - node = walker.nextNode(); - } - }; - - /** - * Disables single element for given session type. - * Adds *sessionType*-disabled css class, adds disabled attribute for - * appropriate elements (input/select/button), and removes href attribute from - * <a> element. - * - * @private - * @param {!Element} element Element that should be disabled. - * @param {string} sessionType account session Type specificator. - */ - UIAccountTweaks.disableElementForSessionType_ = function( - element, sessionType) { - element.classList.add(sessionType + '-disabled'); - if (element.nodeName == 'INPUT' || element.nodeName == 'SELECT' || - element.nodeName == 'BUTTON') { - element.disabled = true; - } else if (element.nodeName == 'A') { - element.onclick = function() { - return false; - }; - } - }; - - // Export - return {UIAccountTweaks: UIAccountTweaks}; - -}); diff --git a/chromium/ui/webui/resources/js/cr/ui/menu.js b/chromium/ui/webui/resources/js/cr/ui/menu.js index 487ff8553c9..ed00ed84f6a 100644 --- a/chromium/ui/webui/resources/js/cr/ui/menu.js +++ b/chromium/ui/webui/resources/js/cr/ui/menu.js @@ -78,6 +78,7 @@ cr.define('cr.ui', function() { * Clears menu. */ clear: function() { + this.selectedItem = null; this.textContent = ''; }, diff --git a/chromium/ui/webui/resources/webui_resources.grd b/chromium/ui/webui/resources/webui_resources.grd index 24f7ea9c074..e8eb478d2a4 100644 --- a/chromium/ui/webui/resources/webui_resources.grd +++ b/chromium/ui/webui/resources/webui_resources.grd @@ -496,18 +496,8 @@ without changes to the corresponding grd file. --> file="js/web_ui_listener_behavior.js" type="chrome_html" /> <structure name="IDR_WEBUI_JS_WEBUI_RESOURCE_TEST" file="js/webui_resource_test.js" type="chrome_html" /> - <if expr="chromeos"> - <structure name="IDR_WEBUI_CSS_UI_ACCOUNT_TWEAKS" - file="css/chromeos/ui_account_tweaks.css" - type="chrome_html" /> - <structure name="IDR_WEBUI_HTML_UI_ACCOUNT_TWEAKS" - file="html/chromeos/ui_account_tweaks.html" - type="chrome_html" /> - <structure name="IDR_WEBUI_JS_UI_ACCOUNT_TWEAKS" - file="js/chromeos/ui_account_tweaks.js" - type="chrome_html" /> - </if> <if expr="not is_android"> + <part file="cr_components_resources.grdp" /> <part file="cr_elements_resources.grdp" /> <part file="polymer_resources.grdp" /> </if> diff --git a/chromium/ui/wm/BUILD.gn b/chromium/ui/wm/BUILD.gn index 2de02709e0b..83f1e187bfb 100644 --- a/chromium/ui/wm/BUILD.gn +++ b/chromium/ui/wm/BUILD.gn @@ -29,8 +29,6 @@ component("wm") { "core/focus_controller.cc", "core/focus_controller.h", "core/focus_rules.h", - "core/masked_window_targeter.cc", - "core/masked_window_targeter.h", "core/native_cursor_manager.h", "core/native_cursor_manager_delegate.h", "core/shadow.cc", diff --git a/chromium/ui/wm/core/compound_event_filter.cc b/chromium/ui/wm/core/compound_event_filter.cc index cd2db9a83fe..a3adb2674cf 100644 --- a/chromium/ui/wm/core/compound_event_filter.cc +++ b/chromium/ui/wm/core/compound_event_filter.cc @@ -215,8 +215,7 @@ void CompoundEventFilter::OnMouseEvent(ui::MouseEvent* event) { event->type() == ui::ET_MOUSE_PRESSED || event->type() == ui::ET_MOUSEWHEEL)) { SetMouseEventsEnableStateOnEvent(window, event, true); - SetCursorVisibilityOnEvent(window, event, - !(event->flags() & ui::EF_DIRECT_INPUT)); + SetCursorVisibilityOnEvent(window, event, true); UpdateCursor(window, event); } diff --git a/chromium/ui/wm/core/easy_resize_window_targeter.cc b/chromium/ui/wm/core/easy_resize_window_targeter.cc index f5230312c2d..d050c1b434f 100644 --- a/chromium/ui/wm/core/easy_resize_window_targeter.cc +++ b/chromium/ui/wm/core/easy_resize_window_targeter.cc @@ -28,39 +28,18 @@ EasyResizeWindowTargeter::EasyResizeWindowTargeter( EasyResizeWindowTargeter::~EasyResizeWindowTargeter() {} -void EasyResizeWindowTargeter::SetInsets(const gfx::Insets& mouse_extend, - const gfx::Insets& touch_extend) { - if (mouse_extend == mouse_extend_ && touch_extend_ == touch_extend) - return; - - mouse_extend_ = mouse_extend; - touch_extend_ = touch_extend; +void EasyResizeWindowTargeter::OnSetInsets() { if (aura::Env::GetInstance()->mode() != aura::Env::Mode::MUS) return; aura::WindowPortMus::Get(container_) - ->SetExtendedHitRegionForChildren(mouse_extend, touch_extend); + ->SetExtendedHitRegionForChildren(mouse_extend(), touch_extend()); } bool EasyResizeWindowTargeter::EventLocationInsideBounds( - aura::Window* window, + aura::Window* target, const ui::LocatedEvent& event) const { - if (ShouldUseExtendedBounds(window)) { - // Note that |event|'s location is in |window|'s parent's coordinate system, - // so convert it to |window|'s coordinate system first. - gfx::Point point = event.location(); - if (window->parent()) - aura::Window::ConvertPointToTarget(window->parent(), window, &point); - - gfx::Rect bounds(window->bounds().size()); - if (event.IsTouchEvent() || event.IsGestureEvent()) - bounds.Inset(touch_extend_); - else - bounds.Inset(mouse_extend_); - - return bounds.Contains(point); - } - return WindowTargeter::EventLocationInsideBounds(window, event); + return WindowTargeter::EventLocationInsideBounds(target, event); } bool EasyResizeWindowTargeter::ShouldUseExtendedBounds( @@ -70,9 +49,14 @@ bool EasyResizeWindowTargeter::ShouldUseExtendedBounds( if (window->parent() != container_) return false; - const bool can_resize = - window->GetProperty(aura::client::kResizeBehaviorKey) & - ui::mojom::kResizeBehaviorCanResize; + // Only resizable windows benefit from the extended hit-test region. + if ((window->GetProperty(aura::client::kResizeBehaviorKey) & + ui::mojom::kResizeBehaviorCanResize) == 0) { + return false; + } + + // For transient children use extended bounds if a transient parent or if + // transient parent's parent is a top level window in |container_|. aura::client::TransientWindowClient* transient_window_client = aura::client::GetTransientWindowClient(); const aura::Window* transient_parent = @@ -80,7 +64,7 @@ bool EasyResizeWindowTargeter::ShouldUseExtendedBounds( ? transient_window_client->GetTransientParent(window) : nullptr; return !transient_parent || transient_parent == container_ || - (can_resize && transient_parent->parent() == container_); + transient_parent->parent() == container_; } } // namespace wm diff --git a/chromium/ui/wm/core/easy_resize_window_targeter.h b/chromium/ui/wm/core/easy_resize_window_targeter.h index 9739fdd6d99..b277bd475c8 100644 --- a/chromium/ui/wm/core/easy_resize_window_targeter.h +++ b/chromium/ui/wm/core/easy_resize_window_targeter.h @@ -26,25 +26,20 @@ class WM_CORE_EXPORT EasyResizeWindowTargeter : public aura::WindowTargeter { ~EasyResizeWindowTargeter() override; protected: - // NOTE: the insets must be negative. - 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_; } - // aura::WindowTargeter: - bool EventLocationInsideBounds(aura::Window* window, - const ui::LocatedEvent& event) const override; + void OnSetInsets() override; private: - // Returns true if the hit testing (EventLocationInsideBounds()) should use - // the extended bounds. - bool ShouldUseExtendedBounds(const aura::Window* window) const; + // aura::WindowTargeter: + // Delegates to WindowTargeter's impl and prevents overriding in subclasses. + bool EventLocationInsideBounds(aura::Window* target, + const ui::LocatedEvent& event) const final; + + // Returns true if the hit testing (GetHitTestRects()) should use the + // extended bounds. + bool ShouldUseExtendedBounds(const aura::Window* window) const override; aura::Window* container_; - gfx::Insets mouse_extend_; - gfx::Insets touch_extend_; DISALLOW_COPY_AND_ASSIGN(EasyResizeWindowTargeter); }; diff --git a/chromium/ui/wm/core/focus_controller.cc b/chromium/ui/wm/core/focus_controller.cc index 695be50f980..069e9f46e2c 100644 --- a/chromium/ui/wm/core/focus_controller.cc +++ b/chromium/ui/wm/core/focus_controller.cc @@ -206,16 +206,29 @@ void FocusController::FocusAndActivateWindow( // Activation change observers may change the focused window. If this happens // we must not adjust the focus below since this will clobber that change. aura::Window* last_focused_window = focused_window_; - if (!updating_activation_) + if (!updating_activation_) { + aura::WindowTracker focusable_window_tracker; + if (focusable) { + focusable_window_tracker.Add(focusable); + focusable = nullptr; + } SetActiveWindow(reason, window, activatable); + if (!focusable_window_tracker.windows().empty()) + focusable = focusable_window_tracker.Pop(); + } // If the window's ActivationChangeObserver shifted focus to a valid window, // we don't want to focus the window we thought would be focused by default. - bool activation_changed_focus = last_focused_window != focused_window_; - if (!updating_focus_ && (!activation_changed_focus || !focused_window_)) { - if (active_window_ && focusable) - DCHECK(active_window_->Contains(focusable)); - SetFocusedWindow(focusable); + if (!updating_focus_) { + const bool activation_changed_focus = + last_focused_window != focused_window_; + if (!activation_changed_focus || !focused_window_) { + if (active_window_ && focusable) + DCHECK(active_window_->Contains(focusable)); + SetFocusedWindow(focusable); + } + if (active_window_ && focused_window_) + DCHECK(active_window_->Contains(focused_window_)); } } @@ -340,8 +353,17 @@ void FocusController::WindowLostFocusFromDispositionChange( if (!(active_window_ && active_window_->Contains(focused_window_))) SetFocusedWindow(next_activatable); } else if (window->Contains(focused_window_)) { - // Active window isn't changing, but focused window might be. - SetFocusedWindow(rules_->GetFocusableWindow(next)); + if (updating_activation_) { + // We're in the process of updating activation, most likely + // ActivationChangeObserver::OnWindowActivated() is changing something + // about the focused window (visibility perhaps). Temporarily set the + // focus to null, we'll set it to something better when activation + // completes. + SetFocusedWindow(nullptr); + } else { + // Active window isn't changing, but focused window might be. + SetFocusedWindow(rules_->GetFocusableWindow(next)); + } } } diff --git a/chromium/ui/wm/core/focus_controller_unittest.cc b/chromium/ui/wm/core/focus_controller_unittest.cc index f4678caec36..1c8630102b8 100644 --- a/chromium/ui/wm/core/focus_controller_unittest.cc +++ b/chromium/ui/wm/core/focus_controller_unittest.cc @@ -149,6 +149,39 @@ class RecordingActivationAndFocusChangeObserver DISALLOW_COPY_AND_ASSIGN(RecordingActivationAndFocusChangeObserver); }; +// Hides a window when activation changes. +class HideOnLoseActivationChangeObserver : public ActivationChangeObserver { + public: + explicit HideOnLoseActivationChangeObserver(aura::Window* window_to_hide) + : root_(window_to_hide->GetRootWindow()), + window_to_hide_(window_to_hide) { + GetActivationClient(root_)->AddObserver(this); + } + + ~HideOnLoseActivationChangeObserver() override { + GetActivationClient(root_)->RemoveObserver(this); + } + + aura::Window* window_to_hide() { return window_to_hide_; } + + private: + // Overridden from ActivationChangeObserver: + void OnWindowActivated(ActivationReason reason, + aura::Window* gained_active, + aura::Window* lost_active) override { + if (window_to_hide_) { + aura::Window* window_to_hide = window_to_hide_; + window_to_hide_ = nullptr; + window_to_hide->Hide(); + } + } + + aura::Window* root_; + aura::Window* window_to_hide_; + + DISALLOW_COPY_AND_ASSIGN(HideOnLoseActivationChangeObserver); +}; + // ActivationChangeObserver that deletes the window losing activation. class DeleteOnLoseActivationChangeObserver : public ActivationChangeObserver, public WindowDeleter { @@ -465,6 +498,7 @@ class FocusControllerTestBase : public aura::test::AuraTestBase { virtual void ChangeFocusWhenNothingFocusedAndCaptured() {} virtual void DontPassDeletedWindow() {} virtual void StackWindowAtTopOnActivation() {} + virtual void HideFocusedWindowDuringActivationLoss() {} private: std::unique_ptr<FocusController> focus_controller_; @@ -865,6 +899,22 @@ class FocusControllerDirectTestBase : public FocusControllerTestBase { EXPECT_EQ(window1.get(), GetActiveWindow()); } + // Verifies focus isn't left when during notification of an activation change + // the focused window is hidden. + void HideFocusedWindowDuringActivationLoss() override { + aura::Window* w11 = root_window()->GetChildById(11); + FocusWindow(w11); + EXPECT_EQ(11, GetFocusedWindowId()); + EXPECT_EQ(1, GetActiveWindowId()); + { + HideOnLoseActivationChangeObserver observer(w11); + ActivateWindowById(2); + EXPECT_EQ(nullptr, observer.window_to_hide()); + EXPECT_EQ(2, GetActiveWindowId()); + EXPECT_EQ(2, GetFocusedWindowId()); + } + } + private: DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase); }; @@ -1311,6 +1361,9 @@ FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDeletedWindow); FOCUS_CONTROLLER_TEST(FocusControllerApiTest, StackWindowAtTopOnActivation); +FOCUS_CONTROLLER_TEST(FocusControllerApiTest, + HideFocusedWindowDuringActivationLoss); + // See description above TransientChildWindowActivationTest() for details. FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest, TransientChildWindowActivationTest); diff --git a/chromium/ui/wm/core/masked_window_targeter.cc b/chromium/ui/wm/core/masked_window_targeter.cc deleted file mode 100644 index 74e8bc8a7c1..00000000000 --- a/chromium/ui/wm/core/masked_window_targeter.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/wm/core/masked_window_targeter.h" - -#include "ui/aura/window.h" -#include "ui/events/event.h" -#include "ui/gfx/path.h" - -namespace wm { - -MaskedWindowTargeter::MaskedWindowTargeter(aura::Window* masked_window) - : masked_window_(masked_window) { -} - -MaskedWindowTargeter::~MaskedWindowTargeter() {} - -bool MaskedWindowTargeter::EventLocationInsideBounds( - aura::Window* window, - const ui::LocatedEvent& event) const { - if (window == masked_window_) { - gfx::Path mask; - if (!GetHitTestMask(window, &mask)) - return WindowTargeter::EventLocationInsideBounds(window, event); - - gfx::Size size = window->bounds().size(); - SkRegion clip_region; - clip_region.setRect(0, 0, size.width(), size.height()); - - gfx::Point point = event.location(); - if (window->parent()) - aura::Window::ConvertPointToTarget(window->parent(), window, &point); - - SkRegion mask_region; - return mask_region.setPath(mask, clip_region) && - mask_region.contains(point.x(), point.y()); - } - - return WindowTargeter::EventLocationInsideBounds(window, event); -} - -} // namespace wm diff --git a/chromium/ui/wm/core/masked_window_targeter.h b/chromium/ui/wm/core/masked_window_targeter.h deleted file mode 100644 index 272fde909a2..00000000000 --- a/chromium/ui/wm/core/masked_window_targeter.h +++ /dev/null @@ -1,40 +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_WM_CORE_MASKED_WINDOW_TARGETER_H_ -#define UI_WM_CORE_MASKED_WINDOW_TARGETER_H_ - -#include "base/macros.h" -#include "ui/aura/window_targeter.h" -#include "ui/wm/core/wm_core_export.h" - -namespace gfx { -class Path; -} - -namespace wm { - -class WM_CORE_EXPORT MaskedWindowTargeter : public aura::WindowTargeter { - public: - explicit MaskedWindowTargeter(aura::Window* masked_window); - ~MaskedWindowTargeter() override; - - protected: - // Sets the hit-test mask for |window| in |mask| (in |window|'s local - // coordinate system). Returns whether a valid mask has been set in |mask|. - virtual bool GetHitTestMask(aura::Window* window, gfx::Path* mask) const = 0; - - // aura::WindowTargeter: - bool EventLocationInsideBounds(aura::Window* target, - const ui::LocatedEvent& event) const override; - - private: - aura::Window* masked_window_; - - DISALLOW_COPY_AND_ASSIGN(MaskedWindowTargeter); -}; - -} // namespace wm - -#endif // UI_WM_CORE_MASKED_WINDOW_TARGETER_H_ diff --git a/chromium/ui/wm/core/window_animations.cc b/chromium/ui/wm/core/window_animations.cc index cca7067e2af..346a7ce9dec 100644 --- a/chromium/ui/wm/core/window_animations.cc +++ b/chromium/ui/wm/core/window_animations.cc @@ -166,7 +166,9 @@ base::LazyInstance<HidingWindowMetricsReporter>::Leaky g_reporter_hide = DEFINE_UI_CLASS_PROPERTY_KEY(int, kWindowVisibilityAnimationTypeKey, WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT); -DEFINE_UI_CLASS_PROPERTY_KEY(int, kWindowVisibilityAnimationDurationKey, 0); +DEFINE_UI_CLASS_PROPERTY_KEY(base::TimeDelta, + kWindowVisibilityAnimationDurationKey, + base::TimeDelta()); DEFINE_UI_CLASS_PROPERTY_KEY(WindowVisibilityAnimationTransition, kWindowVisibilityAnimationTransitionKey, ANIMATE_BOTH); @@ -214,13 +216,13 @@ const int kWindowAnimation_Bounce_GrowShrinkDurationPercent = 40; base::TimeDelta GetWindowVisibilityAnimationDuration( const aura::Window& window) { - int duration = + base::TimeDelta duration = window.GetProperty(kWindowVisibilityAnimationDurationKey); - if (duration == 0 && window.type() == aura::client::WINDOW_TYPE_MENU) { + if (duration.is_zero() && window.type() == aura::client::WINDOW_TYPE_MENU) { return base::TimeDelta::FromMilliseconds( kDefaultAnimationDurationForMenuMS); } - return base::TimeDelta::FromInternalValue(duration); + return duration; } // Gets/sets the WindowVisibilityAnimationType associated with a window. @@ -301,7 +303,7 @@ void AnimateShowWindowCommon(aura::Window* window, // Property sets within this scope will be implicitly animated. ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window); - if (duration.ToInternalValue() > 0) + if (duration > base::TimeDelta()) settings.SetTransitionDuration(duration); window->layer()->SetTransform(end_transform); @@ -320,7 +322,7 @@ void AnimateHideWindowCommon(aura::Window* window, hiding_settings.layer_animation_settings()->SetAnimationMetricsReporter( g_reporter_hide.Pointer()); base::TimeDelta duration = GetWindowVisibilityAnimationDuration(*window); - if (duration.ToInternalValue() > 0) + if (duration > base::TimeDelta()) hiding_settings.layer_animation_settings()->SetTransitionDuration(duration); window->layer()->SetOpacity(kWindowAnimation_HideOpacity); @@ -632,14 +634,12 @@ bool HasWindowVisibilityAnimationTransition( void SetWindowVisibilityAnimationDuration(aura::Window* window, const base::TimeDelta& duration) { - window->SetProperty(kWindowVisibilityAnimationDurationKey, - static_cast<int>(duration.ToInternalValue())); + window->SetProperty(kWindowVisibilityAnimationDurationKey, duration); } base::TimeDelta GetWindowVisibilityAnimationDuration( const aura::Window& window) { - return base::TimeDelta::FromInternalValue( - window.GetProperty(kWindowVisibilityAnimationDurationKey)); + return window.GetProperty(kWindowVisibilityAnimationDurationKey); } void SetWindowVisibilityAnimationVerticalPosition(aura::Window* window, diff --git a/chromium/ui/wm/core/window_modality_controller.cc b/chromium/ui/wm/core/window_modality_controller.cc index 0501aaf037e..3c220ed1aa3 100644 --- a/chromium/ui/wm/core/window_modality_controller.cc +++ b/chromium/ui/wm/core/window_modality_controller.cc @@ -22,19 +22,10 @@ #include "ui/wm/core/window_util.h" namespace wm { - -// Transient child's modal parent. -extern const aura::WindowProperty<aura::Window*>* const kModalParentKey; -DEFINE_UI_CLASS_PROPERTY_KEY(aura::Window*, kModalParentKey, NULL); - namespace { bool HasAncestor(aura::Window* window, aura::Window* ancestor) { - if (!window) - return false; - if (window == ancestor) - return true; - return HasAncestor(window->parent(), ancestor); + return ancestor && ancestor->Contains(window); } bool TransientChildIsWindowModal(aura::Window* window) { @@ -50,15 +41,15 @@ bool TransientChildIsChildModal(aura::Window* window) { } aura::Window* GetModalParent(aura::Window* window) { - return window->GetProperty(kModalParentKey); + return window->GetProperty(aura::client::kChildModalParentKey); } bool IsModalTransientChild(aura::Window* transient, aura::Window* original) { return transient->IsVisible() && - (TransientChildIsWindowModal(transient) || - TransientChildIsSystemModal(transient) || - (TransientChildIsChildModal(transient) && - (HasAncestor(original, GetModalParent(transient))))); + (TransientChildIsWindowModal(transient) || + TransientChildIsSystemModal(transient) || + (TransientChildIsChildModal(transient) && + HasAncestor(original, GetModalParent(transient)))); } aura::Window* GetModalTransientChild( @@ -83,7 +74,7 @@ aura::Window* GetModalTransientChild( } // namespace void SetModalParent(aura::Window* child, aura::Window* parent) { - child->SetProperty(kModalParentKey, parent); + child->SetProperty(aura::client::kChildModalParentKey, parent); } aura::Window* GetModalTransient(aura::Window* window) { diff --git a/chromium/ui/wm/public/activation_client.cc b/chromium/ui/wm/public/activation_client.cc index 8b5b8e7518f..b653b666980 100644 --- a/chromium/ui/wm/public/activation_client.cc +++ b/chromium/ui/wm/public/activation_client.cc @@ -7,7 +7,6 @@ #include "ui/aura/window.h" #include "ui/base/class_property.h" -DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(WM_PUBLIC_EXPORT, aura::Window*) DECLARE_UI_CLASS_PROPERTY_TYPE(wm::ActivationClient*) namespace wm { diff --git a/chromium/ui/wm/public/scoped_drag_drop_disabler.cc b/chromium/ui/wm/public/scoped_drag_drop_disabler.cc index 4ca3572f752..2f30f647510 100644 --- a/chromium/ui/wm/public/scoped_drag_drop_disabler.cc +++ b/chromium/ui/wm/public/scoped_drag_drop_disabler.cc @@ -24,6 +24,9 @@ class NopDragDropClient : public aura::client::DragDropClient { bool IsDragDropInProgress() override { return false; } + void AddObserver(aura::client::DragDropClientObserver* observer) override {} + void RemoveObserver(aura::client::DragDropClientObserver* observer) override { + } }; ScopedDragDropDisabler::ScopedDragDropDisabler(aura::Window* window) |